1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 
3 #include "common.h"
4 
5 #include <sys/types.h>
6 #include <sys/wait.h>
7 #include <signal.h>
8 #include <unistd.h>
9 #include <errno.h>
10 #include <string.h>
11 #include <getopt.h>
12 #include <stdbool.h>
13 #include <fcntl.h>
14 
15 #include <glib.h>
16 
17 #include "utils.h"
18 #include "log.h"
19 #include "seafile-controller.h"
20 
21 #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__)
22 #include <sys/sysctl.h>
23 #include <sys/types.h>
24 #include <sys/user.h>
25 #include <limits.h>
26 
27 #ifndef WITH_PROC_FS
28 #define WITH_PROC_FS g_file_test("/proc/curproc", G_FILE_TEST_EXISTS)
29 #endif
30 
31 static char *command_name = NULL;
32 #endif
33 
34 #define CHECK_PROCESS_INTERVAL 10        /* every 10 seconds */
35 
36 #if defined(__sun)
37 #define PROC_SELF_PATH "/proc/self/path/a.out"
38 #else
39 #define PROC_SELF_PATH "/proc/self/exe"
40 #endif
41 
42 SeafileController *ctl;
43 
44 static char *controller_pidfile = NULL;
45 
46 char *bin_dir = NULL;
47 char *installpath = NULL;
48 char *topdir = NULL;
49 
50 char *seafile_ld_library_path = NULL;
51 
52 static const char *short_opts = "hvftc:d:l:g:G:P:F:";
53 static const struct option long_opts[] = {
54     { "help", no_argument, NULL, 'h', },
55     { "version", no_argument, NULL, 'v', },
56     { "foreground", no_argument, NULL, 'f', },
57     { "test", no_argument, NULL, 't', },
58     { "config-dir", required_argument, NULL, 'c', },
59     { "seafile-dir", required_argument, NULL, 'd', },
60     { "central-config-dir", required_argument, NULL, 'F' },
61     { "logdir", required_argument, NULL, 'l', },
62     { "ccnet-debug-level", required_argument, NULL, 'g' },
63     { "seafile-debug-level", required_argument, NULL, 'G' },
64     { "pidfile", required_argument, NULL, 'P' },
65     { NULL, 0, NULL, 0, },
66 };
67 
68 static void controller_exit (int code) __attribute__((noreturn));
69 
70 static int read_seafdav_config();
71 
72 static void
controller_exit(int code)73 controller_exit (int code)
74 {
75     if (code != 0) {
76         seaf_warning ("seaf-controller exited with code %d\n", code);
77     }
78     exit(code);
79 }
80 
81 //
82 // Utility functions Start
83 //
84 
85 /* returns the pid of the newly created process */
86 static int
spawn_process(char * argv[],bool is_python_process)87 spawn_process (char *argv[], bool is_python_process)
88 {
89     char **ptr = argv;
90     GString *buf = g_string_new(argv[0]);
91     while (*(++ptr)) {
92         g_string_append_printf (buf, " %s", *ptr);
93     }
94     seaf_message ("spawn_process: %s\n", buf->str);
95     g_string_free (buf, TRUE);
96 
97     int pipefd[2] = {0, 0};
98     if (is_python_process) {
99         if (pipe(pipefd) < 0) {
100             seaf_warning("Failed to create pipe.\n");
101         }
102         fcntl(pipefd[0], F_SETFL, O_NONBLOCK);
103     }
104 
105     pid_t pid = fork();
106 
107     if (pid == 0) {
108         if (is_python_process) {
109             if (pipefd[0] > 0 && pipefd[1] > 0) {
110                 close(pipefd[0]);
111                 dup2(pipefd[1], 2);
112             }
113         }
114         /* child process */
115         execvp (argv[0], argv);
116         seaf_warning ("failed to execvp %s\n", argv[0]);
117 
118         if (pipefd[1] > 0) {
119             close(pipefd[1]);
120         }
121 
122         exit(-1);
123     } else {
124         /* controller */
125         if (pid == -1)
126             seaf_warning ("error when fork %s: %s\n", argv[0], strerror(errno));
127         else
128             seaf_message ("spawned %s, pid %d\n", argv[0], pid);
129 
130         if (is_python_process) {
131             char child_stderr[1024] = {0};
132             if (pipefd[0] > 0 && pipefd[1] > 0){
133                 close(pipefd[1]);
134                 sleep(1);
135                 while (read(pipefd[0], child_stderr, sizeof(child_stderr)) > 0)
136                     seaf_warning("%s", child_stderr);
137                 close(pipefd[0]);
138             }
139         }
140         return (int)pid;
141     }
142 }
143 
144 #define PID_ERROR_ENOENT 0
145 #define PID_ERROR_OTHER  -1
146 
147 /**
148  * @return
149  * - pid if successfully opened and read the file
150  * - PID_ERROR_ENOENT if file not exists,
151  * - PID_ERROR_OTHER if other errors
152  */
153 static int
read_pid_from_pidfile(const char * pidfile)154 read_pid_from_pidfile (const char *pidfile)
155 {
156     FILE *pf = g_fopen (pidfile, "r");
157     if (!pf) {
158         if (errno == ENOENT) {
159             return PID_ERROR_ENOENT;
160         } else {
161             return PID_ERROR_OTHER;
162         }
163     }
164 
165     int pid = PID_ERROR_OTHER;
166     if (fscanf (pf, "%d", &pid) < 0) {
167         seaf_warning ("bad pidfile format: %s\n", pidfile);
168         fclose(pf);
169         return PID_ERROR_OTHER;
170     }
171 
172     fclose(pf);
173 
174     return pid;
175 }
176 
177 static void
kill_by_force(int which)178 kill_by_force (int which)
179 {
180     if (which < 0 || which >= N_PID)
181         return;
182 
183     char *pidfile = ctl->pidfile[which];
184     int pid = read_pid_from_pidfile(pidfile);
185     if (pid > 0) {
186         // if SIGKILL send success, then remove related pid file
187         if (kill ((pid_t)pid, SIGKILL) == 0) {
188             g_unlink (pidfile);
189         }
190     }
191 }
192 
193 //
194 // Utility functions End
195 //
196 
197 static int
start_seaf_server()198 start_seaf_server ()
199 {
200     if (!ctl->config_dir || !ctl->seafile_dir)
201         return -1;
202 
203     seaf_message ("starting seaf-server ...\n");
204     static char *logfile = NULL;
205     if (logfile == NULL) {
206         logfile = g_build_filename (ctl->logdir, "seafile.log", NULL);
207     }
208 
209     char *argv[] = {
210         "seaf-server",
211         "-F", ctl->central_config_dir,
212         "-c", ctl->config_dir,
213         "-d", ctl->seafile_dir,
214         "-l", logfile,
215         "-P", ctl->pidfile[PID_SERVER],
216         "-p", ctl->rpc_pipe_path,
217         NULL};
218 
219     int pid = spawn_process (argv, false);
220     if (pid <= 0) {
221         seaf_warning ("Failed to spawn seaf-server\n");
222         return -1;
223     }
224 
225     return 0;
226 }
227 
228 static const char *
get_python_executable()229 get_python_executable() {
230     static const char *python = NULL;
231     if (python != NULL) {
232         return python;
233     }
234 
235     static const char *try_list[] = {
236         "python3"
237     };
238 
239     int i;
240     for (i = 0; i < G_N_ELEMENTS(try_list); i++) {
241         char *binary = g_find_program_in_path (try_list[i]);
242         if (binary != NULL) {
243             python = binary;
244             break;
245         }
246     }
247 
248     if (python == NULL) {
249         python = g_getenv ("PYTHON");
250         if (python == NULL) {
251             python = "python";
252         }
253     }
254 
255     return python;
256 }
257 
258 static void
init_seafile_path()259 init_seafile_path ()
260 {
261     GError *error = NULL;
262 #if defined(__linux__)
263     char *binary = g_file_read_link (PROC_SELF_PATH, &error);
264 #elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__)
265     /*
266      * seafile.sh starts the process using abs path
267      */
268     char binary[_POSIX_PATH_MAX];
269     memset(binary, 0, _POSIX_PATH_MAX);
270     char * rc = realpath(command_name, binary);
271     if (!rc) {
272         seaf_warning ("failed to readpath: %s\n", binary);
273         return;
274     }
275 #endif
276     char *tmp = NULL;
277     if (error != NULL) {
278         seaf_warning ("failed to readlink: %s\n", error->message);
279         return;
280     }
281 
282     bin_dir = g_path_get_dirname (binary);
283 
284     tmp = g_path_get_dirname (bin_dir);
285     installpath = g_path_get_dirname (tmp);
286 
287     topdir = g_path_get_dirname (installpath);
288 
289 #if defined(__linux__)
290     g_free (binary);
291 #endif
292     g_free (tmp);
293 }
294 
295 static void
setup_python_path()296 setup_python_path()
297 {
298     static GList *path_list = NULL;
299     if (path_list != NULL) {
300         /* Only setup once */
301         return;
302     }
303 
304     /* Allow seafdav to access seahub_settings.py */
305     path_list = g_list_prepend (path_list, g_build_filename (topdir, "conf", NULL));
306 
307     path_list = g_list_prepend (path_list,
308         g_build_filename (installpath, "seahub", NULL));
309 
310     path_list = g_list_prepend (path_list,
311         g_build_filename (installpath, "seahub/thirdpart", NULL));
312 
313     path_list = g_list_prepend (path_list,
314         g_build_filename (installpath, "seahub/seahub-extra", NULL));
315 
316     path_list = g_list_prepend (path_list,
317         g_build_filename (installpath, "seahub/seahub-extra/thirdparts", NULL));
318 
319     path_list = g_list_prepend (path_list,
320         g_build_filename (installpath, "seafile/lib/python3.8/site-packages", NULL));
321 
322     path_list = g_list_prepend (path_list,
323         g_build_filename (installpath, "seafile/lib64/python3.8/site-packages", NULL));
324 
325     path_list = g_list_reverse (path_list);
326 
327     GList *ptr;
328     GString *new_pypath = g_string_new (g_getenv("PYTHONPATH"));
329 
330     for (ptr = path_list; ptr != NULL; ptr = ptr->next) {
331         const char *path = (char *)ptr->data;
332 
333         g_string_append_c (new_pypath, ':');
334         g_string_append (new_pypath, path);
335     }
336 
337     g_setenv ("PYTHONPATH", g_string_free (new_pypath, FALSE), TRUE);
338 
339     /* seaf_message ("PYTHONPATH is:\n\n%s\n", g_getenv ("PYTHONPATH")); */
340 }
341 
342 static void
setup_env()343 setup_env ()
344 {
345     g_setenv ("CCNET_CONF_DIR", ctl->config_dir, TRUE);
346     g_setenv ("SEAFILE_CONF_DIR", ctl->seafile_dir, TRUE);
347     g_setenv ("SEAFILE_CENTRAL_CONF_DIR", ctl->central_config_dir, TRUE);
348     g_setenv ("SEAFILE_RPC_PIPE_PATH", ctl->rpc_pipe_path, TRUE);
349 
350     char *seahub_dir = g_build_filename (installpath, "seahub", NULL);
351     char *seafdav_conf = g_build_filename (ctl->central_config_dir, "seafdav.conf", NULL);
352     g_setenv ("SEAHUB_DIR", seahub_dir, TRUE);
353     g_setenv ("SEAFDAV_CONF", seafdav_conf, TRUE);
354 
355     setup_python_path();
356 }
357 
358 static int
start_seafevents()359 start_seafevents() {
360     if (!ctl->has_seafevents)
361         return 0;
362 
363     static char *seafevents_config_file = NULL;
364     static char *seafevents_log_file = NULL;
365 
366     if (seafevents_config_file == NULL)
367         seafevents_config_file = g_build_filename (topdir,
368                                                   "conf/seafevents.conf",
369                                                    NULL);
370     if (seafevents_log_file == NULL)
371         seafevents_log_file = g_build_filename (ctl->logdir,
372                                                 "seafevents.log",
373                                                 NULL);
374 
375     char *argv[] = {
376         (char *)get_python_executable(),
377         "-m", "seafevents.main",
378         "--config-file", seafevents_config_file,
379         "--logfile", seafevents_log_file,
380         "-P", ctl->pidfile[PID_SEAFEVENTS],
381         NULL
382     };
383 
384     int pid = spawn_process (argv, true);
385 
386     if (pid <= 0) {
387         seaf_warning ("Failed to spawn seafevents.\n");
388         return -1;
389     }
390 
391     return 0;
392 }
393 
394 static int
start_seafdav()395 start_seafdav() {
396     static char *seafdav_log_file = NULL;
397     if (seafdav_log_file == NULL)
398         seafdav_log_file = g_build_filename (ctl->logdir,
399                                              "seafdav.log",
400                                              NULL);
401 
402     SeafDavConfig conf = ctl->seafdav_config;
403     char port[16];
404     snprintf (port, sizeof(port), "%d", conf.port);
405 
406     char *argv[] = {
407         (char *)get_python_executable(),
408         "-m", "wsgidav.server.server_cli",
409         "--server", "gunicorn",
410         "--root", "/",
411         "--log-file", seafdav_log_file,
412         "--pid", ctl->pidfile[PID_SEAFDAV],
413         "--port", port,
414         "--host", conf.host,
415         NULL
416     };
417 
418     int pid = spawn_process (argv, true);
419 
420     if (pid <= 0) {
421         seaf_warning ("Failed to spawn seafdav\n");
422         return -1;
423     }
424 
425     return 0;
426 }
427 
428 static void
run_controller_loop()429 run_controller_loop ()
430 {
431     GMainLoop *mainloop = g_main_loop_new (NULL, FALSE);
432 
433     g_main_loop_run (mainloop);
434 }
435 
436 static gboolean
need_restart(int which)437 need_restart (int which)
438 {
439     if (which < 0 || which >= N_PID)
440         return FALSE;
441 
442     int pid = read_pid_from_pidfile (ctl->pidfile[which]);
443     if (pid == PID_ERROR_ENOENT) {
444         seaf_warning ("pid file %s does not exist\n", ctl->pidfile[which]);
445         return TRUE;
446     } else if (pid == PID_ERROR_OTHER) {
447         seaf_warning ("failed to read pidfile %s: %s\n", ctl->pidfile[which], strerror(errno));
448         return FALSE;
449     } else {
450         char buf[256];
451 	gboolean with_procfs;
452 #if defined(__linux__)
453 	with_procfs = g_file_test("/proc/self", G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR);
454 #elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
455 	with_procfs = g_file_test("/proc/curproc", G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR);
456 #else
457 	with_procfs = FALSE;
458 #endif
459 	if (with_procfs) {
460         snprintf (buf, sizeof(buf), "/proc/%d", pid);
461         if (g_file_test (buf, G_FILE_TEST_IS_DIR)) {
462             return FALSE;
463         } else {
464             seaf_warning ("path /proc/%d doesn't exist, restart progress [%d]\n", pid, which);
465             return TRUE;
466 	}
467 
468 	} else {
469 #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__)
470 #ifdef __OpenBSD__
471             int min[6] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid, sizeof(struct kinfo_proc), 1};
472 #else
473             int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
474 #endif
475             size_t len = sizeof(struct kinfo_proc);
476             struct kinfo_proc kp;
477 	    if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &kp, &len, NULL, 0) != -1 &&
478 	      len == sizeof(struct kinfo_proc)) {
479 		return FALSE;
480             } else {
481                 return TRUE;
482             }
483 #else
484 	return FALSE;
485 #endif
486         }
487     }
488 }
489 
490 static gboolean
check_process(void * data)491 check_process (void *data)
492 {
493     if (need_restart(PID_SERVER)) {
494         seaf_message ("seaf-server need restart...\n");
495         start_seaf_server();
496     }
497 
498     if (ctl->seafdav_config.enabled) {
499         if (need_restart(PID_SEAFDAV)) {
500             seaf_message ("seafdav need restart...\n");
501             start_seafdav ();
502         }
503     }
504 
505     if (ctl->has_seafevents && need_restart(PID_SEAFEVENTS)) {
506         seaf_message ("seafevents need restart...\n");
507         start_seafevents ();
508     }
509 
510     return TRUE;
511 }
512 
513 static void
start_process_monitor()514 start_process_monitor ()
515 {
516     ctl->check_process_timer = g_timeout_add (
517         CHECK_PROCESS_INTERVAL * 1000, check_process, NULL);
518 }
519 
520 static int seaf_controller_start ();
521 /* This would also stop seaf-server & other components */
522 static void
stop_services()523 stop_services ()
524 {
525     seaf_message ("shutting down all services ...\n");
526 
527     kill_by_force(PID_SERVER);
528     kill_by_force(PID_SEAFDAV);
529     if (ctl->has_seafevents)
530         kill_by_force(PID_SEAFEVENTS);
531 }
532 
533 static void
init_pidfile_path(SeafileController * ctl)534 init_pidfile_path (SeafileController *ctl)
535 {
536     char *pid_dir = g_build_filename (topdir, "pids", NULL);
537     if (!g_file_test(pid_dir, G_FILE_TEST_EXISTS)) {
538         if (g_mkdir(pid_dir, 0777) < 0) {
539             seaf_warning("failed to create pid dir %s: %s", pid_dir, strerror(errno));
540             controller_exit(1);
541         }
542     }
543 
544     ctl->pidfile[PID_SERVER] = g_build_filename (pid_dir, "seaf-server.pid", NULL);
545     ctl->pidfile[PID_SEAFDAV] = g_build_filename (pid_dir, "seafdav.pid", NULL);
546     ctl->pidfile[PID_SEAFEVENTS] = g_build_filename (pid_dir, "seafevents.pid", NULL);
547 }
548 
549 static int
seaf_controller_init(SeafileController * ctl,char * central_config_dir,char * config_dir,char * seafile_dir,char * logdir)550 seaf_controller_init (SeafileController *ctl,
551                       char *central_config_dir,
552                       char *config_dir,
553                       char *seafile_dir,
554                       char *logdir)
555 {
556     init_seafile_path ();
557     if (!g_file_test (config_dir, G_FILE_TEST_IS_DIR)) {
558         seaf_warning ("invalid config_dir: %s\n", config_dir);
559         return -1;
560     }
561 
562     if (!g_file_test (seafile_dir, G_FILE_TEST_IS_DIR)) {
563         seaf_warning ("invalid seafile_dir: %s\n", seafile_dir);
564         return -1;
565     }
566 
567     if (logdir == NULL) {
568         char *topdir = g_path_get_dirname(config_dir);
569         logdir = g_build_filename (topdir, "logs", NULL);
570         if (checkdir_with_mkdir(logdir) < 0) {
571             fprintf (stderr, "failed to create log folder \"%s\": %s\n",
572                      logdir, strerror(errno));
573             return -1;
574         }
575         g_free (topdir);
576     }
577 
578     ctl->central_config_dir = central_config_dir;
579     ctl->config_dir = config_dir;
580     ctl->seafile_dir = seafile_dir;
581     ctl->rpc_pipe_path = g_build_filename (installpath, "runtime", NULL);
582     ctl->logdir = logdir;
583 
584     if (read_seafdav_config() < 0) {
585         return -1;
586     }
587 
588     char *seafevents_config_file = g_build_filename (topdir,
589                                                      "conf/seafevents.conf",
590                                                      NULL);
591 
592     if (!g_file_test (seafevents_config_file, G_FILE_TEST_EXISTS)) {
593         seaf_message ("No seafevents.\n");
594         ctl->has_seafevents = FALSE;
595     } else {
596         ctl->has_seafevents = TRUE;
597     }
598     g_free (seafevents_config_file);
599 
600     init_pidfile_path (ctl);
601     setup_env ();
602 
603     return 0;
604 }
605 
606 static int
seaf_controller_start()607 seaf_controller_start ()
608 {
609     if (start_seaf_server() < 0) {
610         seaf_warning ("Failed to start seaf server\n");
611         return -1;
612     }
613 
614     start_process_monitor ();
615     return 0;
616 }
617 
618 static int
write_controller_pidfile()619 write_controller_pidfile ()
620 {
621     if (!controller_pidfile)
622         return -1;
623 
624     pid_t pid = getpid();
625 
626     FILE *pidfile = g_fopen(controller_pidfile, "w");
627     if (!pidfile) {
628         seaf_warning ("Failed to fopen() pidfile %s: %s\n",
629                       controller_pidfile, strerror(errno));
630         return -1;
631     }
632 
633     char buf[32];
634     snprintf (buf, sizeof(buf), "%d\n", pid);
635     if (fputs(buf, pidfile) < 0) {
636         seaf_warning ("Failed to write pidfile %s: %s\n",
637                       controller_pidfile, strerror(errno));
638         fclose (pidfile);
639         return -1;
640     }
641 
642     fflush (pidfile);
643     fclose (pidfile);
644     return 0;
645 }
646 
647 static void
remove_controller_pidfile()648 remove_controller_pidfile ()
649 {
650     if (controller_pidfile) {
651         g_unlink (controller_pidfile);
652     }
653 }
654 
655 static void
sigint_handler(int signo)656 sigint_handler (int signo)
657 {
658     stop_services ();
659 
660     remove_controller_pidfile();
661 
662     signal (signo, SIG_DFL);
663     raise (signo);
664 }
665 
666 static void
sigchld_handler(int signo)667 sigchld_handler (int signo)
668 {
669     waitpid (-1, NULL, WNOHANG);
670 }
671 
672 static void
sigusr1_handler(int signo)673 sigusr1_handler (int signo)
674 {
675     seafile_log_reopen();
676 }
677 
678 static void
set_signal_handlers()679 set_signal_handlers ()
680 {
681     signal (SIGINT, sigint_handler);
682     signal (SIGTERM, sigint_handler);
683     signal (SIGCHLD, sigchld_handler);
684     signal (SIGUSR1, sigusr1_handler);
685     signal (SIGPIPE, SIG_IGN);
686 }
687 
688 static void
usage()689 usage ()
690 {
691     fprintf (stderr, "Usage: seafile-controller OPTIONS\n"
692              "OPTIONS:\n"
693              "  -b, --bin-dir           insert a directory in front of the PATH env\n"
694              "  -c, --config-dir        ccnet config dir\n"
695              "  -d, --seafile-dir       seafile dir\n"
696         );
697 }
698 
699 /* seafile-controller -t is used to test whether config file is valid */
700 static void
test_config(const char * central_config_dir,const char * ccnet_dir,const char * seafile_dir)701 test_config (const char *central_config_dir,
702              const char *ccnet_dir,
703              const char *seafile_dir)
704 {
705     char buf[1024];
706     GError *error = NULL;
707     int retcode = 0;
708     char *child_stdout = NULL;
709     char *child_stderr = NULL;
710 
711     snprintf (buf,
712           sizeof(buf),
713           "seaf-server -F \"%s\" -c \"%s\" -d \"%s\" -t -f",
714           central_config_dir,
715           ccnet_dir,
716           seafile_dir);
717 
718     g_spawn_command_line_sync (buf,
719                                &child_stdout,
720                                &child_stderr,
721                                &retcode,
722                                &error);
723 
724     if (error != NULL) {
725         fprintf (stderr,
726                  "failed to run \"seaf-server -t\": %s\n",
727                  error->message);
728         exit (1);
729     }
730 
731     if (child_stdout) {
732         fputs (child_stdout, stdout);
733     }
734 
735     if (child_stderr) {
736         fputs (child_stderr, stdout);
737     }
738 
739     if (retcode != 0) {
740         fprintf (stderr,
741                  "failed to run \"seaf-server -t\" [%d]\n", retcode);
742         exit (1);
743     }
744 
745     exit(0);
746 }
747 
748 static int
read_seafdav_config()749 read_seafdav_config()
750 {
751     int ret = 0;
752     char *seafdav_conf = NULL;
753     GKeyFile *key_file = NULL;
754     GError *error = NULL;
755 
756     seafdav_conf = g_build_filename(ctl->central_config_dir, "seafdav.conf", NULL);
757     if (!g_file_test(seafdav_conf, G_FILE_TEST_EXISTS)) {
758         goto out;
759     }
760 
761     key_file = g_key_file_new ();
762     if (!g_key_file_load_from_file (key_file, seafdav_conf,
763                                     G_KEY_FILE_KEEP_COMMENTS, NULL)) {
764         seaf_warning("Failed to load seafdav.conf\n");
765         ret = -1;
766         goto out;
767     }
768 
769     /* enabled */
770     ctl->seafdav_config.enabled = g_key_file_get_boolean(key_file, "WEBDAV", "enabled", &error);
771     if (error != NULL) {
772         if (error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
773             seaf_message ("Error when reading WEBDAV.enabled, use default value 'false'\n");
774         }
775         ctl->seafdav_config.enabled = FALSE;
776         g_clear_error (&error);
777         goto out;
778     }
779 
780     if (!ctl->seafdav_config.enabled) {
781         goto out;
782     }
783 
784     /* host */
785     char *host = seaf_key_file_get_string (key_file, "WEBDAV", "host", &error);
786     if (error != NULL) {
787         g_clear_error(&error);
788         ctl->seafdav_config.host = g_strdup("0.0.0.0");
789     } else {
790         ctl->seafdav_config.host = host;
791     }
792 
793     /* port */
794     ctl->seafdav_config.port = g_key_file_get_integer(key_file, "WEBDAV", "port", &error);
795     if (error != NULL) {
796         if (error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
797             seaf_message ("Error when reading WEBDAV.port, use deafult value 8080\n");
798         }
799         ctl->seafdav_config.port = 8080;
800         g_clear_error (&error);
801     }
802 
803     if (ctl->seafdav_config.port <= 0 || ctl->seafdav_config.port > 65535) {
804         seaf_warning("Failed to load seafdav config: invalid port %d\n", ctl->seafdav_config.port);
805         ret = -1;
806         goto out;
807     }
808 
809 out:
810     if (key_file) {
811         g_key_file_free (key_file);
812     }
813     g_free (seafdav_conf);
814 
815     return ret;
816 }
817 
818 static int
init_syslog_config()819 init_syslog_config ()
820 {
821     char *seafile_conf = g_build_filename (ctl->central_config_dir, "seafile.conf", NULL);
822     GKeyFile *key_file = g_key_file_new ();
823     int ret = 0;
824 
825     if (!g_key_file_load_from_file (key_file, seafile_conf,
826                                     G_KEY_FILE_KEEP_COMMENTS, NULL)) {
827         seaf_warning("Failed to load seafile.conf.\n");
828         ret = -1;
829         goto out;
830     }
831 
832     set_syslog_config (key_file);
833 
834 out:
835     g_key_file_free (key_file);
836     g_free (seafile_conf);
837 
838     return ret;
839 }
840 
main(int argc,char ** argv)841 int main (int argc, char **argv)
842 {
843     if (argc <= 1) {
844         usage ();
845         exit (1);
846     }
847 
848 #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__)
849     command_name = argv[0];
850 #endif
851     char *config_dir = DEFAULT_CONFIG_DIR;
852     char *central_config_dir = NULL;
853     char *seafile_dir = NULL;
854     char *logdir = NULL;
855     char *ccnet_debug_level_str = "info";
856     char *seafile_debug_level_str = "debug";
857     int daemon_mode = 1;
858     gboolean test_conf = FALSE;
859 
860     int c;
861     while ((c = getopt_long (argc, argv, short_opts,
862                              long_opts, NULL)) != EOF)
863     {
864         switch (c) {
865         case 'h':
866             usage ();
867             exit(1);
868             break;
869         case 'v':
870             fprintf (stderr, "seafile-controller version 1.0\n");
871             break;
872         case 't':
873             test_conf = TRUE;
874             break;
875         case 'c':
876             config_dir = optarg;
877             break;
878         case 'F':
879             central_config_dir = g_strdup(optarg);
880             break;
881         case 'd':
882             seafile_dir = g_strdup(optarg);
883             break;
884         case 'f':
885             daemon_mode = 0;
886             break;
887         case 'l':
888             logdir = g_strdup(optarg);
889             break;
890         case 'g':
891             ccnet_debug_level_str = optarg;
892             break;
893         case 'G':
894             seafile_debug_level_str = optarg;
895             break;
896         case 'P':
897             controller_pidfile = optarg;
898             break;
899         default:
900             usage ();
901             exit (1);
902         }
903     }
904 
905 #if !GLIB_CHECK_VERSION(2, 35, 0)
906     g_type_init();
907 #endif
908 #if !GLIB_CHECK_VERSION(2,32,0)
909     g_thread_init (NULL);
910 #endif
911 
912     if (!seafile_dir) {
913         fprintf (stderr, "<seafile_dir> must be specified with --seafile-dir\n");
914         exit(1);
915     }
916 
917     if (!central_config_dir) {
918         fprintf (stderr, "<central_config_dir> must be specified with --central-config-dir\n");
919         exit(1);
920     }
921 
922     central_config_dir = ccnet_expand_path (central_config_dir);
923     config_dir = ccnet_expand_path (config_dir);
924     seafile_dir = ccnet_expand_path (seafile_dir);
925 
926     if (test_conf) {
927         test_config (central_config_dir, config_dir, seafile_dir);
928     }
929 
930     ctl = g_new0 (SeafileController, 1);
931     if (seaf_controller_init (ctl, central_config_dir, config_dir, seafile_dir, logdir) < 0) {
932         controller_exit(1);
933     }
934 
935     char *logfile = g_build_filename (ctl->logdir, "controller.log", NULL);
936     if (seafile_log_init (logfile, ccnet_debug_level_str,
937                           seafile_debug_level_str) < 0) {
938         seaf_warning ("Failed to init log.\n");
939         controller_exit (1);
940     }
941 
942     if (init_syslog_config () < 0) {
943         controller_exit (1);
944     }
945 
946     set_signal_handlers ();
947 
948     if (seaf_controller_start () < 0)
949         controller_exit (1);
950 
951 #ifndef WIN32
952     if (daemon_mode) {
953 #ifndef __APPLE__
954         daemon (1, 0);
955 #else   /* __APPLE */
956         /* daemon is deprecated under APPLE
957          * use fork() instead
958          * */
959         switch (fork ()) {
960           case -1:
961               seaf_warning ("Failed to daemonize");
962               exit (-1);
963               break;
964           case 0:
965               /* all good*/
966               break;
967           default:
968               /* kill origin process */
969               exit (0);
970         }
971 #endif  /* __APPLE */
972     }
973 #endif /* !WIN32 */
974 
975     if (controller_pidfile == NULL) {
976         controller_pidfile = g_strdup(g_getenv ("SEAFILE_PIDFILE"));
977     }
978 
979     if (controller_pidfile != NULL) {
980         if (write_controller_pidfile () < 0) {
981             seaf_warning ("Failed to write pidfile %s\n", controller_pidfile);
982             return -1;
983         }
984     }
985 
986     run_controller_loop ();
987 
988     return 0;
989 }
990 
991