1 /*******************************************************************************
2  *
3  * Copyright (c) 2004 Guillaume Cottenceau
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2, as
7  * published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  *
18  ******************************************************************************/
19 
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <stdio.h>
23 #include <stdarg.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <sys/types.h>
28 #include <sys/wait.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <glib.h>
32 #include <pwd.h>
33 
34 #include "tools.h"
35 #include "log.h"
36 #include "net.h"
37 
38 // converts a char* to the number it represents, with:
39 // - when failing it returns 0
40 // - it stops on first non-digit char
charstar_to_int(const char * s)41 int charstar_to_int(const char * s)
42 {
43         int number = 0;
44         while (*s && isdigit(*s)) {
45                 number = (number * 10) + (*s - '0');
46                 s++;
47         }
48         return number;
49 }
50 
vasprintf_(const char * msg,va_list args)51 char * vasprintf_(const char *msg, va_list args)
52 {
53         char s[8192];
54         vsnprintf(s, sizeof(s), msg, args);
55         return strdup(s);
56 }
57 
58 // _GNU_SOURCE's asprintf like, but
59 // - doesn't need _GNU_SOURCE
60 // - returns allocated string
61 // - never returns NULL (prints failure and exit on out of memory)
asprintf_(const char * msg,...)62 char * asprintf_(const char *msg, ...)
63 {
64         char * results;
65         va_list arg_ptr;
66         va_start(arg_ptr, msg);
67         results = vasprintf_(msg, arg_ptr);
68         va_end(arg_ptr);
69         return results;
70 }
71 
malloc_(size_t size)72 void * malloc_(size_t size)
73 {
74         void * ret = malloc(size);
75         if (ret == NULL) {
76                 fprintf(stderr, "Out of memory, exiting - size was %zd.\n", size);
77                 exit(EXIT_FAILURE);
78         }
79         return ret;
80 }
81 
realloc_(void * ptr,size_t size)82 void * realloc_(void * ptr, size_t size)
83 {
84         void * ret = realloc(ptr, size);
85         if (ret == NULL) {
86                 fprintf(stderr, "Out of memory, exiting - size was %zd.\n", size);
87                 exit(EXIT_FAILURE);
88         }
89         return ret;
90 }
91 
strdup_(char * input)92 char* strdup_(char* input)
93 {
94         char* ret = strdup(input);
95         if (ret == NULL) {
96                 fprintf(stderr, "Out of memory, exiting - input was %s.\n", input);
97                 exit(EXIT_FAILURE);
98         }
99         return ret;
100 }
101 
memdup(void * src,size_t size)102 void * memdup(void *src, size_t size)
103 {
104         void * r = malloc_(size);
105         memcpy(r, src, size);
106         return r;
107 }
108 
109 /** Should be using strlcat but could not find a GPL implementation.
110     Check http://www.courtesan.com/todd/papers/strlcpy.html it rulz. */
strconcat(char * dst,const char * src,size_t size)111 size_t strconcat(char *dst, const char *src, size_t size)
112 {
113         char *ptr = dst + strlen(dst);
114         while (ptr - dst < size - 1 && *src) {
115                 *ptr = *src;
116                 ptr++;
117                 src++;
118         }
119         *ptr = '\0';
120         return ptr - dst;
121 }
122 
123 
124 // is there a glist function to do that already?
GListp2data(GList * elem)125 void * GListp2data(GList * elem)
126 {
127         if (elem)
128                 return elem->data;
129         else
130                 return NULL;
131 }
132 
133 
134 static gpointer _g_list_fold_left_partial;
135 static GFoldFunc _g_list_fold_left_func;
_g_list_fold_left_aux(gpointer data,gpointer user_data)136 static void _g_list_fold_left_aux(gpointer data, gpointer user_data)
137 {
138         _g_list_fold_left_partial = _g_list_fold_left_func(data, _g_list_fold_left_partial, user_data);
139 }
140 
g_list_fold_left(GList * list,gpointer first,GFoldFunc func,gpointer user_data)141 gpointer g_list_fold_left(GList * list, gpointer first, GFoldFunc func, gpointer user_data)
142 {
143         _g_list_fold_left_partial = first;
144         _g_list_fold_left_func = func;
145         g_list_foreach(list, _g_list_fold_left_aux, user_data);
146         return _g_list_fold_left_partial;
147 }
148 
149 
150 static GTruthFunc _g_list_any_func;
_g_list_any_aux(gconstpointer data,gconstpointer user_data)151 static gint _g_list_any_aux(gconstpointer data, gconstpointer user_data)
152 {
153         if (_g_list_any_func(data, user_data) == TRUE)
154                 return 0;
155         else
156                 return 1;
157 }
158 
g_list_any(GList * list,GTruthFunc func,gpointer user_data)159 gboolean g_list_any(GList * list, GTruthFunc func, gpointer user_data)
160 {
161         _g_list_any_func = func;
162         if (g_list_find_custom(list, user_data, _g_list_any_aux) == NULL)
163                 return FALSE;
164         else
165                 return TRUE;
166 }
167 
close_fds(gpointer data,gpointer user_data)168 static void close_fds(gpointer data, gpointer user_data)
169 {
170         close(GPOINTER_TO_INT(data));
171 }
172 
173 static time_t last_server_register = -1;
174 
reregister_server_if_needed()175 void reregister_server_if_needed() {
176         time_t current_time;
177 
178         if (last_server_register == -1 || interval_reregister == 0)
179                 // feature disabled
180                 return;
181 
182         // don't stack zombies, do minimal housework
183         waitpid(-1, NULL, WNOHANG);
184 
185         current_time = get_current_time();
186         if ((current_time - last_server_register)/60 >= interval_reregister) {
187                 l0(OUTPUT_TYPE_INFO, "Reregistering to master server");
188                 last_server_register = current_time;
189                 int pid = fork();
190                 if (pid < 0) {
191                         l1(OUTPUT_TYPE_ERROR, "Cannot fork: %s", strerror(errno));
192                         return;
193                 }
194                 if (pid == 0) {
195                         // Need to register from a separate process because master server will test us
196                         register_server(1);
197                         _exit(EXIT_SUCCESS);
198                 }
199         }
200 }
201 
daemonize()202 void daemonize() {
203         pid_t pid, sid;
204         GList * retained_fds = NULL;
205         int fd;
206 
207         // Using the file descriptor number 10 is not possible because newlines are checked
208         // for message termination. Retain it.
209         while (1) {
210                 fd = open("/dev/null", 0);
211                 if (fd == -1) {
212                         l1(OUTPUT_TYPE_ERROR, "opening /dev/null: %s", strerror(errno));
213                         exit(EXIT_FAILURE);
214                 }
215                 if (fd < 10) {
216                         retained_fds = g_list_append(retained_fds, GINT_TO_POINTER(fd));
217                 } else if (fd == 10) {
218                         break;
219                 } else {
220                         // Past 10, so close this one and break
221                         close(fd);
222                         break;
223                 }
224         }
225         if (retained_fds) {
226                 g_list_foreach(retained_fds, close_fds, NULL);
227                 g_list_free(retained_fds);
228         }
229 
230         if (debug_mode)
231                 return;
232 
233         last_server_register = get_current_time();
234 
235         pid = fork();
236         if (pid < 0) {
237                 l1(OUTPUT_TYPE_ERROR, "Cannot fork: %s", strerror(errno));
238                 exit(EXIT_FAILURE);
239         }
240         if (pid > 0) {
241                 // Save pid if needed
242                 if (pidfile != NULL) {
243                         FILE* f = fopen(pidfile, "w");
244                         if (f == NULL) {
245                                 l2(OUTPUT_TYPE_ERROR, "Cannot open pidfile '%s' for writing: %s", pidfile, strerror(errno));
246                         } else {
247                                 char* pid_s = asprintf_("%d\n", pid);
248                                 if (fwrite(pid_s, 1, strlen(pid_s), f) < strlen(pid_s))
249                                         l1(OUTPUT_TYPE_ERROR, "Error writing pidfile to '%s'", pidfile);
250                                 fclose(f);
251                                 free(pid_s);
252                         }
253                 }
254                 // Need to register from a separate process because master server will test us
255                 l0(OUTPUT_TYPE_INFO, "registering server");
256                 register_server(0);
257                 exit(EXIT_SUCCESS);
258         }
259 
260         // Switch to specified used if needed
261         if (user_to_switch != NULL) {
262                 struct passwd* user = getpwnam(user_to_switch);
263                 if (user) {
264                         setgid(user->pw_gid);
265                         setuid(user->pw_uid);
266                 } else {
267                         l2(OUTPUT_TYPE_ERROR, "Cannot switch user to %s: %s", user_to_switch, strerror(errno));
268                 }
269         }
270 
271         // Don't stay orphan
272         sid = setsid();
273         if (sid < 0) {
274                 l1(OUTPUT_TYPE_ERROR, "Cannot setsid: %s", strerror(errno));
275                 exit(EXIT_FAILURE);
276         }
277 
278         // Don't lock a directory
279         if (chdir("/") < 0) {
280                 l1(OUTPUT_TYPE_ERROR, "Cannot chdir: %s", strerror(errno));
281                 exit(EXIT_FAILURE);
282         }
283         printf("Entering daemon mode.\n");
284 
285         close(STDIN_FILENO);
286         close(STDOUT_FILENO);
287         close(STDERR_FILENO);
288 
289         // Using the file descriptor number 0 is not possible due to the string-oriented protocol when
290         // negociating the game (since C strings cannot contain the NULL char). Retain one file descriptor.
291         fd = open("/dev/null", 0);
292         if (fd == -1) {
293                 l1(OUTPUT_TYPE_ERROR, "opening /dev/null: %s", strerror(errno));
294                 exit(EXIT_FAILURE);
295         }
296 
297         l0(OUTPUT_TYPE_INFO, "Entered daemon mode.");
298 }
299