1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * Copyright (C) 2001 Ximian Inc.
5 * Authors: Michael Zucchi <notzed@ximian.com>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
22 /* lock helper process */
35 #include <sys/types.h>
37 #define SETEUID_SAVES (1)
39 /* we try and include as little as possible */
41 #include "camel-lock-helper.h"
42 #include "camel-lock.h"
46 /* keeps track of open locks */
48 struct _lock_info *next;
52 time_t stamp; /* when last updated */
56 static int lock_id = 0;
57 static struct _lock_info *lock_info_list;
58 static uid_t lock_root_uid = -1;
59 static uid_t lock_real_uid = -1;
61 /* utility functions */
63 static int read_n(int fd, void *buffer, int inlen)
66 int len, left = inlen;
69 len = read(fd, p, left);
77 } while (left > 0 && len != 0);
82 static int write_n(int fd, void *buffer, int inlen)
85 int len, left = inlen;
88 len = write(fd, p, left);
102 camel_exception_setv (CamelException *ex, ExceptionId id, const char *format, ...)
108 camel_exception_clear (CamelException *exception)
113 char *gettext (const char *msgid);
116 gettext (const char *msgid)
121 static int lock_path(const char *path, guint32 *lockid)
123 struct _lock_info *info = NULL;
124 int res = CAMEL_LOCK_HELPER_STATUS_OK;
127 d(fprintf(stderr, "locking path '%s' id = %d\n", path, lock_id));
129 /* check to see if we have it locked already, make the lock 'recursive' */
130 /* we could also error i suppose, but why bother */
131 info = lock_info_list;
133 if (!strcmp(info->path, path)) {
135 return CAMEL_LOCK_HELPER_STATUS_OK;
140 /* check we are allowed to lock it, we must own it, be able to write to it, and it has to exist */
141 if (stat(path, &st) == -1
142 || st.st_uid != getuid()
143 || !S_ISREG(st.st_mode)
144 || (st.st_mode & 0400) == 0) {
145 return CAMEL_LOCK_HELPER_STATUS_INVALID;
148 info = malloc(sizeof(*info) + strlen(path));
150 res = CAMEL_LOCK_HELPER_STATUS_NOMEM;
154 /* we try the real uid first, and if that fails, try the 'root id' */
155 if (camel_lock_dot(path, NULL) == -1) {
157 if (lock_real_uid != lock_root_uid) {
158 if (seteuid(lock_root_uid) != -1) {
159 if (camel_lock_dot(path, NULL) == -1) {
160 seteuid(lock_real_uid);
161 res = CAMEL_LOCK_HELPER_STATUS_SYSTEM;
164 seteuid(lock_real_uid);
166 res = CAMEL_LOCK_HELPER_STATUS_SYSTEM;
170 res = CAMEL_LOCK_HELPER_STATUS_SYSTEM;
174 res = CAMEL_LOCK_HELPER_STATUS_SYSTEM;
178 info->uid = lock_real_uid;
181 strcpy(info->path, path);
184 info->next = lock_info_list;
185 info->stamp = time (NULL);
186 lock_info_list = info;
193 d(fprintf(stderr, "lock ok\n"));
197 d(fprintf(stderr, "lock failed\n"));
205 static int unlock_id(guint32 lockid)
207 struct _lock_info *info, *p;
209 d(fprintf(stderr, "unlocking id '%d'\n", lockid));
211 p = (struct _lock_info *)&lock_info_list;
214 if (info->id == lockid) {
215 d(fprintf(stderr, "found id %d path '%s'\n", lockid, info->path));
217 if (info->depth <= 0) {
219 if (info->uid != lock_real_uid) {
220 seteuid(lock_root_uid);
221 camel_unlock_dot(info->path);
222 seteuid(lock_real_uid);
225 camel_unlock_dot(info->path);
227 p->next = info->next;
231 return CAMEL_LOCK_HELPER_STATUS_OK;
237 d(fprintf(stderr, "unknown id asked to be unlocked %d\n", lockid));
238 return CAMEL_LOCK_HELPER_STATUS_PROTOCOL;
241 static void lock_touch(const char *path)
245 /* we could also check that we haven't had our lock stolen from us here */
247 name = alloca(strlen(path) + 10);
248 sprintf(name, "%s.lock", path);
250 d(fprintf(stderr, "Updating lock %s\n", name));
254 static void setup_process(void)
259 /* ignore sigint/sigio */
260 sa.sa_handler = SIG_IGN;
261 sigemptyset (&sa.sa_mask);
264 sigemptyset(&sigset);
265 sigaddset(&sigset, SIGIO);
266 sigaddset(&sigset, SIGINT);
267 sigprocmask(SIG_UNBLOCK, &sigset, NULL);
269 sigaction (SIGIO, &sa, NULL);
270 sigaction (SIGINT, &sa, NULL);
272 /* FIXME: add more sanity checks/setup here */
275 /* here we change to the real user id, this is probably not particularly
276 portable so may need configure checks */
277 lock_real_uid = getuid();
278 lock_root_uid = geteuid();
279 if (lock_real_uid != lock_root_uid)
280 seteuid(lock_real_uid);
284 int main(int argc, char **argv)
286 struct _CamelLockHelperMsg msg;
292 struct _lock_info *info;
297 /* do a poll/etc, so we can refresh the .locks as required ... */
299 FD_SET(STDIN_FILENO, &rset);
301 /* check the minimum timeout we need to refresh the next oldest lock */
302 if (lock_info_list) {
303 time_t now = time (NULL);
305 time_t delay = CAMEL_DOT_LOCK_REFRESH;
307 info = lock_info_list;
309 left = CAMEL_DOT_LOCK_REFRESH - (now - info->stamp);
311 delay = MIN(left, delay);
319 d(fprintf(stderr, "lock helper waiting for input\n"));
320 if (select(STDIN_FILENO+1, &rset, NULL, NULL, lock_info_list?&tv:NULL) == -1) {
327 /* did we get a timeout? scan for any locks that need updating */
328 if (!FD_ISSET(STDIN_FILENO, &rset)) {
329 time_t now = time (NULL);
332 d(fprintf(stderr, "Got a timeout, checking locks\n"));
334 info = lock_info_list;
336 left = (now - info->stamp);
337 if (left >= CAMEL_DOT_LOCK_REFRESH) {
338 lock_touch(info->path);
348 len = read_n(STDIN_FILENO, &msg, sizeof(msg));
352 res = CAMEL_LOCK_HELPER_STATUS_PROTOCOL;
353 if (len == sizeof(msg) && msg.magic == CAMEL_LOCK_HELPER_MAGIC) {
355 case CAMEL_LOCK_HELPER_LOCK:
356 res = CAMEL_LOCK_HELPER_STATUS_NOMEM;
357 if (msg.data > 0xffff) {
358 res = CAMEL_LOCK_HELPER_STATUS_PROTOCOL;
359 } else if ((path = malloc(msg.data+1)) != NULL) {
360 res = CAMEL_LOCK_HELPER_STATUS_PROTOCOL;
361 len = read_n(STDIN_FILENO, path, msg.data);
362 if (len == msg.data) {
364 res = lock_path(path, &msg.data);
369 case CAMEL_LOCK_HELPER_UNLOCK:
370 res = unlock_id(msg.data);
374 d(fprintf(stderr, "returning result %d\n", res));
376 msg.magic = CAMEL_LOCK_HELPER_RETURN_MAGIC;
377 write_n(STDOUT_FILENO, &msg, sizeof(msg));
380 d(fprintf(stderr, "parent exited, clsoing down remaining id's\n"));
381 while (lock_info_list)
382 unlock_id(lock_info_list->id);