/* * 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 #include #include #include #include #include #include #include #include #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 __attribute__((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 == (size_t) -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 ((size_t) 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 ((size_t) 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 < (int) 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; }