3 * Copyright (C) 2010 Adrian Perez <aperez@igalia.com>
5 * Distributed under terms of the MIT license.
8 /* Needed for nanosleep(2) */
9 #define _POSIX_C_SOURCE 199309L
11 /* Needed for fsync(2) */
14 /* Needed for ULLONG_MAX */
20 #include <sys/resource.h>
39 if (fcntl (fd, F_SETFD, FD_CLOEXEC) < 0)
40 w_die ("unable to set FD_CLOEXEC\n");
45 safe_sleep (unsigned seconds)
50 /* No time? Save up a syscall! */
57 retval = nanosleep (&ts, &ts);
58 } while (retval == -1 && errno == EINTR);
63 safe_sigaction (const char *name, int signum, struct sigaction *sa)
65 if (sigaction (signum, sa, NULL) < 0) {
66 w_die ("could not set handler for signal $s ($i): $E\n",
73 safe_setrlimit (int what, long value)
77 if (getrlimit (what, &r) < 0)
78 w_die ("getrlimit ($s) failed: $E\n", limit_name (what));
80 if (value < 0 || (unsigned long) value > r.rlim_max)
81 r.rlim_cur = r.rlim_max;
85 if (setrlimit (what, &r) < 0)
86 w_die ("setrlimit ($s=$l) failed: $E\n", limit_name (what), value);
91 interruptible_sleep (unsigned seconds)
95 if (!seconds) return 0;
100 return !(nanosleep (&ts, NULL) == -1 && errno == EINTR);
105 name_to_uidgid (const char *str,
117 num = strtoul (str, &dummy, 0);
118 if (num == ULONG_MAX && errno == ERANGE)
121 if (!dummy || *dummy == '\0') {
122 if ((pw = getpwuid ((uid_t) num)) == NULL)
126 if ((pw = getpwnam (str)) == NULL)
130 *uresult = pw->pw_uid;
131 *gresult = pw->pw_gid;
138 name_to_gid (const char *str, gid_t *result)
147 num = strtoul (str, &dummy, 0);
149 if (num == ULONG_MAX && errno == ERANGE)
152 if (!dummy || *dummy == '\0') {
153 *result = (gid_t) num;
157 if ((grp = getgrnam (str)) == NULL)
161 *result = grp->gr_gid;
167 _parse_gids (char *s,
176 if (u->ngid >= DMON_GID_COUNT) {
177 w_io_format (w_stderr,
178 "more than $L groups given, ignoring additional ones\n",
183 pos = strchr (s, ':');
187 if (name_to_gid (s, &gid)) {
188 if (pos != NULL) *pos = ':';
195 u->gids[u->ngid++] = gid;
197 return (pos == NULL) ? 0 : _parse_gids (pos + 1, u);
202 parse_uidgids (char *s,
210 memset (u, 0x00, sizeof (uidgid_t));
212 pos = strchr (s, ':');
216 if (name_to_uidgid (s, &u->uid, &u->gid)) {
217 if (pos != NULL) *pos = ':';
226 return (pos == NULL) ? 0 : _parse_gids (pos + 1, u);
234 int nullfd = open ("/dev/null", O_RDWR, 0);
237 w_die ("cannot daemonize, unable to open '/dev/null': $E\n");
241 if (dup2 (nullfd, STDIN_FILENO) < 0)
242 w_die ("cannot daemonize, unable to redirect stdin: $E\n");
243 if (dup2 (nullfd, STDOUT_FILENO) < 0)
244 w_die ("cannot daemonize, unable to redirect stdout: $E\n");
245 if (dup2 (nullfd, STDERR_FILENO) < 0)
246 w_die ("cannot daemonize, unable to redirect stderr: $E\n");
250 if (pid < 0) w_die ("cannot daemonize: $E\n");
251 if (pid > 0) _exit (EXIT_SUCCESS);
259 time_period_to_seconds (const char *str, unsigned long long *result)
261 unsigned long long val = 0;
267 val = strtoull (str, &endpos, 0);
269 if (val == ULLONG_MAX && errno == ERANGE)
272 if (!endpos || *endpos == '\0')
276 case 'y': val *= 60 * 60 * 24 * 365; break; /* years */
277 case 'M': val *= 60 * 60 * 24 * 30; break; /* months */
278 case 'w': val *= 60 * 60 * 24 * 7; break; /* weeks */
279 case 'd': val *= 60 * 60 * 24; break; /* days */
280 case 'h': val *= 60 * 60; break; /* hours */
281 case 'm': val *= 60; break; /* minutes */
282 default : return W_YES;
292 time_period_option (const w_opt_context_t *ctx)
295 w_assert (ctx->option);
296 w_assert (ctx->option->extra);
297 w_assert (ctx->option->narg == 1);
299 return (time_period_to_seconds (ctx->argument[0], ctx->option->extra))
306 storage_size_to_bytes (const char *str, unsigned long long *result)
308 unsigned long long val = 0;
314 val = strtoull (str, &endpos, 0);
316 if (val == ULLONG_MAX && errno == ERANGE)
319 if (!endpos || *endpos == '\0')
323 case 'g': case 'G': val *= 1024 * 1024 * 1024; break; /* gigabytes */
324 case 'm': case 'M': val *= 1024 * 1024; break; /* megabytes */
325 case 'k': case 'K': val *= 1024; break; /* kilobytes */
327 default : return W_YES;
337 storage_size_option (const w_opt_context_t *ctx)
340 w_assert (ctx->option);
341 w_assert (ctx->option->extra);
342 w_assert (ctx->option->narg == 1);
344 return (storage_size_to_bytes (ctx->argument[0], ctx->option->extra))
351 _parse_limit_time (const char *sval, long *rval)
353 unsigned long long val;
356 w_assert (sval != NULL);
357 w_assert (rval != NULL);
359 failed = time_period_to_seconds (sval, &val);
361 return failed || (val > LONG_MAX);
366 _parse_limit_number (const char *sval, long *rval)
368 w_assert (sval != NULL);
369 w_assert (rval != NULL);
370 return !(sscanf (sval, "%li", rval) == 1);
375 _parse_limit_bytes (const char *sval, long *rval)
377 unsigned long long val;
380 w_assert (sval != NULL);
381 w_assert (rval != NULL);
383 failed = storage_size_to_bytes (sval, &val);
385 return failed || (val > LONG_MAX);
389 static const struct {
392 wbool (*parse)(const char*, long*);
396 { "vmem", RLIMIT_AS, _parse_limit_bytes,
397 "Maximum size of process' virtual memory (bytes)" },
398 #endif /* RLIMIT_AS */
400 { "core", RLIMIT_CORE, _parse_limit_bytes,
401 "Maximum size of core file (bytes)" },
402 #endif /* RLIMIT_CORE */
404 { "cpu", RLIMIT_CPU, _parse_limit_time,
405 "Maximum CPU time used (seconds)" },
406 #endif /* RLIMIT_CPU */
408 { "data", RLIMIT_DATA, _parse_limit_bytes,
409 "Maximum size of data segment (bytes)" },
410 #endif /* RLIMIT_DATA */
412 { "fsize", RLIMIT_FSIZE, _parse_limit_bytes,
413 "Maximum size of created files (bytes)" },
414 #endif /* RLIMIT_FSIZE */
416 { "locks", RLIMIT_LOCKS, _parse_limit_number,
417 "Maximum number of locked files" },
418 #endif /* RLIMIT_LOCKS */
419 #ifdef RLIMIT_MEMLOCK
420 { "mlock", RLIMIT_MEMLOCK, _parse_limit_bytes,
421 "Maximum number of bytes locked in RAM (bytes)" },
422 #endif /* RLIMIT_MEMLOCK */
423 #ifdef RLIMIT_MSGQUEUE
424 { "msgq", RLIMIT_MSGQUEUE, _parse_limit_number,
425 "Maximum number of bytes used in message queues (bytes)" },
426 #endif /* RLIMIT_MSGQUEUE */
428 { "nice", RLIMIT_NICE, _parse_limit_number,
429 "Ceiling for the process nice value" },
430 #endif /* RLIMIT_NICE */
432 { "files", RLIMIT_NOFILE, _parse_limit_number,
433 "Maximum number of open files" },
434 #endif /* RLIMIT_NOFILE */
436 { "nproc", RLIMIT_NPROC, _parse_limit_number,
437 "Maximum number of processes" },
438 #endif /* RLIMIT_NPROC */
440 #warning Building support for RLIMIT_RSS, this may not work on Linux 2.6+
441 { "rss", RLIMIT_RSS, _parse_limit_number,
442 "Maximum number of pages resident in RAM" },
443 #endif /* RLIMIT_RSS */
445 { "rtprio", RLIMIT_RTPRIO, _parse_limit_number,
446 "Ceiling for the real-time priority" },
447 #endif /* RLIMIT_RTPRIO */
449 { "rttime", RLIMIT_RTTIME, _parse_limit_time,
450 "Maximum real-time CPU time used (seconds)" },
451 #endif /* RLIMIT_RTTIME */
452 #ifdef RLIMIT_SIGPENDING
453 { "sigpending", RLIMIT_SIGPENDING, _parse_limit_number,
454 "Maximum number of queued signals" },
455 #endif /* RLIMIT_SIGPENDING */
457 { "stack", RLIMIT_STACK, _parse_limit_bytes,
458 "Maximum stack segment size (bytes)" },
459 #endif /* RLIMIT_STACK */
464 parse_limit_arg (const char *str, int *what, long *value)
468 w_assert (str != NULL);
469 w_assert (what != NULL);
470 w_assert (value != NULL);
472 if (!strcmp (str, "help")) {
473 for (i = 0; i < w_lengthof (rlimit_specs); i++) {
474 w_io_format (w_stdout, "$s -- $s\n",
475 rlimit_specs[i].name,
476 rlimit_specs[i].desc);
481 for (i = 0; i < w_lengthof (rlimit_specs); i++) {
482 unsigned nlen = strlen (rlimit_specs[i].name);
483 if (!strncmp (str, rlimit_specs[i].name, nlen) && str[nlen] == '=') {
484 *what = rlimit_specs[i].what;
485 return ((*rlimit_specs[i].parse) (str + nlen + 1, value));
495 limit_name (int what)
499 for (i = 0; i < w_lengthof (rlimit_specs); i++) {
500 if (what == rlimit_specs[i].what)
501 return rlimit_specs[i].name;
507 #ifndef REPLACE_ARGS_VCHUNK
508 #define REPLACE_ARGS_VCHUNK 16
509 #endif /* !REPLACE_ARGS_VCHUNK */
511 #ifndef REPLACE_ARGS_SCHUNK
512 #define REPLACE_ARGS_SCHUNK 32
513 #endif /* !REPLACE_ARGS_SCHUNK */
516 replace_args_string (const char *str,
522 int maxarg = REPLACE_ARGS_VCHUNK;
527 char **argv = w_alloc (char*, maxarg);
533 /* Copy argv[0] pointer */
534 argv[numarg++] = (*pargv)[0];
536 while ((ch = *str++) != '\0') {
537 if (!quotes && isspace (ch)) {
540 * Got spaces not inside a quote, and the current argument
541 * is empty: skip spaces at the left side of an argument.
547 * Not inside quotes, got space: add '\0', split and reset
549 if (numarg >= maxarg) {
550 maxarg += REPLACE_ARGS_VCHUNK;
551 argv = w_resize (argv, char*, maxarg);
554 /* Add terminating "\0" */
556 smax += REPLACE_ARGS_SCHUNK;
557 s = w_resize (s, char, smax);
560 /* Save string in array. */
571 * Got a character which is not an space, or *is* an space inside
572 * quotes. When character is the same as used for start quoting,
573 * end quoting, or start quoting if it's a quote; otherwise, just
574 * store the character.
576 if (quotes && quotes == ch) {
579 else if (ch == '"' || ch == '\'') {
586 #elif defined(EILSEQ)
589 #warning Both EINVAL and EILSEQ are undefined, error message will be ambiguous
595 smax += REPLACE_ARGS_SCHUNK;
596 s = w_resize (s, char, smax);
602 /* If there is still an in-progres string, store it. */
604 /* Add terminating "\0" */
606 smax += REPLACE_ARGS_SCHUNK;
607 s = w_resize (s, char, smax);
610 /* Save string in array. */
615 /* Copy remaining pointers at the tail */
616 if ((maxarg - numarg) <= *pargc) {
618 argv = w_resize (argv, char*, maxarg);
620 for (ch = 1; ch < *pargc; ch++)
621 argv[numarg++] = (*pargv)[ch];
623 /* Add terminating NULL */
634 replace_args_shift (unsigned amount,
639 char **argv = *pargv;
644 w_assert (amount > 0);
645 w_assert (*pargc > (int) amount);
649 for (i = 1; i < argc; i++) {
657 /* vim: expandtab shiftwidth=4 tabstop=4