diff options
Diffstat (limited to 'camel/camel-lock-helper.c')
-rw-r--r-- | camel/camel-lock-helper.c | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/camel/camel-lock-helper.c b/camel/camel-lock-helper.c new file mode 100644 index 0000000000..06463f2cf4 --- /dev/null +++ b/camel/camel-lock-helper.c @@ -0,0 +1,389 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2001 Ximian Inc. + * + * Authors: Michael Zucchi <notzed@ximian.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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 + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +/* lock helper process */ + +#include <stdio.h> +#include <stdlib.h> + +#include <errno.h> + +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <signal.h> + +#include <unistd.h> +#include <fcntl.h> +#include <utime.h> + +#include <time.h> + +#define SETEUID_SAVES (1) + +/* we try and include as little as possible */ + +#include "camel-lock-helper.h" +#include "camel-lock.h" + +#define d(x) + +/* keeps track of open locks */ +struct _lock_info { + struct _lock_info *next; + uid_t uid; + int id; + int depth; + time_t stamp; /* when last updated */ + char path[1]; +}; + +static int lock_id = 0; +static struct _lock_info *lock_info_list; +static uid_t lock_root_uid = -1; +static uid_t lock_real_uid = -1; + +/* utility functions */ + +static int read_n(int fd, void *buffer, int inlen) +{ + char *p = buffer; + int len, left = inlen; + + do { + len = read(fd, p, left); + if (len == -1) { + if (errno != EINTR) + return -1; + } else { + left -= len; + p += len; + } + } while (left > 0 && len != 0); + + return inlen - left; +} + +static int write_n(int fd, void *buffer, int inlen) +{ + char *p = buffer; + int len, left = inlen; + + do { + len = write(fd, p, left); + if (len == -1) { + if (errno != EINTR) + return -1; + } else { + left -= len; + p += len; + } + } while (left > 0); + + return inlen; +} + +void +camel_exception_setv (CamelException *ex, ExceptionId id, const char *format, ...) +{ + ; +} + +void +camel_exception_clear (CamelException *exception) +{ + ; +} + +char *gettext (const char *msgid); + +char * +gettext (const char *msgid) +{ + return NULL; +} + +static int lock_path(const char *path, guint32 *lockid) +{ + struct _lock_info *info = NULL; + int res = CAMEL_LOCK_HELPER_STATUS_OK; + struct stat st; + + d(fprintf(stderr, "locking path '%s' id = %d\n", path, lock_id)); + + /* check to see if we have it locked already, make the lock 'recursive' */ + /* we could also error i suppose, but why bother */ + info = lock_info_list; + while (info) { + if (!strcmp(info->path, path)) { + info->depth++; + return CAMEL_LOCK_HELPER_STATUS_OK; + } + info = info->next; + } + + /* check we are allowed to lock it, we must own it, be able to write to it, and it has to exist */ + if (stat(path, &st) == -1 + || st.st_uid != getuid() + || !S_ISREG(st.st_mode) + || (st.st_mode & 0400) == 0) { + return CAMEL_LOCK_HELPER_STATUS_INVALID; + } + + info = malloc(sizeof(*info) + strlen(path)); + if (info == NULL) { + res = CAMEL_LOCK_HELPER_STATUS_NOMEM; + goto fail; + } + + /* we try the real uid first, and if that fails, try the 'root id' */ + if (camel_lock_dot(path, NULL) == -1) { +#ifdef SETEUID_SAVES + if (lock_real_uid != lock_root_uid) { + if (seteuid(lock_root_uid) != -1) { + if (camel_lock_dot(path, NULL) == -1) { + seteuid(lock_real_uid); + res = CAMEL_LOCK_HELPER_STATUS_SYSTEM; + goto fail; + } + seteuid(lock_real_uid); + } else { + res = CAMEL_LOCK_HELPER_STATUS_SYSTEM; + goto fail; + } + } else { + res = CAMEL_LOCK_HELPER_STATUS_SYSTEM; + goto fail; + } +#else + res = CAMEL_LOCK_HELPER_STATUS_SYSTEM; + goto fail; +#endif + } else { + info->uid = lock_real_uid; + } + + strcpy(info->path, path); + info->id = lock_id; + info->depth = 1; + info->next = lock_info_list; + info->stamp = time(0); + lock_info_list = info; + + if (lockid) + *lockid = lock_id; + + lock_id++; + + d(fprintf(stderr, "lock ok\n")); + + return res; +fail: + d(fprintf(stderr, "lock failed\n")); + + if (info) + free(info); + + return res; +} + +static int unlock_id(guint32 lockid) +{ + struct _lock_info *info, *p; + + d(fprintf(stderr, "unlocking id '%d'\n", lockid)); + + p = (struct _lock_info *)&lock_info_list; + info = p->next; + while (info) { + if (info->id == lockid) { + d(fprintf(stderr, "found id %d path '%s'\n", lockid, info->path)); + info->depth--; + if (info->depth <= 0) { +#ifdef SETEUID_SAVES + if (info->uid != lock_real_uid) { + seteuid(lock_root_uid); + camel_unlock_dot(info->path); + seteuid(lock_real_uid); + } +#else + camel_unlock_dot(info->path); +#endif + p->next = info->next; + free(info); + } + + return CAMEL_LOCK_HELPER_STATUS_OK; + } + p = info; + info = info->next; + } + + d(fprintf(stderr, "unknown id asked to be unlocked %d\n", lockid)); + return CAMEL_LOCK_HELPER_STATUS_PROTOCOL; +} + +static void lock_touch(const char *path) +{ + char *name; + + /* we could also check that we haven't had our lock stolen from us here */ + + name = alloca(strlen(path) + 10); + sprintf(name, "%s.lock", path); + + d(fprintf(stderr, "Updating lock %s\n", name)); + utime(name, NULL); +} + +static void setup_process(void) +{ + struct sigaction sa; + sigset_t sigset; + + /* ignore sigint/sigio */ + sa.sa_handler = SIG_IGN; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + + sigemptyset(&sigset); + sigaddset(&sigset, SIGIO); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); + + sigaction (SIGIO, &sa, NULL); + sigaction (SIGINT, &sa, NULL); + + /* FIXME: add more sanity checks/setup here */ + +#ifdef SETEUID_SAVES + /* here we change to the real user id, this is probably not particularly + portable so may need configure checks */ + lock_real_uid = getuid(); + lock_root_uid = geteuid(); + if (lock_real_uid != lock_root_uid) + seteuid(lock_real_uid); +#endif +} + +int main(int argc, char **argv) +{ + struct _CamelLockHelperMsg msg; + int len; + int res; + char *path; + fd_set rset; + struct timeval tv; + struct _lock_info *info; + + setup_process(); + + do { + /* do a poll/etc, so we can refresh the .locks as required ... */ + FD_ZERO(&rset); + FD_SET(STDIN_FILENO, &rset); + + /* check the minimum timeout we need to refresh the next oldest lock */ + if (lock_info_list) { + time_t now = time(0); + time_t left; + time_t delay = CAMEL_DOT_LOCK_REFRESH; + + info = lock_info_list; + while (info) { + left = CAMEL_DOT_LOCK_REFRESH - (now - info->stamp); + left = MAX(left, 0); + delay = MIN(left, delay); + info = info->next; + } + + tv.tv_sec = delay; + tv.tv_usec = 0; + } + + d(fprintf(stderr, "lock helper waiting for input\n")); + if (select(STDIN_FILENO+1, &rset, NULL, NULL, lock_info_list?&tv:NULL) == -1) { + if (errno == EINTR) + break; + + continue; + } + + /* did we get a timeout? scan for any locks that need updating */ + if (!FD_ISSET(STDIN_FILENO, &rset)) { + time_t now = time(0); + time_t left; + + d(fprintf(stderr, "Got a timeout, checking locks\n")); + + info = lock_info_list; + while (info) { + left = (now - info->stamp); + if (left >= CAMEL_DOT_LOCK_REFRESH) { + lock_touch(info->path); + info->stamp = now; + } + info = info->next; + } + + continue; + } + + + len = read_n(STDIN_FILENO, &msg, sizeof(msg)); + if (len == 0) + break; + + res = CAMEL_LOCK_HELPER_STATUS_PROTOCOL; + if (len == sizeof(msg) && msg.magic == CAMEL_LOCK_HELPER_MAGIC) { + switch(msg.id) { + case CAMEL_LOCK_HELPER_LOCK: + res = CAMEL_LOCK_HELPER_STATUS_NOMEM; + path = malloc(msg.data+1); + if (path != NULL) { + res = CAMEL_LOCK_HELPER_STATUS_PROTOCOL; + len = read_n(STDIN_FILENO, path, msg.data); + if (len == msg.data) { + path[len] = 0; + res = lock_path(path, &msg.data); + } + free(path); + } + break; + case CAMEL_LOCK_HELPER_UNLOCK: + res = unlock_id(msg.data); + break; + } + } + d(fprintf(stderr, "returning result %d\n", res)); + msg.id = res; + msg.magic = CAMEL_LOCK_HELPER_RETURN_MAGIC; + write_n(STDOUT_FILENO, &msg, sizeof(msg)); + } while (1); + + d(fprintf(stderr, "parent exited, clsoing down remaining id's\n")); + while (lock_info_list) + unlock_id(lock_info_list->id); + + return 0; +} |