8 #include <sys/socket.h>
15 #include <netinet/in.h>
27 #define CONNECT_RETRY_MSEC 100
28 #define DEFAULT_START_COMMAND_TIMEOUT 60
29 #define DEFAULT_IDLE_SERVER_TIMEOUT (90*60)
30 #define FLOCK_TIMEOUT_OVER_START_TIMEOUT 2
31 #define SESSION_BUFFER_SIZE 0x1000
34 /* /usr/include/glib-2.0/glib/gmacros.h */
36 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
37 #define G_GNUC_PRINTF( format_idx, arg_idx ) \
38 __attribute__((__format__ (__printf__, format_idx, arg_idx)))
40 #define G_GNUC_PRINTF( format_idx, arg_idx )
41 #endif /* !__GNUC__ */
42 #endif /* !G_GNUC_PRINTF */
44 /* /usr/include/glib-2.0/glib/gmacros.h */
45 #ifndef G_GNUC_NORETURN
46 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
47 #define G_GNUC_NORETURN \
48 __attribute__((__noreturn__))
50 #define G_GNUC_NORETURN
51 #endif /* !__GNUC__ */
52 #endif /* !G_GNUC_NORETURN */
54 /* /usr/include/glib-2.0/glib/gmacros.h */
55 /* Count the number of elements in an array. The array must be defined
56 * as such; using this with a dynamically allocated array will give
59 #define G_N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0]))
62 static const char *program_name;
66 static long opt_start_command_timeout=DEFAULT_START_COMMAND_TIMEOUT;
67 static long opt_idle_server_timeout=DEFAULT_IDLE_SERVER_TIMEOUT;
69 static int opt_syslog;
70 static int opt_stderr;
71 static const char *opt_lock;
72 static const char *opt_command;
74 static void fatal(const char *fmt,...) G_GNUC_PRINTF(1,2) G_GNUC_NORETURN;
75 static void fatal(const char *fmt,...)
77 int use_syslog=opt_syslog,use_stderr=opt_stderr;
80 const char *const error_error="Error printing error message";
82 if (!use_syslog && !use_stderr)
85 if (-1==vasprintf(&string,fmt,ap)) {
91 fprintf(stderr,"%s: %s!\n",program_name,string);
93 openlog(program_name,LOG_PID,LOG_DAEMON);
94 syslog(LOG_DAEMON|LOG_ERR,"%s!",string);
101 static void usage(void)
104 Syntax: %s {-1|--start} [{-T|--start-command-timeout} <start-command-timeout>]\n\
105 \t[{-l|--lock} <filename>] [-S|--syslog] [-e|--stderr]\n\
106 \t{-p|--port} <server-port> <start-server-command>\n\
107 or %s {-0|--stop} [{-i|--idle-server-timeout} <idle-server-timeout>]\n\
108 \t[{-l|--lock} <filename>] [-S|--syslog] [-e|--stderr]\n\
109 \t<stop-server-command>\n\
111 Error messages are printed to stderr by default,\n\
112 -S|--syslog omits stderr output, both -S|--syslog and -e|--stderr output\n\
113 the errors by both methods.\n\
114 \n",program_name,program_name);
118 static const struct option longopts[]={
121 {"start-command-timeout",1,0,'T'},
122 {"idle-server-timeout" ,1,0,'i'},
130 static int lock_fd=-1;
132 static int sighandler_flock_timeout_hit;
133 static void sighandler_flock_timeout(int signo)
135 sighandler_flock_timeout_hit=1;
138 static int lock_create(int lock_mode)
141 sighandler_t sighandler_alrm_orig;
146 /* Never drop the lock if the lock is already being held. */
151 if (-1==(lock_fd=open(opt_lock,O_CREAT|O_RDWR,0600)))
152 fatal("Error creating lock file \"%s\": %m",opt_lock);
154 sighandler_alrm_orig=signal(SIGALRM,sighandler_flock_timeout);
155 alarm(opt_start_command_timeout+FLOCK_TIMEOUT_OVER_START_TIMEOUT);
156 flock_rc=flock(lock_fd,lock_mode);
158 signal(SIGALRM,sighandler_alrm_orig);
159 if (sighandler_flock_timeout_hit)
160 fatal("Timeout locking lock file \"%s\": %m",opt_lock);
162 if (lock_mode&LOCK_NB && errno==EWOULDBLOCK)
164 fatal("Error locking lock file \"%s\": %m",opt_lock);
166 if (!access(opt_lock,R_OK|W_OK)) {
168 fatal("Racing for the lock file \"%s\", giving up",opt_lock);
170 fatal("Error closing lock file \"%s\": %m",opt_lock);
177 static void lock_touch(void)
179 if (!opt_lock || lock_fd==-1)
181 if (utime(opt_lock,NULL))
182 fatal("Error updating lock file \"%s\" timestamp: %m",opt_lock);
185 static void lock_close(void)
190 fatal("Error closing lock file \"%s\": %m",opt_lock);
194 static int connect_try(void)
197 struct sockaddr_in sockaddr_tcp;
199 if (-1==(fdtcp=socket(PF_INET,SOCK_STREAM,0)))
200 fatal("socket(PF_INET,SOCK_STREAM,0)=%d: %m",fdtcp);
201 memset(&sockaddr_tcp,0,sizeof(sockaddr_tcp));
202 sockaddr_tcp.sin_family=AF_INET;
203 sockaddr_tcp.sin_addr.s_addr=htonl(INADDR_LOOPBACK);
204 sockaddr_tcp.sin_port=htons(opt_port);
205 if (connect(fdtcp,(const struct sockaddr *)&sockaddr_tcp,sizeof(sockaddr_tcp))) {
206 if (errno==ECONNREFUSED)
208 fatal("connect(TCP socket,127.0.0.1:%d): %m",opt_port);
213 static void session_transfer(
214 const char *conn0_name,int conn0_fdin,int conn0_fdout,
215 const char *conn1_name,int conn1_fdin,int conn1_fdout)
217 static void session_transfer(
218 const char *conn0_name,int conn0_fdin,int conn0_fdout,
219 const char *conn1_name,int conn1_fdin,int conn1_fdout)
221 struct pollfd pollfdi[2];
223 const char *pollfdi_name[2];
226 pollfdi[0].fd=conn0_fdin;
227 pollfdi[0].events=POLLIN;
228 pollfdi[1].fd=conn1_fdin;
229 pollfdi[1].events=POLLIN;
230 pollfdo[0]=conn0_fdout;
231 pollfdo[1]=conn1_fdout;
232 pollfdi_name[0]=conn0_name;
233 pollfdi_name[1]=conn1_name;
235 for (fdi=0;fdi<G_N_ELEMENTS(pollfdi);fdi++)
236 if (pollfdi[fdi].events)
238 if (fdi>=G_N_ELEMENTS(pollfdi)) {
242 if (0>=poll(pollfdi,G_N_ELEMENTS(pollfdi),-1))
243 fatal("poll(%s socket,%s socket): %m",pollfdi_name[0],pollfdi_name[1]);
244 for (fdi=0;fdi<G_N_ELEMENTS(pollfdi);fdi++)
246 || pollfdi[fdi].revents & (POLLERR|POLLHUP|POLLNVAL)
247 || ((pollfdi[fdi].revents & POLLIN) && !(pollfdi[fdi].events & POLLIN))
249 fatal("poll(%s socket): revents=0x%X (events=0x%X)",
250 pollfdi_name[fdi],(unsigned)pollfdi[fdi].revents,
251 (unsigned)pollfdi[fdi].events);
252 for (fdi=0;fdi<G_N_ELEMENTS(pollfdi);fdi++) {
255 char buf[SESSION_BUFFER_SIZE];
257 if (fcntl(pollfdi[fdi].fd,F_SETFL,O_NONBLOCK))
258 fatal("fcntl(%s socket,F_SETFL,O_NONBLOCK): %m",pollfdi_name[fdi]);
259 got=read(pollfdi[fdi].fd,buf,sizeof(buf));
263 fatal("read(%s socket): %m",pollfdi_name[fdi]);
266 pollfdi[fdi].events&=~POLLIN;
269 for (fdo=0;fdo<G_N_ELEMENTS(pollfdi);fdo++) {
272 if (fcntl(pollfdi[fdo].fd,F_SETFL,0 /* !O_NONBLOCK */))
273 fatal("fcntl(%s socket,F_SETFL,0 /* !O_NONBLOCK */): %m",
275 if (got!=write(pollfdi[fdo].fd,buf,got))
276 fatal("write(%s socket,%ld): %m",
277 pollfdi_name[fdo],(long)got);
284 static void session_try(void)
288 if (-1==(fdtcp=connect_try()))
290 session_transfer("remote",STDIN_FILENO,STDOUT_FILENO,"local",fdtcp,fdtcp);
293 static void system_checked(const char *command)
298 if (WIFEXITED(rc) && !WEXITSTATUS(rc))
301 fatal("Error spawning command \"%s\": return code %d",
302 command,WEXITSTATUS(rc));
304 if (WIFSIGNALED(rc) && WCOREDUMP(rc))
305 fatal("Error spawning command \"%s\": dumped core (terminating signal %d)",
306 command,WTERMSIG(rc));
307 #endif /* WCOREDUMP */
309 fatal("Error spawning command \"%s\": terminating signal %d",
310 command,WTERMSIG(rc));
312 fatal("Error spawning command \"%s\": stopping signal %d",
313 command,WSTOPSIG(rc));
315 if (WIFCONTINUED(rc))
316 fatal("Error spawning command \"%s\": resumed by SIGCONT",
318 #endif /* WIFCONTINUED */
319 fatal("Error spawning command \"%s\": unknown reason",
323 static void start(void) G_GNUC_NORETURN;
324 static void start(void)
329 fatal("-p|--port is a required argument for -1|--start");
330 if (opt_idle_server_timeout!=DEFAULT_IDLE_SERVER_TIMEOUT)
331 fatal("-i|--idle-server-timeout is a forbidden argument for -1|--start");
333 lock_create(LOCK_SH);
338 lock_create(LOCK_EX);
339 system_checked(opt_command);
340 lock_create(LOCK_SH);
342 for (retry=0;retry*CONNECT_RETRY_MSEC/1000<opt_start_command_timeout;retry++) {
345 if (poll(NULL,0,CONNECT_RETRY_MSEC))
346 fatal("poll(timeout %dmsec): %m",CONNECT_RETRY_MSEC);
348 fatal("Timed out after %ld seconds connecting to port %d after spawned: %s",
349 opt_start_command_timeout,opt_port,opt_command);
352 /* Returns: Is fresh? */
353 static int lock_create_and_time_check(int lock_mode)
360 if (!lock_create(lock_mode|LOCK_NB))
362 if (lock_fd==-1 || !fstat(lock_fd,&statbuf))
363 fatal("Error fstat(2)ting lock file \"%s\": %m",opt_lock);
364 return statbuf.st_mtime>=time(NULL)-opt_idle_server_timeout;
367 static void lock_delete_and_close(void)
369 /* Should not happen. */
370 if (!opt_lock || lock_fd==-1)
372 if (unlink(opt_lock))
373 fatal("Error deleting no longer used lock file \"%s\": %m",opt_lock);
377 static void stop(void) G_GNUC_NORETURN;
378 static void stop(void)
382 /* Lock still being held! */
384 fatal("-p|--port is a forbidden argument for -0|--stop");
385 if (opt_start_command_timeout!=DEFAULT_START_COMMAND_TIMEOUT)
386 fatal("-T|--start-command-timeout is a forbidden argument for -0|--stop");
387 if (opt_idle_server_timeout!=DEFAULT_IDLE_SERVER_TIMEOUT && !opt_lock)
388 fatal("-l|--lock is a required argument for -i|--idle-server-timeout of -1|--start");
390 is_fresh=lock_create_and_time_check(LOCK_SH);
395 lock_create_and_time_check(LOCK_EX);
396 system_checked(opt_command);
397 lock_delete_and_close();
402 int main(int argc,char **argv) G_GNUC_NORETURN;
403 int main(int argc,char **argv)
407 if ((program_name=strrchr(argv[0],'/')))
410 program_name=argv[0];
412 optarg=NULL; optind=0; /* FIXME: Possible portability problem. */
413 while ((optc=getopt_long(argc,argv,"c:d:L:l:b:xCM:P:s:m:r:t:T:w:fvhV",longopts,NULL))!=EOF) switch (optc) {
417 case '1': /* -1|--start */
421 case '0': /* -0|--stop */
425 case 'T': /* -T|--start-command-timeout */
426 l=strtol(optarg,&endptr,0);
427 if (l<=0 || l>=LONG_MAX-FLOCK_TIMEOUT_OVER_START_TIMEOUT || (endptr && *endptr))
428 fatal("Invalid -T|--start-command-timeout value: %s",optarg);
429 opt_start_command_timeout=l;
432 case 'i': /* -i|--idle-server-timeout */
433 l=strtol(optarg,&endptr,0);
434 if (l<=0 || l>=LONG_MAX || (endptr && *endptr))
435 fatal("Invalid -i|--idle-server-timeout value: %s",optarg);
436 opt_idle_server_timeout=l;
439 case 'S': /* -S|--syslog */
443 case 'e': /* -e|--stderr */
447 case 'l': /* -l|--lock */
450 case 'p': /* -p|--port */
451 l=strtol(optarg,&endptr,0);
452 if (l<=0 || l>=0x10000 || (endptr && *endptr))
453 fatal("Invalid -p|--port value: %s",optarg);
459 fatal("Error parsing commandline");
464 if (!opt_start && !opt_stop)
465 fatal("At least one of -1|--opt-start or -0|--opt-stop is required");
466 if ( opt_start && opt_stop)
467 fatal("Both modes -1|--opt-start and -0|--opt-stop can never be specified simultaneously");
470 fatal("<start-server-command/stop-server-command> is a required argument");
472 fatal("Too many arguments, <start-server-command/stop-server-command> may need quoting");
473 opt_command=argv[optind];