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;
31 /* /usr/include/glib-2.0/glib/gmacros.h */
33 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
34 #define G_GNUC_PRINTF( format_idx, arg_idx ) \
35 __attribute__((__format__ (__printf__, format_idx, arg_idx)))
37 #define G_GNUC_PRINTF( format_idx, arg_idx )
38 #endif /* !__GNUC__ */
39 #endif /* !G_GNUC_PRINTF */
41 /* /usr/include/glib-2.0/glib/gmacros.h */
42 #ifndef G_GNUC_NORETURN
43 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
44 #define G_GNUC_NORETURN \
45 __attribute__((__noreturn__))
47 #define G_GNUC_NORETURN
48 #endif /* !__GNUC__ */
49 #endif /* !G_GNUC_NORETURN */
51 /* /usr/include/glib-2.0/glib/gmacros.h */
52 /* Count the number of elements in an array. The array must be defined
53 * as such; using this with a dynamically allocated array will give
56 #define G_N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0]))
59 static const char *program_name;
61 static void fatal(const char *fmt,...) G_GNUC_PRINTF(1,2) G_GNUC_NORETURN;
62 static void fatal(const char *fmt,...)
64 int use_syslog=opt_syslog,use_stderr=opt_stderr;
67 const char *const error_error="Error printing error message";
69 if (!use_syslog && !use_stderr)
72 if (-1==vasprintf(&string,fmt,ap)) {
78 fprintf(stderr,"%s: %s!\n",program_name,string);
80 openlog(program_name,LOG_PID,LOG_DAEMON);
81 syslog(LOG_DAEMON|LOG_ERR,"%s!",string);
88 static void usage(void)
91 Syntax: %s [{-T|--start-timeout} <start-timeout] [-S|--syslog] [-e|--stderr]\n\
92 {-p|--port} <server-port> <start-server-command>\n\
94 Error messages out to stderr by default, -S|--syslog omits stderr output,\n\
95 both -S|--syslog and -e|--stderr output the errors by both methods.\n\
100 static const struct option longopts[]={
101 {"start-timeout",1,0,'T'},
108 static int connect_try(void)
111 struct sockaddr_in sockaddr_tcp;
113 if (-1==(fdtcp=socket(PF_INET,SOCK_STREAM,0)))
114 fatal("socket(PF_INET,SOCK_STREAM,0)=%d: %m",fdtcp);
115 memset(&sockaddr_tcp,0,sizeof(sockaddr_tcp));
116 sockaddr_tcp.sin_family=AF_INET;
117 sockaddr_tcp.sin_addr.s_addr=htonl(INADDR_LOOPBACK);
118 sockaddr_tcp.sin_port=htons(opt_port);
119 if (connect(fdtcp,(const struct sockaddr *)&sockaddr_tcp,sizeof(sockaddr_tcp))) {
120 if (errno==ECONNREFUSED)
122 fatal("connect(TCP socket,127.0.0.1:%d): %m",opt_port);
127 static void session_transfer(
128 const char *conn0_name,int conn0_fdin,int conn0_fdout,
129 const char *conn1_name,int conn1_fdin,int conn1_fdout)
131 static void session_transfer(
132 const char *conn0_name,int conn0_fdin,int conn0_fdout,
133 const char *conn1_name,int conn1_fdin,int conn1_fdout)
135 struct pollfd pollfdi[2];
137 const char *pollfdi_name[2];
140 pollfdi[0].fd=conn0_fdin;
141 pollfdi[0].events=POLLIN;
142 pollfdi[1].fd=conn1_fdin;
143 pollfdi[1].events=POLLIN;
144 pollfdo[0]=conn0_fdout;
145 pollfdo[1]=conn1_fdout;
146 pollfdi_name[0]=conn0_name;
147 pollfdi_name[1]=conn1_name;
149 for (fdi=0;fdi<G_N_ELEMENTS(pollfdi);fdi++)
150 if (pollfdi[fdi].events)
152 if (fdi>=G_N_ELEMENTS(pollfdi))
154 if (0>=poll(pollfdi,G_N_ELEMENTS(pollfdi),-1))
155 fatal("poll(%s socket,%s socket): %m",pollfdi_name[0],pollfdi_name[1]);
156 for (fdi=0;fdi<G_N_ELEMENTS(pollfdi);fdi++)
158 || pollfdi[fdi].revents & (POLLERR|POLLHUP|POLLNVAL)
159 || ((pollfdi[fdi].revents & POLLIN) && !(pollfdi[fdi].events & POLLIN))
161 fatal("poll(%s socket): revents=0x%X (events=0x%X)",
162 pollfdi_name[fdi],(unsigned)pollfdi[fdi].revents,
163 (unsigned)pollfdi[fdi].events);
164 for (fdi=0;fdi<G_N_ELEMENTS(pollfdi);fdi++) {
169 if (fcntl(pollfdi[fdi].fd,F_SETFL,O_NONBLOCK))
170 fatal("fcntl(%s socket,F_SETFL,O_NONBLOCK): %m",pollfdi_name[fdi]);
171 got=read(pollfdi[fdi].fd,buf,sizeof(buf));
175 fatal("read(%s socket): %m",pollfdi_name[fdi]);
178 pollfdi[fdi].events&=~POLLIN;
181 for (fdo=0;fdo<G_N_ELEMENTS(pollfdi);fdo++) {
184 if (fcntl(pollfdi[fdo].fd,F_SETFL,0 /* !O_NONBLOCK */))
185 fatal("fcntl(%s socket,F_SETFL,0 /* !O_NONBLOCK */): %m",
187 if (got!=write(pollfdi[fdo].fd,buf,got))
188 fatal("write(%s socket,%ld): %m",
189 pollfdi_name[fdo],(long)got);
196 static void session_try(void)
200 if (-1==(fdtcp=connect_try()))
202 session_transfer("remote",STDIN_FILENO,STDOUT_FILENO,"local",fdtcp,fdtcp);
205 static void system_checked(const char *command)
210 if (WIFEXITED(rc) && !WEXITSTATUS(rc))
213 fatal("Error spawning command \"%s\": return code %d!",
214 command,WEXITSTATUS(rc));
216 if (WIFSIGNALED(rc) && WCOREDUMP(rc))
217 fatal("Error spawning command \"%s\": dumped core (terminating signal %d)!",
218 command,WTERMSIG(rc));
219 #endif /* WCOREDUMP */
221 fatal("Error spawning command \"%s\": terminating signal %d!",
222 command,WTERMSIG(rc));
224 fatal("Error spawning command \"%s\": stopping signal %d!",
225 command,WSTOPSIG(rc));
227 if (WIFCONTINUED(rc))
228 fatal("Error spawning command \"%s\": resumed by SIGCONT!",
230 #endif /* WIFCONTINUED */
231 fatal("Error spawning command \"%s\": unknown reason!",
235 int main(int argc,char **argv)
241 if ((program_name=strrchr(argv[0],'/')))
244 program_name=argv[0];
246 optarg=NULL; optind=0; /* FIXME: Possible portability problem. */
247 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) {
251 case 'T': /* -T|--start-timeout */
252 l=strtol(optarg,&endptr,0);
253 if (l<=0 || l>=LONG_MAX || (endptr && *endptr))
254 fatal("Invalid -T|--start-timeout values: %s",optarg);
258 case 'p': /* -p|--port */
259 l=strtol(optarg,&endptr,0);
260 if (l<=0 || l>=0x10000 || (endptr && *endptr))
261 fatal("Invalid -p|--port value: %s",optarg);
267 fatal("Error parsing commandline!");
273 fatal("-p|--port is a required argument!");
275 fatal("<start-server-command> is a required argument!");
277 fatal("Too many arguments, <start-server-command> needs quoting?");
278 command=argv[optind];
281 system_checked(command);
283 for (retry=0;retry*CONNECT_RETRY_MSEC/1000<opt_start_timeout;retry++) {
285 if (poll(NULL,0,CONNECT_RETRY_MSEC))
286 fatal("poll(timeout %dmsec): %m",CONNECT_RETRY_MSEC);
288 fatal("Timed out after %ld seconds connecting to port %d after spawned: %s",
289 opt_start_timeout,opt_port,command);