osx_write: Add big fat comment on callback btr node.
[paraslash.git] / daemon.c
1 /*
2  * Copyright (C) 1997-2012 Andre Noll <maan@systemlinux.org>
3  *
4  * Licensed under the GPL v2. For licencing details see COPYING.
5  */
6
7 /** \file daemon.c Some helpers for programs that detach from the console. */
8
9 #include <regex.h>
10 #include <pwd.h>
11 #include <sys/types.h> /* getgrnam() */
12 #include <grp.h>
13 #include <sys/time.h>
14 #include <signal.h>
15
16 #include "para.h"
17 #include "daemon.h"
18 #include "string.h"
19 #include "color.h"
20
21 /** The internal state of the daemon. */
22 struct daemon {
23         /** See \ref daemon_flags. */
24         unsigned flags;
25         /** Set by \ref daemon_set_logfile(). */
26         char *logfile_name;
27         /** Current loglevel, see \ref daemon_set_loglevel(). */
28         int loglevel;
29         /** Used by \ref server_uptime() and \ref uptime_str(). */
30         time_t startuptime;
31         /** The file pointer if the logfile is open. */
32         FILE *logfile;
33         /** Used by para_log() if \p DF_LOG_HOSTNAME is set. */
34         char *hostname;
35         /** Used for colored log messages. */
36         char log_colors[NUM_LOGLEVELS][COLOR_MAXLEN];
37 };
38
39 static struct daemon the_daemon, *me = &the_daemon;
40
41 /**
42  * Activate default log colors.
43  *
44  * This should be called early if color support is wanted.
45  */
46 void daemon_set_default_log_colors(void)
47 {
48         int i;
49         static const char *default_log_colors[NUM_LOGLEVELS] = {
50                 [LL_DEBUG] = "normal",
51                 [LL_INFO] = "normal",
52                 [LL_NOTICE] = "normal",
53                 [LL_WARNING] = "yellow",
54                 [LL_ERROR] = "red",
55                 [LL_CRIT] = "magenta bold",
56                 [LL_EMERG] = "red bold",
57         };
58         for (i = 0; i < NUM_LOGLEVELS; i++)
59                 color_parse_or_die(default_log_colors[i], me->log_colors[i]);
60 }
61
62 /**
63  * Set the color for one loglevel.
64  *
65  * \param arg The loglevel/color specifier.
66  *
67  * \a arg must be of the form "ll:[fg [bg]] [attr]".
68  */
69 void daemon_set_log_color_or_die(char const *arg)
70 {
71         char *p = strchr(arg, ':');
72         int ret, ll;
73
74         if (!p)
75                 goto err;
76         ret = get_loglevel_by_name(arg);
77         if (ret < 0)
78                 goto err;
79         ll = ret;
80         p++;
81         color_parse_or_die(p, me->log_colors[ll]);
82         return;
83 err:
84         PARA_EMERG_LOG("%s: color syntax error\n", arg);
85         exit(EXIT_FAILURE);
86 }
87
88 /**
89  * Init or change the name of the log file.
90  *
91  * \param logfile_name The full path of the logfile.
92  */
93 void daemon_set_logfile(char *logfile_name)
94 {
95         free(me->logfile_name);
96         me->logfile_name = NULL;
97         if (logfile_name)
98                 me->logfile_name = para_strdup(logfile_name);
99 }
100
101 /**
102  * Suppress log messages with severity lower than the given loglevel.
103  *
104  * \param loglevel The smallest level that should be logged.
105  */
106 void daemon_set_loglevel(char *loglevel)
107 {
108         int ret = get_loglevel_by_name(loglevel);
109
110         assert(ret >= 0);
111         me->loglevel = ret;
112 }
113
114 /**
115  * Set one of the daemon config flags.
116  *
117  * \param flag The flag to set.
118  *
119  * \sa \ref daemon_flags.
120  */
121 void daemon_set_flag(unsigned flag)
122 {
123         me->flags |= flag;
124 }
125
126 /**
127  * Clear one of the daemon config flags.
128  *
129  * \param flag The flag to clear.
130  *
131  * \sa \ref daemon_flags.
132  */
133 void daemon_clear_flag(unsigned flag)
134 {
135         me->flags &= ~flag;
136 }
137
138 static bool daemon_test_flag(unsigned flag)
139 {
140         return me->flags & flag;
141 }
142
143 static void dummy_sighandler(__a_unused int s)
144 {
145 }
146
147 /**
148  * Do the usual stuff to become a daemon.
149  *
150  * \param parent_waits Whether the parent process should pause before exit.
151  *
152  * Fork, become session leader, cd to /, and dup fd 0, 1, 2 to /dev/null. If \a
153  * parent_waits is false, the parent process terminates immediately.
154  * Otherwise, it calls pause() to sleep until it receives \p SIGTERM or \p
155  * SIGCHLD and exits successfully thereafter. This behaviour is useful if the
156  * daemon process should not detach from the console until the child process
157  * has completed its setup.
158  *
159  * \sa fork(2), setsid(2), dup(2), pause(2).
160  */
161 void daemonize(bool parent_waits)
162 {
163         pid_t pid;
164         int null;
165
166         PARA_INFO_LOG("daemonizing\n");
167         pid = fork();
168         if (pid < 0)
169                 goto err;
170         if (pid) {
171                 if (parent_waits) {
172                         signal(SIGTERM, dummy_sighandler);
173                         signal(SIGCHLD, dummy_sighandler);
174                         pause();
175                 }
176                 exit(EXIT_SUCCESS); /* parent exits */
177         }
178         /* become session leader */
179         if (setsid() < 0)
180                 goto err;
181         if (chdir("/") < 0)
182                 goto err;
183         null = open("/dev/null", O_RDONLY);
184         if (null < 0)
185                 goto err;
186         if (dup2(null, STDIN_FILENO) < 0)
187                 goto err;
188         if (dup2(null, STDOUT_FILENO) < 0)
189                 goto err;
190         if (dup2(null, STDERR_FILENO) < 0)
191                 goto err;
192         close(null);
193         return;
194 err:
195         PARA_EMERG_LOG("fatal: %s\n", strerror(errno));
196         exit(EXIT_FAILURE);
197 }
198
199 /**
200  * Close the log file of the daemon.
201  */
202 void daemon_close_log(void)
203 {
204         if (!me->logfile)
205                 return;
206         PARA_INFO_LOG("closing logfile\n");
207         fclose(me->logfile);
208         me->logfile = NULL;
209 }
210
211 /**
212  * fopen() the logfile in append mode.
213  *
214  * \return Either succeeds or exits.
215  */
216 void daemon_open_log_or_die(void)
217 {
218         daemon_close_log();
219         if (!me->logfile_name)
220                 return;
221         me->logfile = fopen(me->logfile_name, "a");
222         if (!me->logfile) {
223                 PARA_EMERG_LOG("can not open %s: %s\n", me->logfile_name,
224                         strerror(errno));
225                 exit(EXIT_FAILURE);
226         }
227         setlinebuf(me->logfile);
228 }
229
230 /**
231  * Log the startup message containing the paraslash version.
232  */
233 void log_welcome(const char *whoami)
234 {
235         PARA_INFO_LOG("welcome to %s " PACKAGE_VERSION " ("BUILD_DATE")\n",
236                 whoami);
237 }
238
239 /**
240  * Give up superuser privileges.
241  *
242  * \param username The user to switch to.
243  * \param groupname The group to switch to.
244  *
245  * This function returns immediately if not invoked with EUID zero. Otherwise,
246  * it tries to obtain the GID of \a groupname and the UID of \a username.  On
247  * success, effective and real GID/UID and the saved set-group-ID/set-user-ID
248  * are all set accordingly. On errors, an appropriate message is logged and
249  * exit() is called to terminate the process.
250  *
251  * \sa getpwnam(3), getuid(2), setuid(2), getgrnam(2), setgid(2)
252  */
253 void drop_privileges_or_die(const char *username, const char *groupname)
254 {
255         struct passwd *p;
256         char *tmp;
257
258         if (geteuid())
259                 return;
260         if (groupname) {
261                 struct group *g = getgrnam(groupname);
262                 if (!g) {
263                         PARA_EMERG_LOG("failed to get group %s: %s\n",
264                                 groupname, strerror(errno));
265                         exit(EXIT_FAILURE);
266                 }
267                 if (setgid(g->gr_gid) < 0) {
268                         PARA_EMERG_LOG("failed to set group id %d: %s\n",
269                                 (int)g->gr_gid, strerror(errno));
270                         exit(EXIT_FAILURE);
271                 }
272         }
273         if (!username) {
274                 PARA_EMERG_LOG("root privileges, but no user option given\n");
275                 exit(EXIT_FAILURE);
276         }
277         tmp = para_strdup(username);
278         p = getpwnam(tmp);
279         free(tmp);
280         if (!p) {
281                 PARA_EMERG_LOG("%s: no such user\n", username);
282                 exit(EXIT_FAILURE);
283         }
284         PARA_INFO_LOG("dropping root privileges\n");
285         if (setuid(p->pw_uid) < 0) {
286                 PARA_EMERG_LOG("failed to set effective user ID (%s)",
287                         strerror(errno));
288                 exit(EXIT_FAILURE);
289         }
290         PARA_DEBUG_LOG("uid: %d, euid: %d\n", (int)getuid(), (int)geteuid());
291 }
292
293 /**
294  * Set the server startup time.
295  *
296  * \param startuptime The value to store as the server start time.
297  *
298  * This should be called once on startup with \a startuptime either NULL or a
299  * pointer to a struct timeval which contains the current time. If \a
300  * startuptime is NULL, the server start time is set to the current time.
301  *
302  * \sa time(2), difftime(3) \ref get_server_uptime(), \ref
303  * get_server_uptime_str().
304  */
305 void set_server_start_time(const struct timeval *startuptime)
306 {
307         if (startuptime)
308                 me->startuptime = startuptime->tv_sec;
309         else
310                 time(&me->startuptime);
311 }
312
313 /**
314  * Get the server uptime.
315  *
316  * \param current_time The current time.
317  *
318  * The \a current_time pointer may be \p NULL. In this case the function
319  * obtains the current time from the system.
320  *
321  * \return This returns the server uptime in seconds, i.e. the difference
322  * between the current time and the value stored previously via \ref
323  * set_server_start_time().
324  */
325 time_t get_server_uptime(const struct timeval *current_time)
326 {
327         time_t t;
328
329         if (current_time)
330                 return current_time->tv_sec - me->startuptime;
331         time(&t);
332         return difftime(t, me->startuptime);
333 }
334
335 /**
336  * Construct a string containing the current uptime.
337  *
338  * \param current_time See a \ref get_server_uptime().
339  *
340  * \return A dynamically allocated string of the form "days:hours:minutes".
341  *
342  * \sa server_uptime.
343  */
344 __malloc char *get_server_uptime_str(const struct timeval *current_time)
345 {
346         long t = get_server_uptime(current_time);
347         return make_message("%li:%02li:%02li", t / 86400,
348                 (t / 3600) % 24, (t / 60) % 60);
349 }
350
351 /**
352  * The log function for para_server and para_audiod.
353  *
354  * \param ll The log level.
355  * \param fmt The format string describing the log message.
356  */
357 __printf_2_3 void daemon_log(int ll, const char* fmt,...)
358 {
359         va_list argp;
360         FILE *fp;
361         struct tm *tm;
362         char *color;
363         bool log_time = daemon_test_flag(DF_LOG_TIME), log_timing =
364                 daemon_test_flag(DF_LOG_TIMING);
365
366         ll = PARA_MIN(ll, NUM_LOGLEVELS - 1);
367         ll = PARA_MAX(ll, LL_DEBUG);
368         if (ll < me->loglevel)
369                 return;
370
371         fp = me->logfile? me->logfile : stderr;
372         color = daemon_test_flag(DF_COLOR_LOG)? me->log_colors[ll] : NULL;
373         if (color)
374                 fprintf(fp, "%s", color);
375         if (log_time || log_timing) {
376                 struct timeval tv;
377                 gettimeofday(&tv, NULL);
378                 if (daemon_test_flag(DF_LOG_TIME)) { /* print date and time */
379                         time_t t1 = tv.tv_sec;
380                         char str[100];
381                         tm = localtime(&t1);
382                         strftime(str, sizeof(str), "%b %d %H:%M:%S", tm);
383                         fprintf(fp, "%s%s", str, log_timing? ":" : " ");
384                 }
385                 if (log_timing) /* print milliseconds */
386                         fprintf(fp, "%04lu ", (long unsigned)tv.tv_usec / 1000);
387         }
388         if (daemon_test_flag(DF_LOG_HOSTNAME)) {
389                 if (!me->hostname)
390                         me->hostname = para_hostname();
391                 fprintf(fp, "%s ", me->hostname);
392         }
393         if (daemon_test_flag(DF_LOG_LL)) /* log loglevel */
394                 fprintf(fp, "(%d) ", ll);
395         if (daemon_test_flag(DF_LOG_PID)) { /* log pid */
396                 pid_t mypid = getpid();
397                 fprintf(fp, "(%d) ", (int)mypid);
398         }
399         va_start(argp, fmt);
400         vfprintf(fp, fmt, argp);
401         va_end(argp);
402         if (color)
403                 fprintf(fp, "%s", COLOR_RESET);
404 }