summaryrefslogtreecommitdiffstats
path: root/src/havegecmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/havegecmd.c')
-rw-r--r--src/havegecmd.c200
1 files changed, 143 insertions, 57 deletions
diff --git a/src/havegecmd.c b/src/havegecmd.c
index 18ff322..0a324ed 100644
--- a/src/havegecmd.c
+++ b/src/havegecmd.c
@@ -26,6 +26,7 @@
#ifndef NO_COMMAND_MODE
#include <errno.h>
+#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
@@ -35,6 +36,7 @@
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/stat.h>
#include <sys/un.h>
#include <unistd.h>
@@ -49,45 +51,60 @@ struct ucred
#include "havegecmd.h"
+int first_byte;
int socket_fd;
+static char errmsg[1024];
-static void new_root( /* RETURN: nothing */
+static int new_root( /* RETURN: status */
const char *root, /* IN: path of the new root file system */
const volatile char *path, /* IN: path of the haveged executable */
char *const argv[], /* IN: arguments for the haveged process */
struct pparams *params) /* IN: input params */
{
int ret;
+ struct stat st;
+ const char *realtive = (const char*)&path[0];
- fprintf(stderr, "%s: restart in new root: %s\n", params->daemon, root);
ret = chdir(root);
if (ret < 0) {
- if (errno != ENOENT)
- error_exit("can't change to working directory : %s", root);
- else
- fprintf(stderr, "%s: can't change to working directory : %s\n", params->daemon, root);
+ snprintf(&errmsg[0], sizeof(errmsg)-1,
+ "can't change to working directory %s: %s\n",
+ root, strerror(errno));
+ goto err;
+ }
+ if (path[0] == '/')
+ realtive++;
+ ret = fstatat(AT_FDCWD, realtive, &st, 0);
+ if (ret < 0) {
+ snprintf(&errmsg[0], sizeof(errmsg)-1,
+ "can't restart %s: %s\n",
+ path, strerror(errno));
+ goto err;
}
ret = chroot(".");
if (ret < 0) {
- if (errno != ENOENT)
- error_exit("can't change root directory");
- else
- fprintf(stderr, "%s: can't change root directory\n", params->daemon);
+ snprintf(&errmsg[0], sizeof(errmsg)-1,
+ "can't change root directory %s\n",
+ strerror(errno));
+ goto err;
}
ret = chdir("/");
if (ret < 0) {
- if (errno != ENOENT)
- error_exit("can't change to working directory /");
- else
- fprintf(stderr, "%s: can't change to working directory /\n", params->daemon);
+ snprintf(&errmsg[0], sizeof(errmsg)-1,
+ "can't change to working directory / : %s\n",
+ strerror(errno));
+ goto err;
}
ret = execv((const char *)path, argv);
if (ret < 0) {
- if (errno != ENOENT)
- error_exit("can't restart %s", path);
- else
- fprintf(stderr, "%s: can't restart %s\n", params->daemon, path);
- }
+ snprintf(&errmsg[0], sizeof(errmsg)-1,
+ "can't restart %s: %s\n",
+ path, strerror(errno));
+ }
+err:
+ if (ret < 0)
+ print_msg("%s", errmsg);
+ return ret;
}
/**
@@ -105,7 +122,7 @@ int cmd_listen( /* RETURN: UNIX socket file descriptor */
fd = socket(PF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (fd < 0) {
- fprintf(stderr, "%s: can not open UNIX socket\n", params->daemon);
+ print_msg("%s: can not open UNIX socket\n", params->daemon);
goto err;
}
@@ -113,7 +130,7 @@ int cmd_listen( /* RETURN: UNIX socket file descriptor */
if (ret < 0) {
close(fd);
fd = -1;
- fprintf(stderr, "%s: can not set option for UNIX socket\n", params->daemon);
+ print_msg("%s: can not set option for UNIX socket\n", params->daemon);
goto err;
}
@@ -122,7 +139,7 @@ int cmd_listen( /* RETURN: UNIX socket file descriptor */
close(fd);
fd = -1;
if (errno != EADDRINUSE)
- fprintf(stderr, "%s: can not bind a name to UNIX socket\n", params->daemon);
+ print_msg("%s: can not bind a name to UNIX socket\n", params->daemon);
else
fd = -2;
goto err;
@@ -132,7 +149,7 @@ int cmd_listen( /* RETURN: UNIX socket file descriptor */
if (ret < 0) {
close(fd);
fd = -1;
- fprintf(stderr, "%s: can not listen on UNIX socket\n", params->daemon);
+ print_msg("%s: can not listen on UNIX socket\n", params->daemon);
goto err;
}
err:
@@ -154,13 +171,13 @@ int cmd_connect( /* RETURN: UNIX socket file descriptor */
fd = socket(PF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (fd < 0) {
- fprintf(stderr, "%s: can not open UNIX socket\n", params->daemon);
+ print_msg("%s: can not open UNIX socket\n", params->daemon);
goto err;
}
ret = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, (socklen_t)sizeof(one));
if (ret < 0) {
- fprintf(stderr, "%s: can not set option for UNIX socket\n", params->daemon);
+ print_msg("%s: can not set option for UNIX socket\n", params->daemon);
close(fd);
fd = -1;
goto err;
@@ -169,7 +186,7 @@ int cmd_connect( /* RETURN: UNIX socket file descriptor */
ret = connect(fd, (struct sockaddr *)&su, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(su.sun_path+1));
if (ret < 0) {
if (errno != ECONNREFUSED)
- fprintf(stderr, "%s: can not connect on UNIX socket\n", params->daemon);
+ print_msg("%s: can not connect on UNIX socket\n", params->daemon);
close(fd);
fd = -1;
goto err;
@@ -191,6 +208,7 @@ int getcmd( /* RETURN: success or error */
const char* opt;
} cmds[] = {
{ "root=", MAGIC_CHROOT, 1, NULL }, /* New root */
+ { "close", MAGIC_CLOSE, 0, NULL }, /* Close socket */
{0}
}, *cmd = cmds;
int ret = -1;
@@ -235,39 +253,52 @@ int socket_handler( /* RETURN: closed file descriptor */
int ret = -1, len;
if (fd < 0) {
- fprintf(stderr, "%s: no connection jet\n", params->daemon);
+ print_msg("%s: no connection jet\n", params->daemon);
}
ptr = &magic[0];
len = sizeof(magic);
ret = safein(fd, ptr, len);
+ if (ret < 0) {
+ print_msg("%s: can not read from UNIX socket\n", params->daemon);
+ goto out;
+ }
- if (magic[1] == '\002') { /* read argument provided */
- unsigned char alen;
+ if (magic[1] == '\002') { /* ASCII start of text: read argument provided */
+ uint32_t alen;
- ret = safein(fd, &alen, sizeof(unsigned char));
+ ret = receive_uinteger(fd, &alen);
+ if (ret < 0) {
+ print_msg("%s: can not read from UNIX socket\n", params->daemon);
+ goto out;
+ }
optarg = calloc(alen, sizeof(char));
- if (!optarg)
- error_exit("can not allocate memory for message from UNIX socket");
-
+ if (!optarg) {
+ print_msg("can not allocate memory for message from UNIX socket");
+ goto out;
+ }
ptr = (unsigned char*)optarg;
- len = alen;
- ret = safein(fd, ptr, len);
+
+ ret = safein(fd, ptr, alen);
+ if (ret < 0) {
+ print_msg("%s: can not read from UNIX socket\n", params->daemon);
+ goto out;
+ }
}
clen = sizeof(struct ucred);
ret = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &clen);
if (ret < 0) {
- fprintf(stderr, "%s: can not get credentials from UNIX socket part1\n", params->daemon);
+ print_msg("%s: can not get credentials from UNIX socket part1\n", params->daemon);
goto out;
}
if (clen != sizeof(struct ucred)) {
- fprintf(stderr, "%s: can not get credentials from UNIX socket part2\n", params->daemon);
+ print_msg("%s: can not get credentials from UNIX socket part2\n", params->daemon);
goto out;
}
if (cred.uid != 0) {
- enqry = "\x15";
+ enqry = ASCII_NAK;
ptr = (unsigned char *)enqry;
len = (int)strlen(enqry)+1;
@@ -276,19 +307,39 @@ int socket_handler( /* RETURN: closed file descriptor */
switch (magic[0]) {
case MAGIC_CHROOT:
- enqry = "\x6";
+ enqry = ASCII_ACK;
+
+ ret = new_root(optarg, path, argv, params);
+ if (ret < 0) {
+ uint32_t size = strlen(errmsg);
+ safeout(fd, ASCII_STX, strlen(ASCII_STX));
+ send_uinteger(fd, size);
+ safeout(fd, errmsg, size+1);
+ break;
+ }
+
+ ptr = (unsigned char *)enqry;
+ len = (int)strlen(enqry);
+ safeout(fd, ptr, len);
+
+ break;
+ case MAGIC_CLOSE:
+ enqry = ASCII_ACK;
+
+ close(socket_fd);
+ socket_fd = -1;
ptr = (unsigned char *)enqry;
- len = (int)strlen(enqry)+1;
+ len = (int)strlen(enqry);
safeout(fd, ptr, len);
+ argv[0][0] = first_byte;
- new_root(optarg, path, argv, params);
break;
default:
- enqry = "\x15";
+ enqry = ASCII_NAK;
ptr = (unsigned char *)enqry;
- len = (int)strlen(enqry)+1;
+ len = (int)strlen(enqry);
safeout(fd, ptr, len);
break;
}
@@ -308,31 +359,30 @@ out:
ssize_t safein( /* RETURN: read bytes */
int fd, /* IN: file descriptor */
void *ptr, /* OUT: pointer to buffer */
- size_t sz) /* IN: size of buffer */
+ size_t len) /* IN: size of buffer */
{
- int saveerr = errno, t;
+ int saveerr = errno, avail;
ssize_t ret = 0;
- size_t len;
- if (sz > SSIZE_MAX)
- sz = SSIZE_MAX;
+ if (len > SSIZE_MAX)
+ len = SSIZE_MAX;
- t = 0;
- if ((ioctl(fd, FIONREAD, &t) < 0) || (t <= 0))
+ ret = ioctl(fd, FIONREAD, &avail);
+ if (ret < 0 || avail <=0)
goto out;
- len = (size_t)t;
- if (len > sz)
- len = sz;
+ if (len > avail)
+ len = avail;
do {
- ssize_t p = recv(fd, ptr, len, MSG_DONTWAIT);
+ errno = saveerr;
+ ssize_t p = recv(fd, ptr, len, 0 /* MSG_DONTWAIT */);
if (p < 0) {
if (errno == EINTR)
continue;
if (errno == EAGAIN || errno == EWOULDBLOCK)
break;
- error_exit("Unable to read from socket: %d", socket_fd);
+ print_msg("Unable to read from socket %d: %s", socket_fd, strerror(errno));
}
ptr = (char *) ptr + p;
ret += p;
@@ -340,7 +390,6 @@ ssize_t safein( /* RETURN: read bytes */
}
while (len > 0);
out:
- errno = saveerr;
return ret;
}
@@ -361,7 +410,7 @@ void safeout( /* RETURN: nothing */
continue;
if (errno == EPIPE || errno == EAGAIN || errno == EWOULDBLOCK)
break;
- error_exit("Unable to write to socket: %d", fd);
+ print_msg("Unable to write to socket %d: %s", fd, strerror(errno));
}
ptr = (char *) ptr + p;
len -= p;
@@ -371,4 +420,41 @@ void safeout( /* RETURN: nothing */
errno = saveerr;
}
+/**
+ * Send outgoing unsigned integer to socket
+ */
+void send_uinteger( /* RETURN: nothing */
+ int fd, /* IN: file descriptor */
+ uint32_t value) /* IN: 32 bit unsigned integer */
+{
+ uint8_t buffer[4];
+
+ buffer[0] = (uint8_t)((value >> 24) & 0xFF);
+ buffer[1] = (uint8_t)((value >> 16) & 0xFF);
+ buffer[2] = (uint8_t)((value >> 8) & 0xFF);
+ buffer[3] = (uint8_t)((value >> 0) & 0xFF);
+
+ safeout(fd, buffer, 4 * sizeof(uint8_t));
+}
+
+/**
+ * Receive incomming unsigned integer from socket
+ */
+int receive_uinteger( /* RETURN: status */
+ int fd, /* IN: file descriptor */
+ uint32_t *value) /* OUT: 32 bit unsigned integer */
+{
+ uint8_t buffer[4];
+
+ if (safein(fd, buffer, 4 * sizeof(uint8_t)) < 0)
+ return -1;
+
+ *value = (((uint32_t)buffer[0]) << 24) |
+ (((uint32_t)buffer[1]) << 16) |
+ (((uint32_t)buffer[2]) << 8) |
+ (((uint32_t)buffer[3]) << 0);
+
+ return 0;
+}
+
#endif