source: trunk/daemon/daemon.c @ 5167

Last change on this file since 5167 was 5167, checked in by charles, 14 years ago

add win32/posix wrapper function tr_lockfile() to libT and use it in daemon/gtk.

  • Property svn:keywords set to Date Rev Author Id
File size: 8.9 KB
Line 
1/******************************************************************************
2 * $Id: daemon.c 5167 2008-02-28 19:06:23Z charles $
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 <getopt.h>
36#include <signal.h>
37#include <stdarg.h>
38#include <stdlib.h>
39#include <stdio.h>
40#include <string.h>
41#include <unistd.h>
42
43#include <libtransmission/trcompat.h>
44#include <libtransmission/platform.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  getsock     ( const char * );
56static void exitcleanup ( void );
57static void setupsigs   ( struct event_base * );
58static void gotsig      ( int, short, void * );
59static int  savepid     ( const char * );
60
61static char gl_lockpath[MAXPATHLEN] = "";
62static int  gl_sockfd               = -1;
63static char gl_sockpath[MAXPATHLEN] = "";
64static char gl_pidfile[MAXPATHLEN]  = "";
65
66int
67main( int argc, char ** argv )
68{
69    struct event_base * evbase;
70    int                 nofork, debug, sockfd;
71    char              * sockpath, * pidfile;
72
73    setmyname( argv[0] );
74    readargs( argc, argv, &nofork, &debug, &sockpath, &pidfile );
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    if( NULL != pidfile && 0 > savepid( pidfile ) )
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_base_dispatch( evbase );
105
106    return 1;
107}
108
109void
110usage( const char * msg, ... )
111{
112    va_list ap;
113
114    if( NULL != msg )
115    {
116        printf( "%s: ", getmyname() );
117        va_start( ap, msg );
118        vprintf( msg, ap );
119        va_end( ap );
120        printf( "\n" );
121    }
122
123    printf(
124  "usage: %s [-dfh] [-p file] [-s file]\n"
125  "\n"
126  "Transmission %s http://www.transmissionbt.com/\n"
127  "A fast and easy BitTorrent client\n"
128  "\n"
129  "  -d --debug                Print data send and received, implies -f\n"
130  "  -f --foreground           Run in the foreground and log to stderr\n"
131  "  -h --help                 Display this message and exit\n"
132  "  -p --pidfile <path>       Save the process id in a file at <path>\n"
133  "  -s --socket <path>        Place the socket file at <path>\n"
134  "\n"
135  "To add torrents or set options, use the transmission-remote program.\n",
136            getmyname(), LONG_VERSION_STRING );
137    exit( 0 );
138}
139
140void
141readargs( int argc, char ** argv, int * nofork, int * debug, char ** sock,
142          char ** pidfile )
143{
144    char optstr[] = "dfhp:s:";
145    struct option longopts[] =
146    {
147        { "debug",              no_argument,       NULL, 'd' },
148        { "foreground",         no_argument,       NULL, 'f' },
149        { "help",               no_argument,       NULL, 'h' },
150        { "pidfile",            required_argument, NULL, 'p' },
151        { "socket",             required_argument, NULL, 's' },
152        { NULL, 0, NULL, 0 }
153    };
154    int opt;
155
156    *nofork    = 0;
157    *debug     = 0;
158    *sock      = NULL;
159    *pidfile   = NULL;
160
161    while( 0 <= ( opt = getopt_long( argc, argv, optstr, longopts, NULL ) ) )
162    {
163        switch( opt )
164        {
165            case 'd':
166                *debug = 1;
167                /* FALLTHROUGH */
168            case 'f':
169                *nofork = 1;
170                break;
171            case 'p':
172                *pidfile = optarg;
173                break;
174            case 's':
175                *sock   = optarg;
176                break;
177            default:
178                usage( NULL );
179                break;
180        }
181    }
182}
183
184static int
185getlock( const char * filename )
186{
187    const int state = tr_lockfile( filename );
188    const int success = state == TR_LOCKFILE_SUCCESS;
189
190    if( !success ) switch( state ) {
191        case TR_LOCKFILE_EOPEN:
192            errnomsg( "failed to open file: %s", filename );
193            break;
194        case TR_LOCKFILE_ELOCK:
195            errmsg( "another copy of %s is already running", getmyname() );
196            break;
197        default:
198            errmsg( "unhandled tr_lockfile error: %d", state );
199            break;
200    }
201
202    return success;
203}
204
205
206int
207trylocksock( const char * sockpath )
208{
209    char path[MAXPATHLEN];
210    int  fd;
211
212    confpath( path, sizeof path, NULL, CONF_PATH_TYPE_DAEMON );
213    if( 0 > mkdir( path, 0777 ) && EEXIST != errno )
214    {
215        errnomsg( "failed to create directory: %s", path );
216        return -1;
217    }
218
219    confpath( path, sizeof path, CONF_FILE_LOCK, 0 );
220    if( !getlock( path ) )
221        return -1;
222    strlcpy( gl_lockpath, path, sizeof gl_lockpath );
223
224    if( NULL == sockpath )
225    {
226        confpath( path, sizeof path, CONF_FILE_SOCKET, 0 );
227        sockpath = path;
228    }
229    fd = getsock( sockpath );
230    if( 0 > fd )
231    {
232        return -1;
233    }
234    gl_sockfd = fd;
235    strlcpy( gl_sockpath, sockpath, sizeof gl_sockpath );
236
237    return fd;
238}
239
240int
241getsock( const char * path )
242{
243    struct sockaddr_un sa;
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    memset( &sa, 0, sizeof sa );
254    sa.sun_family = AF_LOCAL;
255    strlcpy( sa.sun_path, path, sizeof sa.sun_path );
256    unlink( path );
257    if( 0 > bind( fd, ( struct sockaddr * )&sa, SUN_LEN( &sa ) ) )
258    {
259        /* bind can sometimes fail on the first call */
260        unlink( path );
261        if( 0 > bind( fd, ( struct sockaddr * )&sa, SUN_LEN( &sa ) ) )
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_pidfile[0] )
281    {
282        unlink( gl_pidfile );
283    }
284
285    if( *gl_lockpath )
286        unlink( gl_lockpath );
287}
288
289void
290setupsigs( struct event_base * base )
291{
292    static struct event ev_int;
293    static struct event ev_quit;
294    static struct event ev_term;
295
296    signal_set( &ev_int, SIGINT, gotsig, NULL );
297    event_base_set( base, &ev_int );
298    signal_add( &ev_int, NULL );
299
300    signal_set( &ev_quit, SIGQUIT, gotsig, NULL );
301    event_base_set( base, &ev_quit );
302    signal_add( &ev_quit, NULL );
303
304    signal_set( &ev_term, SIGTERM, gotsig, NULL );
305    event_base_set( base, &ev_term );
306    signal_add( &ev_term, NULL );
307
308    signal( SIGPIPE, SIG_IGN );
309    signal( SIGHUP, SIG_IGN );
310}
311
312void
313gotsig( int sig, short what UNUSED, void * arg UNUSED )
314{
315    static int exiting = 0;
316
317    if( !exiting )
318    {
319        exiting = 1;
320        errmsg( "received fatal signal %i, attempting to exit cleanly", sig );
321        server_quit();
322    }
323    else
324    {
325        errmsg( "received fatal signal %i while exiting, exiting immediately",
326                sig );
327        signal( sig, SIG_DFL );
328        raise( sig );
329    }
330}
331
332int
333savepid( const char * file )
334{
335    FILE * pid;
336
337    pid = fopen( file, "wb" );
338    if( NULL == pid )
339    {
340        errnomsg( "failed to open pid file: %s", file );
341        return -1;
342    }
343
344    if( 0 > fprintf( pid, "%d\n", (int) getpid() ) )
345    {
346        errnomsg( "failed to write pid to file: %s", file );
347        fclose( pid );
348        unlink( file );
349        return -1;
350    }
351
352    fclose( pid );
353    strlcpy( gl_pidfile, file, sizeof gl_pidfile );
354
355    return 0;
356}
Note: See TracBrowser for help on using the repository browser.