2 * Copyright (C) 1997-2009 Andre Noll <maan@systemlinux.org>
4 * Licensed under the GPL v2. For licencing details see COPYING.
7 /** \file daemon.c Some helpers for programs that detach from the console. */
11 #include <sys/types.h> /* getgrnam() */
17 /** The internal state of the daemon. */
19 /** See \ref daemon_flags. */
21 /** Set by \ref daemon_set_logfile(). */
23 /** Current loglevel, see \ref daemon_set_loglevel(). */
26 /** Used by \ref server_uptime() and \ref uptime_str(). */
28 /** The file pointer if the logfile is open. */
30 /** Used by para_log() if \p DF_LOG_HOSTNAME is set. */
32 /** Used for colored log messages. */
33 char log_colors[NUM_LOGLEVELS][COLOR_MAXLEN];
36 static struct daemon the_daemon, *me = &the_daemon;
39 * Activate default log colors.
41 * This should be called early if color support is wanted.
43 void daemon_set_default_log_colors(void)
46 static const char *default_log_colors[NUM_LOGLEVELS] = {
47 [LL_DEBUG] = "normal",
49 [LL_NOTICE] = "normal",
50 [LL_WARNING] = "yellow",
52 [LL_CRIT] = "magenta bold",
53 [LL_EMERG] = "red bold",
55 for (i = 0; i < NUM_LOGLEVELS; i++) {
56 int ret = color_parse(default_log_colors[i], me->log_colors[i]);
61 static int get_loglevel_by_name(const char *txt, size_t n)
63 if (!strncasecmp(txt, "debug", n))
65 if (!strncasecmp(txt, "info", n))
67 if (!strncasecmp(txt, "notice", n))
69 if (!strncasecmp(txt, "warning", n))
71 if (!strncasecmp(txt, "error", n))
73 if (!strncasecmp(txt, "crit", n))
75 if (!strncasecmp(txt, "emerg", n))
81 * Set the color for one loglevel.
83 * \param arg The loglevel/color specifier.
85 * \a arg must be of the form "ll:[fg [bg]] [attr]".
87 * \return 1 On success, -1 on errors.
89 int daemon_set_log_color(char const *arg)
91 char *p = strchr(arg, ':');
96 ret = get_loglevel_by_name(arg, p - arg);
101 ret = color_parse(p, me->log_colors[ll]);
106 PARA_ERROR_LOG("%s: color syntax error\n", arg);
112 * Init or change the name of the log file.
114 * \param logfile_name The full path of the logfile.
116 void daemon_set_logfile(char *logfile_name)
118 free(me->logfile_name);
119 me->logfile_name = NULL;
121 me->logfile_name = para_strdup(logfile_name);
125 * Supress log messages with severity lower than the given loglevel.
127 * \param loglevel The smallest level that should be logged.
129 void daemon_set_loglevel(int loglevel)
131 me->loglevel = loglevel;
135 * Set one of the daemon config flags.
137 * \param flag The flag to set.
139 * \sa \ref daemon_flags.
141 void daemon_set_flag(unsigned flag)
147 * Clear one of the daemon config flags.
149 * \param flag The flag to clear.
151 * \sa \ref daemon_flags.
153 void daemon_clear_flag(unsigned flag)
158 static unsigned daemon_test_flag(unsigned flag)
160 return me->flags & flag;
164 * Do the usual stuff to become a daemon.
166 * Fork, become session leader, dup fd 0, 1, 2 to /dev/null.
168 * \sa fork(2), setsid(2), dup(2).
175 PARA_INFO_LOG("daemonizing\n");
180 exit(EXIT_SUCCESS); /* parent exits */
181 /* become session leader */
187 null = open("/dev/null", O_RDONLY);
190 if (dup2(null, STDIN_FILENO) < 0)
192 if (dup2(null, STDOUT_FILENO) < 0)
194 if (dup2(null, STDERR_FILENO) < 0)
199 PARA_EMERG_LOG("fatal: %s\n", strerror(errno));
204 * Close the log file of the daemon.
206 void daemon_close_log(void)
210 PARA_INFO_LOG("closing logfile\n");
216 * fopen() the logfile in append mode.
218 * \return Either succeeds or exits.
220 void daemon_open_log_or_die(void)
223 if (!me->logfile_name)
225 me->logfile = fopen(me->logfile_name, "a");
227 PARA_EMERG_LOG("can not open %s: %s\n", me->logfile_name,
231 setlinebuf(me->logfile);
235 * Log the startup message containing the paraslash version.
237 void log_welcome(const char *whoami, int loglevel)
239 PARA_INFO_LOG("welcome to %s " PACKAGE_VERSION " ("BUILD_DATE")\n",
241 PARA_DEBUG_LOG("using loglevel %d\n", loglevel);
245 * Give up superuser privileges.
247 * \param username The user to switch to.
248 * \param groupname The group to switch to.
250 * This function returns immediately if not invoked with EUID zero. Otherwise,
251 * it tries to obtain the GID of \a groupname and the UID of \a username. On
252 * success, effective and real GID/UID and the saved set-group-ID/set-user-ID
253 * are all set accordingly. On errors, an appropriate message is logged and
254 * exit() is called to terminate the process.
256 * \sa getpwnam(3), getuid(2), setuid(2), getgrnam(2), setgid(2)
258 void drop_privileges_or_die(const char *username, const char *groupname)
266 struct group *g = getgrnam(groupname);
268 PARA_EMERG_LOG("failed to get group %s: %s\n",
269 groupname, strerror(errno));
272 if (setgid(g->gr_gid) < 0) {
273 PARA_EMERG_LOG("failed to set group id %d: %s\n",
274 (int)g->gr_gid, strerror(errno));
279 PARA_EMERG_LOG("root privileges, but no user option given\n");
282 tmp = para_strdup(username);
286 PARA_EMERG_LOG("%s: no such user\n", username);
289 PARA_INFO_LOG("dropping root privileges\n");
291 PARA_DEBUG_LOG("uid: %d, euid: %d\n", (int)getuid(), (int)geteuid());
295 * Set/get the server uptime.
297 * \param set_or_get Chose one of the two modes.
299 * This should be called at startup time with \a set_or_get equal to \p
300 * UPTIME_SET which sets the uptime to zero. Subsequent calls with \a
301 * set_or_get equal to \p UPTIME_GET return the uptime.
303 * \return Zero if called with \a set_or_get equal to \p UPTIME_SET, the number
304 * of seconds ellapsed since the last reset otherwise.
306 * \sa time(2), difftime(3).
308 time_t server_uptime(enum uptime set_or_get)
313 if (set_or_get == UPTIME_SET) {
314 time(&me->startuptime);
318 diff = difftime(now, me->startuptime);
319 return (time_t) diff;
323 * Construct string containing uptime.
325 * \return A dynamically allocated string of the form "days:hours:minutes".
329 __malloc char *uptime_str(void)
331 long t = server_uptime(UPTIME_GET);
332 return make_message("%li:%02li:%02li", t / 86400,
333 (t / 3600) % 24, (t / 60) % 60);
337 * The log function for para_server and para_audiod.
339 * \param ll The log level.
340 * \param fmt The format string describing the log message.
342 __printf_2_3 void para_log(int ll, const char* fmt,...)
348 char *color, str[MAXLINE] = "";
350 ll = PARA_MIN(ll, NUM_LOGLEVELS - 1);
351 ll = PARA_MAX(ll, LL_DEBUG);
352 if (ll < me->loglevel)
355 fp = me->logfile? me->logfile : stderr;
356 color = daemon_test_flag(DF_COLOR_LOG)? me->log_colors[ll] : NULL;
358 fprintf(fp, "%s", color);
359 if (daemon_test_flag(DF_LOG_TIME)) {
363 strftime(str, MAXLINE, "%b %d %H:%M:%S", tm);
364 fprintf(fp, "%s ", str);
366 if (daemon_test_flag(DF_LOG_HOSTNAME)) {
368 me->hostname = para_hostname();
369 fprintf(fp, "%s ", me->hostname);
371 if (daemon_test_flag(DF_LOG_LL)) /* log loglevel */
372 fprintf(fp, "(%d) ", ll);
373 if (daemon_test_flag(DF_LOG_PID)) { /* log pid */
374 pid_t mypid = getpid();
375 fprintf(fp, "(%d) ", (int)mypid);
378 vfprintf(fp, fmt, argp);
381 fprintf(fp, "%s", COLOR_RESET);