source: trunk/daemon/daemon.c @ 2415

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

Apparently "sun" is a bad choice for a variable name on solaris.

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