source: trunk/daemon/daemon.c @ 1806

Last change on this file since 1806 was 1806, checked in by joshe, 15 years ago

Add debug mode to daemon.

  • Property svn:keywords set to Date Rev Author Id
File size: 8.0 KB
Line 
1/******************************************************************************
2 * $Id: daemon.c 1806 2007-04-26 07:03:36Z joshe $
3 *
4 * Copyright (c) 2007 Joshua Elsasser
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <sys/types.h>
26#include <sys/param.h>
27#include <sys/stat.h>
28#include <sys/socket.h>
29#include <sys/time.h>
30#include <sys/uio.h>
31#include <sys/un.h>
32#include <assert.h>
33#include <errno.h>
34#include <event.h>
35#include <fcntl.h>
36#include <getopt.h>
37#include <signal.h>
38#include <stdarg.h>
39#include <stdlib.h>
40#include <stdio.h>
41#include <string.h>
42#include <unistd.h>
43
44#include "errors.h"
45#include "misc.h"
46#include "server.h"
47#include "torrents.h"
48#include "trcompat.h"
49#include "version.h"
50
51static void usage       ( const char *, ... );
52static void readargs    ( int, char **, int *, int * );
53static int  trylocksock ( void );
54static int  getlock     ( const char * );
55static int  getsock     ( const char * );
56static void exitcleanup ( void );
57static void setupsigs   ( struct event_base * );
58static void gotsig      ( int, short, void * );
59
60static int  gl_lockfd               = -1;
61static char gl_lockpath[MAXPATHLEN] = "";
62static int  gl_sockfd               = -1;
63static char gl_sockpath[MAXPATHLEN] = "";
64
65int
66main( int argc, char ** argv )
67{
68    struct event_base * evbase;
69    int                 nofork, debug, sockfd;
70
71    setmyname( argv[0] );
72    readargs( argc, argv, &nofork, &debug );
73
74    if( !nofork )
75    {
76        if( 0 > daemon( 1, 0 ) )
77        {
78            errnomsg( "failed to daemonize" );
79            exit( 1 );
80        }
81        errsyslog( 1 );
82    }
83
84    atexit( exitcleanup );
85    sockfd = trylocksock();
86    if( 0 > sockfd )
87    {
88        exit( 1 );
89    }
90
91    evbase = event_init();
92    setupsigs( evbase );
93    torrent_init( evbase );
94    server_init( evbase );
95    server_debug( debug );
96    server_listen( sockfd );
97
98    event_dispatch();
99    /* XXX event_base_dispatch( evbase ); */
100
101    return 1;
102}
103
104void
105usage( const char * msg, ... )
106{
107    va_list ap;
108
109    if( NULL != msg )
110    {
111        printf( "%s: ", getmyname() );
112        va_start( ap, msg );
113        vprintf( msg, ap );
114        va_end( ap );
115        printf( "\n" );
116    }
117
118    printf(
119  "usage: %s [-dfh]\n"
120  "\n"
121  "Transmission %s (r%d) http://transmission.m0k.org/\n"
122  "A free, lightweight BitTorrent client with a simple, intuitive interface\n"
123  "\n"
124  "  -d --debug                Print data send and received, implies -f\n"
125  "  -f --foreground           Run in the foreground and log to stderr\n"
126  "  -h --help                 Display this message and exit\n"
127  "\n"
128  "To add torrents or set options, use the transmission-remote program.\n",
129            getmyname(), VERSION_STRING, VERSION_REVISION );
130    exit( 0 );
131}
132
133void
134readargs( int argc, char ** argv, int * nofork, int * debug )
135{
136    char optstr[] = "dfh";
137    struct option longopts[] =
138    {
139        { "debug",              no_argument,       NULL, 'd' },
140        { "foreground",         no_argument,       NULL, 'f' },
141        { "help",               no_argument,       NULL, 'h' },
142        { NULL, 0, NULL, 0 }
143    };
144    int opt;
145
146    *nofork = 0;
147    *debug  = 0;
148
149    while( 0 <= ( opt = getopt_long( argc, argv, optstr, longopts, NULL ) ) )
150    {
151        switch( opt )
152        {
153            case 'd':
154                *debug = 1;
155                /* FALLTHROUGH */
156            case 'f':
157                *nofork = 1;
158                break;
159            default:
160                usage( NULL );
161                break;
162        }
163    }
164}
165
166int
167trylocksock( void )
168{
169    char path[MAXPATHLEN];
170    int  fd;
171
172    confpath( path, sizeof path, NULL, CONF_PATH_TYPE_DAEMON );
173    if( 0 > mkdir( path, 0777 ) && EEXIST != errno )
174    {
175        errnomsg( "failed to create directory: %s", path );
176        return -1;
177    }
178
179    confpath( path, sizeof path, CONF_FILE_LOCK, 0 );
180    fd = getlock( path );
181    if( 0 > fd )
182    {
183        return -1;
184    }
185    gl_lockfd = fd;
186    strlcpy( gl_lockpath, path, sizeof gl_lockpath );
187
188    confpath( path, sizeof path, CONF_FILE_SOCKET, 0 );
189    fd = getsock( path );
190    if( 0 > fd )
191    {
192        return -1;
193    }
194    gl_sockfd = fd;
195    strlcpy( gl_sockpath, path, sizeof gl_sockpath );
196
197    return fd;
198}
199
200int
201getlock( const char * path )
202{
203    struct flock lk;
204    int          fd;
205    char         pid[64];
206
207    fd = open( path, O_RDWR | O_CREAT, 0666 );
208    if( 0 > fd )
209    {
210        errnomsg( "failed to open file: %s", path );
211        return -1;
212    }
213
214    bzero( &lk, sizeof lk );
215    lk.l_start  = 0;
216    lk.l_len    = 0;
217    lk.l_type   = F_WRLCK;
218    lk.l_whence = SEEK_SET;
219    if( 0 > fcntl( fd, F_SETLK, &lk ) )
220    {
221        if( EAGAIN == errno )
222        {
223            errnomsg( "another copy of %s is already running", getmyname() );
224        }
225        else
226        {
227            errnomsg( "failed to obtain lock on file: %s", path );
228        }
229        close( fd );
230        return -1;
231    }
232
233    ftruncate( fd, 0 );
234    snprintf( pid, sizeof pid, "%i\n", getpid() );
235    write( fd, pid, strlen( pid ) );
236
237    return fd;
238}
239
240int
241getsock( const char * path )
242{
243    struct sockaddr_un sun;
244    int                fd;
245
246    fd = socket( PF_LOCAL, SOCK_STREAM, 0 );
247    if( 0 > fd )
248    {
249        errnomsg( "failed to create socket file: %s", path );
250        return -1;
251    }
252
253    bzero( &sun, sizeof sun );
254    sun.sun_family = AF_LOCAL;
255    strlcpy( sun.sun_path, path, sizeof sun.sun_path );
256    unlink( path );
257    if( 0 > bind( fd, ( struct sockaddr * )&sun, SUN_LEN( &sun ) ) )
258    {
259        /* bind can sometimes fail on the first call */
260        unlink( path );
261        if( 0 > bind( fd, ( struct sockaddr * )&sun, SUN_LEN( &sun ) ) )
262        {
263            errnomsg( "failed to bind socket file: %s", path );
264            close( fd );
265            return -1;
266        }
267    }
268
269    return fd;
270}
271
272void
273exitcleanup( void )
274{
275    if( 0 <= gl_sockfd )
276    {
277        unlink( gl_sockpath );
278        close( gl_sockfd );
279    }
280    if( 0 <= gl_lockfd )
281    {
282        unlink( gl_lockpath );
283        close( gl_lockfd );
284    }
285}
286
287void
288setupsigs( struct event_base * base /* XXX */ UNUSED )
289{
290    static struct event ev_int;
291    static struct event ev_quit;
292    static struct event ev_term;
293
294    signal_set( &ev_int, SIGINT, gotsig, NULL );
295    /* XXX event_base_set( base, &ev_int ); */
296    signal_add( &ev_int, NULL );
297
298    signal_set( &ev_quit, SIGINT, gotsig, NULL );
299    /* XXX event_base_set( base, &ev_quit ); */
300    signal_add( &ev_quit, NULL );
301
302    signal_set( &ev_term, SIGINT, gotsig, NULL );
303    /* XXX event_base_set( base, &ev_term ); */
304    signal_add( &ev_term, NULL );
305
306    signal( SIGPIPE, SIG_IGN );
307    signal( SIGHUP, SIG_IGN );
308}
309
310void
311gotsig( int sig, short what UNUSED, void * arg UNUSED )
312{
313    static int exiting = 0;
314
315    if( !exiting )
316    {
317        exiting = 1;
318        errmsg( "received fatal signal %i, attempting to exit cleanly", sig );
319        torrent_exit( 0 );
320    }
321    else
322    {
323        errmsg( "received fatal signal %i while exiting, exiting immediately",
324                sig );
325        signal( sig, SIG_DFL );
326        raise( sig );
327    }
328}
Note: See TracBrowser for help on using the repository browser.