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