Codebase list pseudo / debian/1.9.0+git20180920-1 pseudo_ipc.c
debian/1.9.0+git20180920-1

Tree @debian/1.9.0+git20180920-1 (Download .tar.gz)

pseudo_ipc.c @debian/1.9.0+git20180920-1raw · history · blame

/*
 * 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 __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(PDBGF_IPC | PDBGF_VERBOSE, "type: %d\n", msg->type);
	pseudo_debug(PDBGF_IPC | PDBGF_VERBOSE, "inode: %llu\n", (unsigned long long) msg->ino);
	pseudo_debug(PDBGF_IPC | PDBGF_VERBOSE, "uid: %d\n", msg->uid);
	pseudo_debug(PDBGF_IPC | PDBGF_VERBOSE, "pathlen: %d\n", (int) msg->pathlen);
	if (msg->pathlen) {
		pseudo_debug(PDBGF_IPC | PDBGF_VERBOSE, "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(PDBGF_IPC, "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(PDBGF_IPC | PDBGF_VERBOSE, "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(PDBGF_IPC, "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(PDBGF_IPC | PDBGF_VERBOSE, "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(PDBGF_IPC, "read failed: %s\n", strerror(errno));
		return 0;
	}
	if (r < (int) PSEUDO_HEADER_SIZE) {
		pseudo_debug(PDBGF_IPC, "got only %d bytes (%s)\n", r, strerror(errno));
		return 0;
	}
	pseudo_debug(PDBGF_IPC, "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(PDBGF_IPC, "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 PSEUDO_STATBUF *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(PSEUDO_STATBUF *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;
}