source: unix.c @ c775a58

Last change on this file since c775a58 was 3fc6c32, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-10-16T22:58:21Z

Stop calling nogaim_init() on every incoming connection (unless we're
using libpurple). This was leaking memory and indirectly caused the bug
fixed by the previous revision.

  • Property mode set to 100644
File size: 9.4 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2004 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* Main file (Unix specific part)                                       */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23  Suite 330, Boston, MA  02111-1307  USA
24*/
25
26#include "bitlbee.h"
27
28#include "arc.h"
29#include "base64.h"
30#include "commands.h"
31#include "protocols/nogaim.h"
32#include "help.h"
33#include "ipc.h"
34#include "lib/ssl_client.h"
35#include "md5.h"
36#include "misc.h"
37#include <signal.h>
38#include <unistd.h>
39#include <sys/time.h>
40#include <sys/wait.h>
41#include <pwd.h>
42#include <locale.h>
43
44#if defined(OTR_BI) || defined(OTR_PI)
45#include "otr.h"
46#endif
47
48global_t global;        /* Against global namespace pollution */
49
50static void sighandler( int signal );
51
52static int crypt_main( int argc, char *argv[] );
53
54int main( int argc, char *argv[] )
55{
56        int i = 0;
57        char *old_cwd = NULL;
58        struct sigaction sig, old;
59       
60        /* Required to make iconv to ASCII//TRANSLIT work. This makes BitlBee
61           system-locale-sensitive. :-( */
62        setlocale( LC_CTYPE, "" );
63       
64        if( argc > 1 && strcmp( argv[1], "-x" ) == 0 )
65                return crypt_main( argc, argv );
66       
67        log_init();
68       
69        global.conf_file = g_strdup( CONF_FILE_DEF );
70        global.conf = conf_load( argc, argv );
71        if( global.conf == NULL )
72                return( 1 );
73       
74        b_main_init();
75       
76        /* libpurple doesn't like fork()s after initializing itself, so if
77           we use it, do this init a little later (in case we're running in
78           ForkDaemon mode). */
79#ifndef WITH_PURPLE
80        nogaim_init();
81#endif
82       
83        /* Ugly Note: libotr and gnutls both use libgcrypt. libgcrypt
84           has a process-global config state whose initialization happpens
85           twice if libotr and gnutls are used together. libotr installs custom
86           memory management functions for libgcrypt while our gnutls module
87           uses the defaults. Therefore we initialize OTR after SSL. *sigh* */
88        ssl_init();
89#ifdef OTR_BI
90        otr_init();
91#endif
92        /* And in case OTR is loaded as a plugin, it'll also get loaded after
93           this point. */
94       
95        srand( time( NULL ) ^ getpid() );
96       
97        global.helpfile = g_strdup( HELP_FILE );
98        if( help_init( &global.help, global.helpfile ) == NULL )
99                log_message( LOGLVL_WARNING, "Error opening helpfile %s.", HELP_FILE );
100
101        global.storage = storage_init( global.conf->primary_storage, global.conf->migrate_storage );
102        if( global.storage == NULL )
103        {
104                log_message( LOGLVL_ERROR, "Unable to load storage backend '%s'", global.conf->primary_storage );
105                return( 1 );
106        }
107       
108        if( global.conf->runmode == RUNMODE_INETD )
109        {
110                log_link( LOGLVL_ERROR, LOGOUTPUT_IRC );
111                log_link( LOGLVL_WARNING, LOGOUTPUT_IRC );
112       
113                i = bitlbee_inetd_init();
114                log_message( LOGLVL_INFO, "BitlBee %s starting in inetd mode.", BITLBEE_VERSION );
115
116        }
117        else if( global.conf->runmode == RUNMODE_DAEMON )
118        {
119                log_link( LOGLVL_ERROR, LOGOUTPUT_CONSOLE );
120                log_link( LOGLVL_WARNING, LOGOUTPUT_CONSOLE );
121
122                i = bitlbee_daemon_init();
123                log_message( LOGLVL_INFO, "BitlBee %s starting in daemon mode.", BITLBEE_VERSION );
124        }
125        else if( global.conf->runmode == RUNMODE_FORKDAEMON )
126        {
127                log_link( LOGLVL_ERROR, LOGOUTPUT_CONSOLE );
128                log_link( LOGLVL_WARNING, LOGOUTPUT_CONSOLE );
129
130                /* In case the operator requests a restart, we need this. */
131                old_cwd = g_malloc( 256 );
132                if( getcwd( old_cwd, 255 ) == NULL )
133                {
134                        log_message( LOGLVL_WARNING, "Could not save current directory: %s", strerror( errno ) );
135                        g_free( old_cwd );
136                        old_cwd = NULL;
137                }
138               
139                i = bitlbee_daemon_init();
140                log_message( LOGLVL_INFO, "BitlBee %s starting in forking daemon mode.", BITLBEE_VERSION );
141        }
142        if( i != 0 )
143                return( i );
144       
145        if( ( global.conf->user && *global.conf->user ) &&
146            ( global.conf->runmode == RUNMODE_DAEMON || 
147              global.conf->runmode == RUNMODE_FORKDAEMON ) &&
148            ( !getuid() || !geteuid() ) )
149        {
150                struct passwd *pw = NULL;
151                pw = getpwnam( global.conf->user );
152                if( pw )
153                {
154                        setgid( pw->pw_gid );
155                        setuid( pw->pw_uid );
156                }
157        }
158       
159        /* Catch some signals to tell the user what's happening before quitting */
160        memset( &sig, 0, sizeof( sig ) );
161        sig.sa_handler = sighandler;
162        sigaction( SIGCHLD, &sig, &old );
163        sigaction( SIGPIPE, &sig, &old );
164        sig.sa_flags = SA_RESETHAND;
165        sigaction( SIGINT,  &sig, &old );
166        sigaction( SIGILL,  &sig, &old );
167        sigaction( SIGBUS,  &sig, &old );
168        sigaction( SIGFPE,  &sig, &old );
169        sigaction( SIGSEGV, &sig, &old );
170        sigaction( SIGTERM, &sig, &old );
171        sigaction( SIGQUIT, &sig, &old );
172        sigaction( SIGXCPU, &sig, &old );
173       
174        if( !getuid() || !geteuid() )
175                log_message( LOGLVL_WARNING, "BitlBee is running with root privileges. Why?" );
176       
177        b_main_run();
178       
179        /* Mainly good for restarting, to make sure we close the help.txt fd. */
180        help_free( &global.help );
181       
182        if( global.restart )
183        {
184                char *fn = ipc_master_save_state();
185                char *env;
186               
187                env = g_strdup_printf( "_BITLBEE_RESTART_STATE=%s", fn );
188                putenv( env );
189                g_free( fn );
190                /* Looks like env should *not* be freed here as putenv
191                   doesn't make a copy. Odd. */
192               
193                i = chdir( old_cwd );
194                close( global.listen_socket );
195               
196                if( execv( argv[0], argv ) == -1 )
197                        /* Apparently the execve() failed, so let's just
198                           jump back into our own/current main(). */
199                        /* Need more cleanup code to make this work. */
200                        return 1; /* main( argc, argv ); */
201        }
202       
203        return( 0 );
204}
205
206static int crypt_main( int argc, char *argv[] )
207{
208        int pass_len;
209        unsigned char *pass_cr, *pass_cl;
210       
211        if( argc < 4 || ( strcmp( argv[2], "hash" ) != 0 &&
212                          strcmp( argv[2], "unhash" ) != 0 && argc < 5 ) )
213        {
214                printf( "Supported:\n"
215                        "  %s -x enc <key> <cleartext password>\n"
216                        "  %s -x dec <key> <encrypted password>\n"
217                        "  %s -x hash <cleartext password>\n"
218                        "  %s -x unhash <hashed password>\n"
219                        "  %s -x chkhash <hashed password> <cleartext password>\n",
220                        argv[0], argv[0], argv[0], argv[0], argv[0] );
221        }
222        else if( strcmp( argv[2], "enc" ) == 0 )
223        {
224                pass_len = arc_encode( argv[4], strlen( argv[4] ), (unsigned char**) &pass_cr, argv[3], 12 );
225                printf( "%s\n", base64_encode( pass_cr, pass_len ) );
226        }
227        else if( strcmp( argv[2], "dec" ) == 0 )
228        {
229                pass_len = base64_decode( argv[4], (unsigned char**) &pass_cr );
230                arc_decode( pass_cr, pass_len, (char**) &pass_cl, argv[3] );
231                printf( "%s\n", pass_cl );
232        }
233        else if( strcmp( argv[2], "hash" ) == 0 )
234        {
235                md5_byte_t pass_md5[21];
236                md5_state_t md5_state;
237               
238                random_bytes( pass_md5 + 16, 5 );
239                md5_init( &md5_state );
240                md5_append( &md5_state, (md5_byte_t*) argv[3], strlen( argv[3] ) );
241                md5_append( &md5_state, pass_md5 + 16, 5 ); /* Add the salt. */
242                md5_finish( &md5_state, pass_md5 );
243               
244                printf( "%s\n", base64_encode( pass_md5, 21 ) );
245        }
246        else if( strcmp( argv[2], "unhash" ) == 0 )
247        {
248                printf( "Hash %s submitted to a massive Beowulf cluster of\n"
249                        "overclocked 486s. Expect your answer next year somewhere around this time. :-)\n", argv[3] );
250        }
251        else if( strcmp( argv[2], "chkhash" ) == 0 )
252        {
253                char *hash = strncmp( argv[3], "md5:", 4 ) == 0 ? argv[3] + 4 : argv[3];
254                int st = md5_verify_password( argv[4], hash );
255               
256                printf( "Hash %s given password.\n", st == 0 ? "matches" : "does not match" );
257               
258                return st;
259        }
260       
261        return 0;
262}
263
264static void sighandler( int signal )
265{
266        /* FIXME: Calling log_message() here is not a very good idea! */
267       
268        if( signal == SIGTERM || signal == SIGQUIT || signal == SIGINT )
269        {
270                static int first = 1;
271               
272                if( first )
273                {
274                        /* We don't know what we were doing when this signal came in. It's not safe to touch
275                           the user data now (not to mention writing them to disk), so add a timer. */
276                       
277                        log_message( LOGLVL_ERROR, "SIGTERM received, cleaning up process." );
278                        b_timeout_add( 1, (b_event_handler) bitlbee_shutdown, NULL );
279                       
280                        first = 0;
281                }
282                else
283                {
284                        /* Well, actually, for now we'll never need this part because this signal handler
285                           will never be called more than once in a session for a non-SIGPIPE signal...
286                           But just in case we decide to change that: */
287                       
288                        log_message( LOGLVL_ERROR, "SIGTERM received twice, so long for a clean shutdown." );
289                        raise( signal );
290                }
291        }
292        else if( signal == SIGCHLD )
293        {
294                pid_t pid;
295                int st;
296               
297                while( ( pid = waitpid( 0, &st, WNOHANG ) ) > 0 )
298                {
299                        if( WIFSIGNALED( st ) )
300                                log_message( LOGLVL_INFO, "Client %d terminated normally. (status = %d)", (int) pid, WEXITSTATUS( st ) );
301                        else if( WIFEXITED( st ) )
302                                log_message( LOGLVL_INFO, "Client %d killed by signal %d.", (int) pid, WTERMSIG( st ) );
303                }
304        }
305        else if( signal != SIGPIPE )
306        {
307                log_message( LOGLVL_ERROR, "Fatal signal received: %d. That's probably a bug.", signal );
308                raise( signal );
309        }
310}
311
312double gettime()
313{
314        struct timeval time[1];
315
316        gettimeofday( time, 0 );
317        return( (double) time->tv_sec + (double) time->tv_usec / 1000000 );
318}
Note: See TracBrowser for help on using the repository browser.