source: trunk/daemon/daemon.c @ 4390

Last change on this file since 4390 was 4390, checked in by livings124, 14 years ago

replace other instances of the old url

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