source: branches/daemon/daemon/daemon.c @ 1732

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

Give up on using event bases for now so we can build with libevent 1.1.

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