source: trunk/daemon/daemon.c @ 2743

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

Add a pidfile command-line argument.

  • Property svn:keywords set to Date Rev Author Id
File size: 9.4 KB
Line 
1/******************************************************************************
2 * $Id: daemon.c 2743 2007-08-14 04:02:50Z 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 <libtransmission/trcompat.h>
45#include <libtransmission/version.h>
46
47#include "errors.h"
48#include "misc.h"
49#include "server.h"
50#include "torrents.h"
51
52static void usage       ( const char *, ... );
53static void readargs    ( int, char **, int *, int *, char **, char ** );
54static int  trylocksock ( const char * );
55static int  getlock     ( const char * );
56static int  getsock     ( const char * );
57static void exitcleanup ( void );
58static void setupsigs   ( struct event_base * );
59static void gotsig      ( int, short, void * );
60static int  savepid     ( const char * );
61
62static int  gl_lockfd               = -1;
63static char gl_lockpath[MAXPATHLEN] = "";
64static int  gl_sockfd               = -1;
65static char gl_sockpath[MAXPATHLEN] = "";
66static char gl_pidfile[MAXPATHLEN]  = "";
67
68int
69main( int argc, char ** argv )
70{
71    struct event_base * evbase;
72    int                 nofork, debug, sockfd;
73    char              * sockpath, * pidfile;
74
75    setmyname( argv[0] );
76    readargs( argc, argv, &nofork, &debug, &sockpath, &pidfile );
77
78    if( !nofork )
79    {
80        if( 0 > daemon( 1, 0 ) )
81        {
82            errnomsg( "failed to daemonize" );
83            exit( 1 );
84        }
85        errsyslog( 1 );
86    }
87
88    if( 0 > savepid( pidfile ) )
89        exit( 1 );
90    atexit( exitcleanup );
91    sockfd = trylocksock( sockpath );
92    if( 0 > sockfd )
93    {
94        exit( 1 );
95    }
96
97    evbase = event_init();
98    setupsigs( evbase );
99    torrent_init( evbase );
100    server_init( evbase );
101    server_debug( debug );
102    server_listen( sockfd );
103
104    event_dispatch();
105    /* XXX event_base_dispatch( evbase ); */
106
107    return 1;
108}
109
110void
111usage( const char * msg, ... )
112{
113    va_list ap;
114
115    if( NULL != msg )
116    {
117        printf( "%s: ", getmyname() );
118        va_start( ap, msg );
119        vprintf( msg, ap );
120        va_end( ap );
121        printf( "\n" );
122    }
123
124    printf(
125  "usage: %s [-dfh] [-p file] [-s file]\n"
126  "\n"
127  "Transmission %s http://transmission.m0k.org/\n"
128  "A free, lightweight BitTorrent client with a simple, intuitive interface\n"
129  "\n"
130  "  -d --debug                Print data send and received, implies -f\n"
131  "  -f --foreground           Run in the foreground and log to stderr\n"
132  "  -h --help                 Display this message and exit\n"
133  "  -p --pidfile <path>       Save the process id in a file at <path>\n"
134  "  -s --socket <path>        Place the socket file at <path>\n"
135  "\n"
136  "To add torrents or set options, use the transmission-remote program.\n",
137            getmyname(), LONG_VERSION_STRING );
138    exit( 0 );
139}
140
141void
142readargs( int argc, char ** argv, int * nofork, int * debug, char ** sock,
143          char ** pidfile )
144{
145    char optstr[] = "dfhp:s:";
146    struct option longopts[] =
147    {
148        { "debug",              no_argument,       NULL, 'd' },
149        { "foreground",         no_argument,       NULL, 'f' },
150        { "help",               no_argument,       NULL, 'h' },
151        { "pidfile",            required_argument, NULL, 'p' },
152        { "socket",             required_argument, NULL, 's' },
153        { NULL, 0, NULL, 0 }
154    };
155    int opt;
156
157    *nofork    = 0;
158    *debug     = 0;
159    *sock      = NULL;
160    *pidfile   = NULL;
161
162    while( 0 <= ( opt = getopt_long( argc, argv, optstr, longopts, NULL ) ) )
163    {
164        switch( opt )
165        {
166            case 'd':
167                *debug = 1;
168                /* FALLTHROUGH */
169            case 'f':
170                *nofork = 1;
171                break;
172            case 'p':
173                *pidfile = optarg;
174                break;
175            case 's':
176                *sock   = optarg;
177                break;
178            default:
179                usage( NULL );
180                break;
181        }
182    }
183}
184
185int
186trylocksock( const char * sockpath )
187{
188    char path[MAXPATHLEN];
189    int  fd;
190
191    confpath( path, sizeof path, NULL, CONF_PATH_TYPE_DAEMON );
192    if( 0 > mkdir( path, 0777 ) && EEXIST != errno )
193    {
194        errnomsg( "failed to create directory: %s", path );
195        return -1;
196    }
197
198    confpath( path, sizeof path, CONF_FILE_LOCK, 0 );
199    fd = getlock( path );
200    if( 0 > fd )
201    {
202        return -1;
203    }
204    gl_lockfd = fd;
205    strlcpy( gl_lockpath, path, sizeof gl_lockpath );
206
207    if( NULL == sockpath )
208    {
209        confpath( path, sizeof path, CONF_FILE_SOCKET, 0 );
210        sockpath = path;
211    }
212    fd = getsock( sockpath );
213    if( 0 > fd )
214    {
215        return -1;
216    }
217    gl_sockfd = fd;
218    strlcpy( gl_sockpath, sockpath, sizeof gl_sockpath );
219
220    return fd;
221}
222
223int
224getlock( const char * path )
225{
226    struct flock lk;
227    int          fd;
228    char         pid[64];
229
230    fd = open( path, O_RDWR | O_CREAT, 0666 );
231    if( 0 > fd )
232    {
233        errnomsg( "failed to open file: %s", path );
234        return -1;
235    }
236
237    memset( &lk, 0, sizeof lk );
238    lk.l_start  = 0;
239    lk.l_len    = 0;
240    lk.l_type   = F_WRLCK;
241    lk.l_whence = SEEK_SET;
242    if( 0 > fcntl( fd, F_SETLK, &lk ) )
243    {
244        if( EAGAIN == errno )
245        {
246            errmsg( "another copy of %s is already running", getmyname() );
247        }
248        else
249        {
250            errnomsg( "failed to obtain lock on file: %s", path );
251        }
252        close( fd );
253        return -1;
254    }
255
256    ftruncate( fd, 0 );
257    snprintf( pid, sizeof pid, "%i\n", getpid() );
258    write( fd, pid, strlen( pid ) );
259
260    return fd;
261}
262
263int
264getsock( const char * path )
265{
266    struct sockaddr_un sa;
267    int                fd;
268
269    fd = socket( PF_LOCAL, SOCK_STREAM, 0 );
270    if( 0 > fd )
271    {
272        errnomsg( "failed to create socket file: %s", path );
273        return -1;
274    }
275
276    memset( &sa, 0, sizeof sa );
277    sa.sun_family = AF_LOCAL;
278    strlcpy( sa.sun_path, path, sizeof sa.sun_path );
279    unlink( path );
280    if( 0 > bind( fd, ( struct sockaddr * )&sa, SUN_LEN( &sa ) ) )
281    {
282        /* bind can sometimes fail on the first call */
283        unlink( path );
284        if( 0 > bind( fd, ( struct sockaddr * )&sa, SUN_LEN( &sa ) ) )
285        {
286            errnomsg( "failed to bind socket file: %s", path );
287            close( fd );
288            return -1;
289        }
290    }
291
292    return fd;
293}
294
295void
296exitcleanup( void )
297{
298    if( 0 <= gl_sockfd )
299    {
300        unlink( gl_sockpath );
301        close( gl_sockfd );
302    }
303    if( 0 <= gl_lockfd )
304    {
305        unlink( gl_lockpath );
306        close( gl_lockfd );
307    }
308    if( 0 != gl_pidfile[0] )
309    {
310        unlink( gl_pidfile );
311    }
312}
313
314void
315setupsigs( struct event_base * base /* XXX */ UNUSED )
316{
317    static struct event ev_int;
318    static struct event ev_quit;
319    static struct event ev_term;
320
321    signal_set( &ev_int, SIGINT, gotsig, NULL );
322    /* XXX event_base_set( base, &ev_int ); */
323    signal_add( &ev_int, NULL );
324
325    signal_set( &ev_quit, SIGQUIT, gotsig, NULL );
326    /* XXX event_base_set( base, &ev_quit ); */
327    signal_add( &ev_quit, NULL );
328
329    signal_set( &ev_term, SIGTERM, gotsig, NULL );
330    /* XXX event_base_set( base, &ev_term ); */
331    signal_add( &ev_term, NULL );
332
333    signal( SIGPIPE, SIG_IGN );
334    signal( SIGHUP, SIG_IGN );
335}
336
337void
338gotsig( int sig, short what UNUSED, void * arg UNUSED )
339{
340    static int exiting = 0;
341
342    if( !exiting )
343    {
344        exiting = 1;
345        errmsg( "received fatal signal %i, attempting to exit cleanly", sig );
346        torrent_exit( 0 );
347    }
348    else
349    {
350        errmsg( "received fatal signal %i while exiting, exiting immediately",
351                sig );
352        signal( sig, SIG_DFL );
353        raise( sig );
354    }
355}
356
357int
358savepid( const char * file )
359{
360    FILE * pid;
361
362    pid = fopen( file, "wb" );
363    if( NULL == pid )
364    {
365        errnomsg( "failed to open pid file: %s", file );
366        return -1;
367    }
368
369    if( 0 > fprintf( pid, "%d\n", (int) getpid() ) )
370    {
371        errnomsg( "failed to write pid to file: %s", file );
372        fclose( pid );
373        unlink( file );
374        return -1;
375    }
376
377    fclose( pid );
378    strlcpy( gl_pidfile, file, sizeof gl_pidfile );
379
380    return 0;
381}
Note: See TracBrowser for help on using the repository browser.