root/trunk/src/main.c

Revision 686, 15.8 kB (checked in by jajcus, 3 years ago)

- include timestamps in the debuging messages, log level cleanup (fixes #3) [jaak]

Line 
1 /* $Id: main.c,v 1.57 2004/04/13 17:44:07 jajcus Exp $ */
2
3 /*
4  *  (C) Copyright 2002-2006 Jacek Konieczny [jajcus(a)jajcus,net]
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License Version 2 as
8  *  published by the Free Software Foundation.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19
20 #include "ggtrans.h"
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <signal.h>
24 #include <sys/wait.h>
25 #include <sys/types.h>
26 #include <limits.h>
27 #include <errno.h>
28 #include <pwd.h>
29 #include <grp.h>
30 #include <ctype.h>
31 #include <time.h>
32 #include <locale.h>
33 #include <time.h>
34
35 #ifdef HAVE_LANGINFO_CODESET
36 include <langinfo.h>
37 #endif
38
39 #include "jabber.h"
40 #include "sessions.h"
41 #include "encoding.h"
42 #include "requests.h"
43 #include "conf.h"
44 #include "acl.h"
45 #include "debug.h"
46
47 #ifndef OPEN_MAX
48 #define OPEN_MAX 255
49 #endif
50
51 GMainLoop *main_loop;
52 static GSource *signal_source;
53 static int signal_received=FALSE;
54 static gboolean the_end=FALSE;
55 gboolean do_restart=FALSE;
56 static int restart_timeout=60;
57 static gboolean foreground=TRUE;
58 static int debug_level=0;
59
60 static FILE *log_file=NULL;
61 static gboolean use_syslog=FALSE;
62 static char *pid_filename=NULL;
63 static GAllocator* list_allocator;
64
65 GList *admins=NULL;
66 time_t start_time=0;
67 unsigned long packets_in=0;
68 unsigned long packets_out=0;
69 unsigned long gg_messages_in=0;
70 unsigned long gg_messages_out=0;
71
72 static struct {
73         const char *name;
74         int code;
75 }facilitynames[] =
76   {
77     { "auth", LOG_AUTH },
78 #ifdef LOG_AUTHPRIV
79     { "authpriv", LOG_AUTHPRIV },
80 #endif
81     { "cron", LOG_CRON },
82     { "daemon", LOG_DAEMON },
83 #ifdef LOG_FTP
84     { "ftp", LOG_FTP },
85 #endif
86     { "kern", LOG_KERN },
87     { "lpr", LOG_LPR },
88     { "mail", LOG_MAIL },
89     { "news", LOG_NEWS },
90     { "syslog", LOG_SYSLOG },
91     { "user", LOG_USER },
92     { "uucp", LOG_UUCP },
93     { "local0", LOG_LOCAL0 },
94     { "local1", LOG_LOCAL1 },
95     { "local2", LOG_LOCAL2 },
96     { "local3", LOG_LOCAL3 },
97     { "local4", LOG_LOCAL4 },
98     { "local5", LOG_LOCAL5 },
99     { "local6", LOG_LOCAL6 },
100     { "local7", LOG_LOCAL7 },
101     { NULL, -1 }
102   };
103
104
105 void sigchld_handler (int signum){
106 int pid, status, serrno;
107         serrno = errno;
108         while (1){
109                    pid = waitpid (WAIT_ANY, &status, WNOHANG);
110                    if (pid<=0)
111                      break;
112         }
113         errno = serrno;
114 }
115
116
117 void signal_handler(int sig){
118
119         switch(sig){
120                 case SIGHUP:
121                         restart_timeout=0;
122                         do_restart=TRUE;
123                 case SIGINT:
124                 case SIGTERM:
125                         the_end=TRUE;
126                         break;
127                 case SIGPIPE:
128                         break;
129                 default:
130                         break;
131         }
132         signal(sig,signal_handler);
133         signal_received=sig;
134 }
135
136 gboolean signal_source_prepare(GSource *source,
137                                 gint     *timeout){
138
139         *timeout=1000;
140         if (signal_received) return TRUE;
141         return FALSE;
142 }
143
144 gboolean signal_source_check(GSource *source){
145
146         if (signal_received) return TRUE;
147         return FALSE;
148 }
149
150 gboolean signal_source_dispatch(GSource *source,
151                         GSourceFunc callback,
152                         gpointer  user_data){
153
154         psignal(signal_received,"signal received");
155         g_warning("Signal received: %s",g_strsignal(signal_received));
156         if (the_end) g_main_quit(main_loop);
157         if (signal_received==SIGUSR1){
158                 g_message("Active sessions:");
159                 sessions_print_all(1);
160         }
161         signal_received=0;
162         return TRUE;
163 }
164
165 void signal_source_finalize(GSource *source){
166 }
167
168 static GSourceFuncs signal_source_funcs={
169                 signal_source_prepare,
170                 signal_source_check,
171                 signal_source_dispatch,
172                 signal_source_finalize,
173                 NULL,
174                 NULL
175                 };
176
177 void log_handler_file(FILE *f,const gchar *log_domain, GLogLevelFlags log_level,
178                         const gchar *message){
179 struct tm localTime;
180 time_t now = time(0);
181
182         localtime_r(&now, &localTime);
183         fprintf(f,"%04d-%02d-%02d %02d:%02d:%02d ",
184                 localTime.tm_year + 1900,
185                 localTime.tm_mon + 1,
186                 localTime.tm_mday,
187                 localTime.tm_hour,
188                 localTime.tm_min,
189                 localTime.tm_sec
190                 );
191         if (log_domain && log_domain[0]) fprintf(f,"%s: ",log_domain);
192         switch(log_level){
193                 case G_LOG_LEVEL_ERROR:
194                         fprintf(f,_("Fatal error: %s\n"),message);
195                         break;
196                 case G_LOG_LEVEL_CRITICAL:
197                         fprintf(f,_("Error: %s\n"),message);
198                         break;
199                 case G_LOG_LEVEL_WARNING:
200                         fprintf(f,_("Warning: %s\n"),message);
201                         break;
202                 case G_LOG_LEVEL_MESSAGE:
203                 case G_LOG_LEVEL_INFO:
204                         fprintf(f,"%s\n",message);
205                         break;
206                 case G_LOG_LEVEL_DEBUG:
207                         fprintf(f,_("Debug: %s\n"),message);
208                         break;
209                 default:
210                         fprintf(f,_("Unknown: %s\n"),message);
211                         break;
212         }
213 }
214
215 void log_handler_syslog(const gchar *log_domain, GLogLevelFlags log_level,
216                         const gchar *message){
217
218         switch(log_level){
219                 case G_LOG_LEVEL_ERROR:
220                         syslog(LOG_ERR,_("Fatal error: %s"),message);
221                         break;
222                 case G_LOG_LEVEL_CRITICAL:
223                         syslog(LOG_ERR,_("Error: %s"),message);
224                         break;
225                 case G_LOG_LEVEL_WARNING:
226                         syslog(LOG_WARNING,_("Warning: %s"),message);
227                         break;
228                 case G_LOG_LEVEL_MESSAGE:
229                         syslog(LOG_NOTICE,"%s",message);
230                         break;
231                 case G_LOG_LEVEL_INFO:
232                         syslog(LOG_NOTICE,"%s",message);
233                         break;
234                 case G_LOG_LEVEL_DEBUG:
235                         syslog(LOG_DEBUG,_("Debug: %s\n"),message);
236                         break;
237                 default:
238                         syslog(LOG_NOTICE,_("Unknown: %s\n"),message);
239                         break;
240         }
241 }
242
243 #ifdef ENABLE_NLS
244 const char *local_translate(const char *str){
245 char *lc_ctype,*lc_messages,*td_codeset,*ret;
246
247         td_codeset=g_strdup(bind_textdomain_codeset(PACKAGE,NULL));
248         lc_ctype=g_strdup(setlocale(LC_CTYPE,NULL));
249         lc_messages=g_strdup(setlocale(LC_MESSAGES,NULL));
250         setlocale(LC_MESSAGES,"");
251         setlocale(LC_CTYPE,"");
252 #ifdef HAVE_LANGINFO_CODESET
253         bind_textdomain_codeset(PACKAGE,nl_langinfo(CODESET));
254 #endif
255 /*      textdomain(PACKAGE);*/
256
257         ret=gettext(str);
258
259         setlocale(LC_CTYPE,lc_ctype);
260         setlocale(LC_MESSAGES,lc_ctype);
261         bind_textdomain_codeset(PACKAGE,td_codeset);
262         g_free(lc_ctype);
263         g_free(lc_messages);
264         g_free(td_codeset);
265
266         return ret;
267 }
268 #endif
269
270 void log_handler(const gchar *log_domain, GLogLevelFlags log_level,
271                         const gchar *message, gpointer user_data){
272 #ifdef ENABLE_NLS
273 char *lc_ctype,*lc_messages,*td_codeset;
274 #endif
275
276         switch(log_level){
277                 case G_LOG_LEVEL_MESSAGE:
278                         if (debug_level<-1) return;
279                         break;
280                 case G_LOG_LEVEL_INFO:
281                         if (debug_level<0) return;
282                         break;
283                 case G_LOG_LEVEL_DEBUG:
284                         if (debug_level<=0) return;
285                         break;
286         }
287
288 #ifdef ENABLE_NLS
289         td_codeset=g_strdup(bind_textdomain_codeset(PACKAGE,NULL));
290         lc_ctype=g_strdup(setlocale(LC_CTYPE,NULL));
291         lc_messages=g_strdup(setlocale(LC_MESSAGES,NULL));
292         setlocale(LC_MESSAGES,"");
293         setlocale(LC_CTYPE,"");
294 ifdef HAVE_LANGINFO_CODESET
295         bind_textdomain_codeset(PACKAGE,nl_langinfo(CODESET));
296 endif
297 /*      textdomain(PACKAGE);*/
298 #endif
299
300         log_level&=G_LOG_LEVEL_MASK;
301
302         if (foreground) log_handler_file(stderr,log_domain,log_level,message);
303         if (log_file) log_handler_file(log_file,log_domain,log_level,message);
304         if (use_syslog) log_handler_syslog(log_domain,log_level,message);
305
306 #ifdef ENABLE_NLS
307         setlocale(LC_CTYPE,lc_ctype);
308         setlocale(LC_MESSAGES,lc_messages);
309         bind_textdomain_codeset(PACKAGE,td_codeset);
310         g_free(lc_ctype);
311         g_free(lc_messages);
312         g_free(td_codeset);
313 #endif
314 }
315
316 void daemonize(FILE *pidfile){
317 pid_t pid;
318 pid_t sid;
319 int fd;
320
321         debug(L_("Daemonizing..."));
322         pid=fork();
323         if (pid==-1) g_error(L_("Failed to fork(): %s"),g_strerror(errno));
324         if (pid){
325                 if (pidfile){
326                         fprintf(pidfile,"%u",pid);
327                         fclose(pidfile);
328                 }
329                 debug(L_("Daemon born, pid %i."),pid);
330                 exit(0);
331         }
332
333         for (fd=0; fd < OPEN_MAX; fd++)
334                 close(fd);
335
336         fd = open("/dev/null", O_RDWR);
337         if (fd){
338                 if (fd != 0)
339                         dup2(fd, 0);
340                 if (fd != 1)
341                         dup2(fd, 1);
342                 if (fd != 2)
343                         dup2(fd, 2);
344                 if (fd > 2)
345                         close(fd);
346         }
347
348         sid=setsid();
349         if (sid==-1) abort();
350         foreground=FALSE;
351         debug(L_("I am a daemon, I think."));
352         return;
353 }
354
355 void usage(const char *name){
356 char *p;
357
358         p=strrchr(name,'/');
359         if (p) name=p+1;
360         printf(_("\nJabber GaduGadu Transport %s\n"),VERSION);
361         printf("\n");
362         printf(_("\tUsage: %s [OPTIONS]... [<config file>]\n"),name);
363         printf(_("\nOptions:\n"));
364         printf(_("\t-h        This message\n"));
365         printf(_("\t-f        Run in foreground. Debug/error messages will be sent to stderr\n"));
366         printf(_("\t-d <n>      Log level (0(default) - normal, >0 more, <0 less)\n"));
367         printf(_("\t-D <n>      libgg debug level (enables also -f)\n"));
368         printf(_("\t-u <user>   Switch to uid of <user> on startup\n"));
369         printf(_("\t-g <group>  Switch to gid of <group> on startup\n"));
370         printf("\n");
371 }
372
373 int main(int argc,char *argv[]){
374 int c;
375 gboolean fg=FALSE;
376 xmlnode tag;
377 char *log_type=NULL;
378 char *log_filename=NULL;
379 char *str;
380 char *config_file;
381 int log_facility=-1;
382 uid_t uid,euid,newgid;
383 struct passwd *pwd;
384 struct group *grp;
385 char *user,*group;
386 char *data;
387 char saved_pwd_b[1024],*saved_pwd;
388 const char *param_d=NULL,*param_D=NULL;
389 int restarting=0;
390 FILE *pidfile;
391 guint lh;
392 int i;
393
394         uid=getuid();
395         euid=geteuid();
396         if (euid==0 && uid!=euid){
397                 fprintf(stderr,"Refusing to work setuid-root!\n");
398                 exit(1);
399         }
400         newgid=0; user=NULL; group=NULL;
401
402         /* use local locale for error and debug messages */
403         setlocale(LC_MESSAGES,"");
404         setlocale(LC_CTYPE,"");
405         bindtextdomain(PACKAGE,LOCALEDIR);
406         textdomain(PACKAGE);
407
408         saved_pwd=getcwd(saved_pwd_b,1024);
409         opterr=0;
410         while ((c = getopt (argc, argv, "Rhfd:D:u:g:")) != -1){
411                 switch(c){
412                         case 'R':
413                                 restarting=1;
414                                 break;
415                         case 'h':
416                                 usage(argv[0]);
417                                 return 0;
418                         case 'f':
419                                 fg=TRUE;
420                                 break;
421                         case 'd':
422                                 param_d=optarg;
423                                 debug_level=atoi(optarg);
424                                 break;
425                         case 'D':
426                                 param_D=optarg;
427                                 gg_debug_level=atoi(optarg);
428                                 fg=TRUE;
429                                 break;
430                         case 'u':
431                                 if (uid!=0) g_error(_("Cannot change user."));
432                                 user=optarg;
433                                 break;
434                         case 'g':
435                                 if (uid!=0) g_error(_("Cannot change group."));
436                                 group=optarg;
437                                 break;
438                         case '?':
439                                 if (isprint(optopt))
440                                         fprintf(stderr,_("Unknown command-line option: -%c.\n"),optopt);
441                                 else
442                                         fprintf(stderr,_("Unknown command-line option: -\\%03o.\n"),optopt);
443                                 usage(argv[0]);
444                                 return 1;
445                         default:
446                                 g_error(_("Error while processing command line options"));
447                                 break;
448                 }
449         }
450
451         if (optind<argc-1){
452                 fprintf(stderr,_("Unexpected argument: %s\n"),argv[optind]);
453                 usage(argv[0]);
454                 return 1;
455         }
456
457         if (optind==argc-1) config_file=g_strdup(argv[optind]);
458         else config_file=g_strdup_printf("%s/%s",SYSCONFDIR,"jggtrans.xml");
459
460         /* own allocator will be usefull for mem-leak tracing */
461         list_allocator=g_allocator_new("la",128);
462         g_list_push_allocator(list_allocator);
463
464         lh=g_log_set_handler(NULL,G_LOG_FLAG_FATAL | G_LOG_LEVEL_ERROR
465                                 | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING
466                                 | G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO
467                                 | G_LOG_LEVEL_DEBUG,log_handler,NULL);
468
469 #ifdef ENABLE_NLS
470         /* now the log handlers worry about the right language */
471         setlocale(LC_MESSAGES,"C");
472         setlocale(LC_CTYPE,"C");
473         bind_textdomain_codeset(PACKAGE,"UTF-8");
474 #endif
475
476         config=xmlnode_file(config_file);
477         if (!config){
478                 g_error(L_("Couldn't load config!"));
479                 return 1;
480         }
481         str=xmlnode_get_name(config);
482         if (!str || strcmp(str,"jggtrans")){
483                 g_error(L_("%s doesn't look like jggtrans config file."),config_file);
484                 return 1;
485         }
486         g_free(config_file);
487
488         for(tag=xmlnode_get_firstchild(config);tag;tag=xmlnode_get_nextsibling(tag)){
489                 str=xmlnode_get_name(tag);
490                 if (!str) continue;
491                 if (!strcmp(str,"admin")){
492                         data=xmlnode_get_data(tag);
493                         admins=g_list_append(admins,data);
494                 }
495                 if (strcmp(str,"log")) continue;
496                 log_type=xmlnode_get_attrib(tag,"type");
497                 if (!strcmp(log_type,"syslog")){
498                         if (log_facility!=-1){
499                                 g_warning(N_("Multiple syslog configs specified. Using only one."));
500                                 continue;
501                         }
502                         str=xmlnode_get_attrib(tag,"facility");
503                         if (!str){
504                                 log_facility=LOG_USER;
505                                 continue;
506                         }
507                         for(log_facility=0;facilitynames[log_facility].name;log_facility++)
508                                 if (!strcmp(facilitynames[log_facility].name,str)) break;
509                         if (!facilitynames[log_facility].name)
510                                  g_error(L_("Unknown syslog facility: %s"),str);
511                 }
512                 else if (!strcmp(log_type,"file")){
513                         if (log_filename) g_warning(N_("Multiple log files specified. Using only one."));
514                         else{
515                                 data=xmlnode_get_data(tag);
516                                 if (data!=NULL)
517                                         log_filename=g_strstrip(data);
518                         }
519                 }
520                 else g_warning(N_("Ignoring unknown log type: %s"),xmlnode2str(tag));
521         }
522
523         pid_filename=config_load_string("pidfile");
524
525         restart_timeout=config_load_int("restart_timeout",restart_timeout);
526
527         if (pid_filename && !restarting){
528                 if (pid_filename) pidfile=fopen(pid_filename,"r");
529                 else pidfile=NULL;
530                 if (pidfile){
531                         pid_t pid;
532                         int r;
533
534                         r=fscanf(pidfile,"%u",&pid);
535                         fclose(pidfile);
536                         if (r==1 && pid>0){
537                                 r=kill(pid,0);
538                                 if (!r || (r && errno!=ESRCH)) g_error(L_("jggtrans already running"));
539                                 if (r){
540                                         g_warning(N_("Stale pid file. Removing."));
541                                         unlink(pid_filename);
542                                 }
543                         }
544                         else if (r!=EOF) g_error(L_("Invalid pid file."));
545                 }
546                 if (pid_filename) pidfile=fopen(pid_filename,"w");
547                 if (pidfile==NULL)
548                         g_error(L_("Couldn't open pidfile %s"),pid_filename);
549         }
550         else
551                 pidfile=NULL;
552
553         if (group){
554                 grp=getgrnam(group);
555                 if (!grp) g_error(L_("Couldn't find group %s"),group);
556                 newgid=grp->gr_gid;
557         }
558         if (user){
559                 pwd=getpwnam(user);
560                 if (!pwd) g_error(L_("Couldn't find user %s"),user);
561                 if (newgid<=0) newgid=pwd->pw_gid;
562                 if (pidfile) fchown(fileno(pidfile),pwd->pw_uid,newgid);
563                 if (setgid(newgid)) g_error(L_("Couldn't change group: %s"),g_strerror(errno));
564                 if (initgroups(user,newgid)) g_error(L_("Couldn't init groups: %s"),g_strerror(errno));
565                 if (setuid(pwd->pw_uid)) g_error(L_("Couldn't change user: %s"),g_strerror(errno));
566         }
567         else if (uid==0 && !restarting) g_error(L_("Refusing to run with uid=0"));
568
569         main_loop=g_main_new(0);
570
571         if (jabber_init()) return 1;
572         if (sessions_init()) return 1;
573         if (users_init()) return 1;
574         if (encoding_init()) return 1;
575         if (requests_init()) return 1;
576         if (acl_init()) return 1;
577
578         if (!fg && !restarting) daemonize(pidfile);
579         else if (pidfile!=NULL){
580                 fprintf(pidfile,"%i",getpid());
581                 fclose(pidfile);
582         }
583
584         if (log_filename){
585                 log_file=fopen(log_filename,"a");
586                 if (!log_file) g_critical(L_("Couldn't open log file '%s': %s"),
587                                                 log_filename,g_strerror(errno));
588                 if (log_file) setvbuf(log_file,NULL,_IOLBF,0);
589         }
590
591         if (log_facility!=-1){
592                 openlog("jggtrans",0,log_facility);
593                 use_syslog=1;
594         }
595
596         if (jabber_connect()) return 1;
597
598         signal_source=g_source_new(&signal_source_funcs,sizeof(GSource));
599         g_source_attach(signal_source,g_main_loop_get_context(main_loop));
600
601         signal(SIGPIPE,signal_handler);
602         signal(SIGHUP,signal_handler);
603         signal(SIGINT,signal_handler);
604         signal(SIGTERM,signal_handler);
605         signal(SIGUSR1,signal_handler);
606         signal(SIGCHLD,sigchld_handler);
607
608         start_time=time(NULL);
609         debug("starting the main loop...");
610         g_main_run(main_loop);
611         debug("g_main_run() finished.");
612
613         sessions_done();
614         users_done();
615         requests_done();
616
617         signal_received=0;
618         /* process pending events - write anything not written yet */
619         if (g_main_pending())
620                 for(i=0;i<100;i++){
621                         debug("flushing events...");
622                         if (!g_main_iteration(1)) break;
623                 }
624
625         jabber_done();
626         encoding_done();
627         acl_done();
628
629         g_source_destroy(signal_source);
630         g_main_destroy(main_loop);
631
632         if (do_restart && restart_timeout>=0){
633                 char *newargv[10];
634                 int n;
635
636                 g_message(L_("Restarting in %i seconds.\n"),restart_timeout);
637                 if (restart_timeout>0) sleep(restart_timeout);
638                 if (saved_pwd) chdir(saved_pwd);
639
640                 n=0;
641                 newargv[n++]=argv[0];
642                 newargv[n++]="-R";
643                 if (param_d){
644                         newargv[n++]="-d";
645                         newargv[n++]=(char *)param_d;
646                 }
647                 if (param_D){
648                         newargv[n++]="-D";
649                         newargv[n++]=(char *)param_D;
650                 }
651                 newargv[n]=NULL;
652                 if (!the_end){
653                         execvp(argv[0],newargv);
654                         perror("exec");
655                         return 1;
656                 }
657         }
658
659         g_message(L_("Exiting normally.\n"));
660
661         g_log_remove_handler(NULL,lh);
662
663         g_list_pop_allocator();
664         g_allocator_free(list_allocator);
665
666         if (log_file!=NULL){
667                 fclose(log_file);
668                 log_file=NULL;
669         }
670
671         if (pid_filename){
672                 if (unlink(pid_filename)!=0){
673                         pidfile=fopen(pid_filename,"w");
674                         if (pidfile) fclose(pidfile);
675                 }
676         }
677         xmlnode_free(config);
678
679         return 0;
680 }
681
Note: See TracBrowser for help on using the browser.