Skip to content

Commit

Permalink
Fix error handling as well as add new command
Browse files Browse the repository at this point in the history
for closing the communication socket. As well as get
error messages back from running instance to client process.

Signed-off-by: Werner Fink <werner@suse.de>
  • Loading branch information
bitstreamout committed Nov 25, 2020
1 parent 36c2016 commit 26d35af
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 84 deletions.
6 changes: 5 additions & 1 deletion man/haveged.8
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ Set collection buffer size to nnn KW. Default is 128KW (or 512KB).
-c cmd, --command=cmd
Switch to command mode and send a command to an already running
.B haveged
process or daemon. Currently the only knows command is
process or daemon. Currently the only known commands are
.I close
to close the current communication socket of the running
.B haveged
process as well as
.IR root = <new_root>
where
.I <new_root>
Expand Down
200 changes: 143 additions & 57 deletions src/havegecmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#ifndef NO_COMMAND_MODE
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
Expand All @@ -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>

Expand All @@ -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;
}

/**
Expand All @@ -105,15 +122,15 @@ 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;
}

ret = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, (socklen_t)sizeof(one));
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;
}

Expand All @@ -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;
Expand All @@ -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:
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand All @@ -308,39 +359,37 @@ int socket_handler( /* RETURN: closed file descriptor */
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;
len -= p;
}
while (len > 0);
out:
errno = saveerr;
return ret;
}

Expand All @@ -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;
Expand All @@ -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
Loading

0 comments on commit 26d35af

Please sign in to comment.