8 #include <sys/socket.h>
15 #include <netinet/in.h>
23 #define CONNECT_RETRY_MSEC 100
25 static long opt_start_timeout=60;
27 static int opt_syslog;
28 static int opt_stderr;
29 static const char *opt_lock;
32 /* /usr/include/glib-2.0/glib/gmacros.h */
34 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
35 #define G_GNUC_PRINTF( format_idx, arg_idx ) \
36 __attribute__((__format__ (__printf__, format_idx, arg_idx)))
38 #define G_GNUC_PRINTF( format_idx, arg_idx )
39 #endif /* !__GNUC__ */
40 #endif /* !G_GNUC_PRINTF */
42 /* /usr/include/glib-2.0/glib/gmacros.h */
43 #ifndef G_GNUC_NORETURN
44 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
45 #define G_GNUC_NORETURN \
46 __attribute__((__noreturn__))
48 #define G_GNUC_NORETURN
49 #endif /* !__GNUC__ */
50 #endif /* !G_GNUC_NORETURN */
52 /* /usr/include/glib-2.0/glib/gmacros.h */
53 /* Count the number of elements in an array. The array must be defined
54 * as such; using this with a dynamically allocated array will give
57 #define G_N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0]))
60 static const char *program_name;
62 static void fatal(const char *fmt,...) G_GNUC_PRINTF(1,2) G_GNUC_NORETURN;
63 static void fatal(const char *fmt,...)
65 int use_syslog=opt_syslog,use_stderr=opt_stderr;
68 const char *const error_error="Error printing error message";
70 if (!use_syslog && !use_stderr)
73 if (-1==vasprintf(&string,fmt,ap)) {
79 fprintf(stderr,"%s: %s!\n",program_name,string);
81 openlog(program_name,LOG_PID,LOG_DAEMON);
82 syslog(LOG_DAEMON|LOG_ERR,"%s!",string);
89 static void usage(void)
92 Syntax: %s [{-T|--start-timeout} <start-timeout] [-S|--syslog] [-e|--stderr]\n\
93 [{-l|--lock} <filename>]\n\
94 {-p|--port} <server-port> <start-server-command>\n\
96 Error messages out to stderr by default, -S|--syslog omits stderr output,\n\
97 both -S|--syslog and -e|--stderr output the errors by both methods.\n\
102 static const struct option longopts[]={
103 {"start-timeout",1,0,'T'},
111 static int connect_try(void)
114 struct sockaddr_in sockaddr_tcp;
116 if (-1==(fdtcp=socket(PF_INET,SOCK_STREAM,0)))
117 fatal("socket(PF_INET,SOCK_STREAM,0)=%d: %m",fdtcp);
118 memset(&sockaddr_tcp,0,sizeof(sockaddr_tcp));
119 sockaddr_tcp.sin_family=AF_INET;
120 sockaddr_tcp.sin_addr.s_addr=htonl(INADDR_LOOPBACK);
121 sockaddr_tcp.sin_port=htons(opt_port);
122 if (connect(fdtcp,(const struct sockaddr *)&sockaddr_tcp,sizeof(sockaddr_tcp))) {
123 if (errno==ECONNREFUSED)
125 fatal("connect(TCP socket,127.0.0.1:%d): %m",opt_port);
130 static void session_transfer(
131 const char *conn0_name,int conn0_fdin,int conn0_fdout,
132 const char *conn1_name,int conn1_fdin,int conn1_fdout)
134 static void session_transfer(
135 const char *conn0_name,int conn0_fdin,int conn0_fdout,
136 const char *conn1_name,int conn1_fdin,int conn1_fdout)
138 struct pollfd pollfdi[2];
140 const char *pollfdi_name[2];
143 pollfdi[0].fd=conn0_fdin;
144 pollfdi[0].events=POLLIN;
145 pollfdi[1].fd=conn1_fdin;
146 pollfdi[1].events=POLLIN;
147 pollfdo[0]=conn0_fdout;
148 pollfdo[1]=conn1_fdout;
149 pollfdi_name[0]=conn0_name;
150 pollfdi_name[1]=conn1_name;
152 for (fdi=0;fdi<G_N_ELEMENTS(pollfdi);fdi++)
153 if (pollfdi[fdi].events)
155 if (fdi>=G_N_ELEMENTS(pollfdi))
157 if (0>=poll(pollfdi,G_N_ELEMENTS(pollfdi),-1))
158 fatal("poll(%s socket,%s socket): %m",pollfdi_name[0],pollfdi_name[1]);
159 for (fdi=0;fdi<G_N_ELEMENTS(pollfdi);fdi++)
161 || pollfdi[fdi].revents & (POLLERR|POLLHUP|POLLNVAL)
162 || ((pollfdi[fdi].revents & POLLIN) && !(pollfdi[fdi].events & POLLIN))
164 fatal("poll(%s socket): revents=0x%X (events=0x%X)",
165 pollfdi_name[fdi],(unsigned)pollfdi[fdi].revents,
166 (unsigned)pollfdi[fdi].events);
167 for (fdi=0;fdi<G_N_ELEMENTS(pollfdi);fdi++) {
172 if (fcntl(pollfdi[fdi].fd,F_SETFL,O_NONBLOCK))
173 fatal("fcntl(%s socket,F_SETFL,O_NONBLOCK): %m",pollfdi_name[fdi]);
174 got=read(pollfdi[fdi].fd,buf,sizeof(buf));
178 fatal("read(%s socket): %m",pollfdi_name[fdi]);
181 pollfdi[fdi].events&=~POLLIN;
184 for (fdo=0;fdo<G_N_ELEMENTS(pollfdi);fdo++) {
187 if (fcntl(pollfdi[fdo].fd,F_SETFL,0 /* !O_NONBLOCK */))
188 fatal("fcntl(%s socket,F_SETFL,0 /* !O_NONBLOCK */): %m",
190 if (got!=write(pollfdi[fdo].fd,buf,got))
191 fatal("write(%s socket,%ld): %m",
192 pollfdi_name[fdo],(long)got);
199 static void session_try(void)
203 if (-1==(fdtcp=connect_try()))
205 session_transfer("remote",STDIN_FILENO,STDOUT_FILENO,"local",fdtcp,fdtcp);
208 static void system_checked(const char *command)
213 if (WIFEXITED(rc) && !WEXITSTATUS(rc))
216 fatal("Error spawning command \"%s\": return code %d!",
217 command,WEXITSTATUS(rc));
219 if (WIFSIGNALED(rc) && WCOREDUMP(rc))
220 fatal("Error spawning command \"%s\": dumped core (terminating signal %d)!",
221 command,WTERMSIG(rc));
222 #endif /* WCOREDUMP */
224 fatal("Error spawning command \"%s\": terminating signal %d!",
225 command,WTERMSIG(rc));
227 fatal("Error spawning command \"%s\": stopping signal %d!",
228 command,WSTOPSIG(rc));
230 if (WIFCONTINUED(rc))
231 fatal("Error spawning command \"%s\": resumed by SIGCONT!",
233 #endif /* WIFCONTINUED */
234 fatal("Error spawning command \"%s\": unknown reason!",
238 static int lock_fd=-1;
240 static void lock_create(void)
247 if (-1==(lock_fd=open(opt_lock,O_CREAT|O_RDWR,0600)))
248 fatal("Error creating lock file \"%s\": %m",opt_lock);
249 if (flock(lock_fd,LOCK_EX))
250 fatal("Error locking lock file \"%s\": %m",opt_lock);
251 if (!access(opt_lock,O_RD|O_WR)) {
253 fatal("Racing for the lock file \"%s\", giving up");
255 fatal("Error closing lock file \"%s\": %m",opt_lock);
259 if (utime(opt_lock,NULL))
260 fatal("Error updating lock file \"%s\" timestamp: %m",opt_lock);
263 static void lock_close(void)
268 fatal("Error closing lock file \"%s\": %m",opt_lock);
272 int main(int argc,char **argv)
278 if ((program_name=strrchr(argv[0],'/')))
281 program_name=argv[0];
283 optarg=NULL; optind=0; /* FIXME: Possible portability problem. */
284 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) {
288 case 'T': /* -T|--start-timeout */
289 l=strtol(optarg,&endptr,0);
290 if (l<=0 || l>=LONG_MAX || (endptr && *endptr))
291 fatal("Invalid -T|--start-timeout values: %s",optarg);
295 case 'S': /* -S|--syslog */
299 case 'e': /* -e|--stderr */
303 case 'l': /* -l|--lock */
306 case 'p': /* -p|--port */
307 l=strtol(optarg,&endptr,0);
308 if (l<=0 || l>=0x10000 || (endptr && *endptr))
309 fatal("Invalid -p|--port value: %s",optarg);
315 fatal("Error parsing commandline!");
321 fatal("-p|--port is a required argument!");
323 fatal("<start-server-command> is a required argument!");
325 fatal("Too many arguments, <start-server-command> needs quoting?");
326 command=argv[optind];
330 system_checked(command);
332 for (retry=0;retry*CONNECT_RETRY_MSEC/1000<opt_start_timeout;retry++) {
334 if (poll(NULL,0,CONNECT_RETRY_MSEC))
335 fatal("poll(timeout %dmsec): %m",CONNECT_RETRY_MSEC);
337 fatal("Timed out after %ld seconds connecting to port %d after spawned: %s",
338 opt_start_timeout,opt_port,command);