diff options
Diffstat (limited to 'pseudo_ipc.c')
-rw-r--r-- | pseudo_ipc.c | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/pseudo_ipc.c b/pseudo_ipc.c new file mode 100644 index 0000000..cd18140 --- /dev/null +++ b/pseudo_ipc.c @@ -0,0 +1,244 @@ +/* + * pseudo_ipc.c, IPC code for pseudo client/server + * + * Copyright (c) 2008-2010 Wind River Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the Lesser GNU General Public License version 2.1 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Lesser GNU General Public License for more details. + * + * You should have received a copy of the Lesser GNU General Public License + * version 2.1 along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> +#include <errno.h> +#include <sys/stat.h> + +#include "pseudo.h" +#include "pseudo_ipc.h" + +/* Short reads or writes can cause a sigpipe, killing the program, so we + * trap it and report that something happened. + */ +static sig_atomic_t pipe_error = 0; +static void (*old_handler)(int) = SIG_DFL; + +static void +sigpipe_trap(int unused) { + pipe_error = 1; +} + +static void +ignore_sigpipe(void) { + pipe_error = 0; + old_handler = signal(SIGPIPE, sigpipe_trap); +} + +static void +allow_sigpipe(void) { + signal(SIGPIPE, old_handler); +} + +#if 0 +/* useful only when debugging crazy stuff */ +static void +display_msg_header(pseudo_msg_t *msg) { + pseudo_debug(4, "type: %d\n", msg->type); + pseudo_debug(4, "inode: %llu\n", (unsigned long long) msg->ino); + pseudo_debug(4, "uid: %d\n", msg->uid); + pseudo_debug(4, "pathlen: %d\n", (int) msg->pathlen); + if (msg->pathlen) { + pseudo_debug(4, "path: %s\n", msg->path); + } +} +#endif + +/* + * send message on fd + * return: + * 0 on success + * >0 on error + * <0 on error suggesting other end is dead + */ +int +pseudo_msg_send(int fd, pseudo_msg_t *msg, size_t len, const char *path) { + int r; + + if (!msg) + return 1; + + if (fd < 0) + return -1; + + if (path) { + pseudo_debug(4, "msg type %d (%s), external path %s, mode 0%o\n", + msg->type, pseudo_op_name(msg->op), path, (int) msg->mode); + if (len == -1) + len = strlen(path) + 1; + msg->pathlen = len; + ignore_sigpipe(); + r = write(fd, msg, PSEUDO_HEADER_SIZE); + if (r == PSEUDO_HEADER_SIZE) { + r += write(fd, path, len); + } + allow_sigpipe(); + pseudo_debug(5, "wrote %d bytes\n", r); + if (pipe_error || (r == -1 && errno == EBADF)) + return -1; + return (r != PSEUDO_HEADER_SIZE + len); + } else { + pseudo_debug(4, "msg type %d (%s), result %d (%s), path %.*s, mode 0%o\n", + msg->type, pseudo_op_name(msg->op), + msg->result, pseudo_res_name(msg->result), + msg->pathlen, msg->path, (int) msg->mode); + // display_msg_header(msg); + ignore_sigpipe(); + r = write(fd, msg, PSEUDO_HEADER_SIZE + msg->pathlen); + allow_sigpipe(); + pseudo_debug(5, "wrote %d bytes\n", r); + if (pipe_error || (r == -1 && errno == EBADF)) + return -1; + return (r != PSEUDO_HEADER_SIZE + msg->pathlen); + } +} + +/* attempts to receive a message from fd + * return is allocated message if one is provided + */ +pseudo_msg_t * +pseudo_msg_receive(int fd) { + static pseudo_msg_t *incoming; + static size_t incoming_pathlen; + pseudo_msg_t *newmsg, header; + int r; + + if (fd < 0) + return 0; + errno = 0; + r = read(fd, &header, PSEUDO_HEADER_SIZE); + if (r == -1) { + pseudo_debug(2, "read failed: %s\n", strerror(errno)); + return 0; + } + if (r < (int) PSEUDO_HEADER_SIZE) { + pseudo_debug(2, "got only %d bytes (%s)\n", r, strerror(errno)); + return 0; + } + pseudo_debug(4, "got header, type %d, pathlen %d\n", header.type, (int) header.pathlen); + // display_msg_header(&header); + if (!incoming || header.pathlen >= incoming_pathlen) { + newmsg = pseudo_msg_new(header.pathlen + 128, 0); + if (!newmsg) { + pseudo_diag("Couldn't allocate header for path of %d bytes.\n", + (int) header.pathlen); + return 0; + } + free(incoming); + incoming = newmsg; + incoming_pathlen = header.pathlen + 128; + } + *incoming = header; + if (incoming->pathlen) { + r = read(fd, incoming->path, incoming->pathlen); + if (r < incoming->pathlen) { + pseudo_debug(2, "short read on path, expecting %d, got %d\n", + (int) incoming->pathlen, r); + return 0; + } + /* ensure null termination */ + incoming->path[r] = '\0'; + } + // display_msg_header(incoming); + return incoming; +} + +/* duplicate a message -- currently totally unused */ +pseudo_msg_t * +pseudo_msg_dup(pseudo_msg_t *old) { + pseudo_msg_t *newmsg; + if (!old) + return NULL; + newmsg = malloc(sizeof(pseudo_msg_t) + old->pathlen); + if (!newmsg) + return NULL; + memcpy(newmsg, old, sizeof(pseudo_msg_t) + old->pathlen); + return newmsg; +} + +/* allocate a message either with pathlen chars of storage or with enough + * storage for path + */ +pseudo_msg_t * +pseudo_msg_new(size_t pathlen, const char *path) { + pseudo_msg_t *newmsg; + if (pathlen) { + newmsg = malloc(sizeof(pseudo_msg_t) + pathlen); + if (newmsg) { + newmsg->pathlen = pathlen; + if (path) + memcpy(newmsg->path, path, pathlen); + newmsg->path[pathlen - 1] = '\0'; + } + return newmsg; + } else { + if (!path) { + /* no pathlen, no path == purely informational */ + newmsg = malloc(sizeof(pseudo_msg_t)); + if (newmsg) { + newmsg->pathlen = 0; + } + return newmsg; + } else { + pathlen = strlen(path) + 1; + newmsg = malloc(sizeof(pseudo_msg_t) + pathlen); + if (newmsg) { + memcpy(newmsg->path, path, pathlen); + newmsg->pathlen = pathlen; + } + return newmsg; + } + } +} + +/* The following functions populate messages from statbufs and vice versa. + * It is intentional that copying a message into a stat doesn't touch nlink; + * the nlink value was not stored in the database (it is in the message at + * all only so the server can do sanity checks). + */ + +void +pseudo_msg_stat(pseudo_msg_t *msg, const struct stat64 *buf) { + if (!msg || !buf) + return; + msg->uid = buf->st_uid; + msg->gid = buf->st_gid; + msg->dev = buf->st_dev; + msg->ino = buf->st_ino; + msg->mode = buf->st_mode; + msg->rdev = buf->st_rdev; + msg->nlink = buf->st_nlink; +} + +void +pseudo_stat_msg(struct stat64 *buf, const pseudo_msg_t *msg) { + if (!msg || !buf) + return; + buf->st_uid = msg->uid; + buf->st_gid = msg->gid; + buf->st_mode = msg->mode; + buf->st_rdev = msg->rdev; +} + |