1 : /******************************************************************************
2 : * $Id: gdalserver.c 25684 2013-02-25 15:40:26Z rouault $
3 : *
4 : * Project: GDAL
5 : * Purpose: Server application that is forked by libgdal
6 : * Author: Even Rouault, <even dot rouault at mines-paris dot org>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2013, Even Rouault, <even dot rouault at mines-paris dot org>
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "cpl_port.h"
31 :
32 : #ifdef WIN32
33 : #ifndef _WIN32_WINNT
34 : #define _WIN32_WINNT 0x0501
35 : #endif
36 : #include <winsock2.h>
37 : #include <ws2tcpip.h>
38 : typedef SOCKET CPL_SOCKET;
39 : #ifndef HAVE_GETADDRINFO
40 : #define HAVE_GETADDRINFO 1
41 : #endif
42 : #else
43 : #include <sys/time.h>
44 : #include <sys/types.h>
45 : #include <sys/wait.h>
46 : #include <sys/socket.h>
47 : #include <sys/un.h>
48 : #include <netinet/in.h>
49 : #include <unistd.h>
50 : #ifdef HAVE_GETADDRINFO
51 : #include <netdb.h>
52 : #endif
53 : typedef int CPL_SOCKET;
54 : #define INVALID_SOCKET -1
55 : #define SOCKET_ERROR -1
56 : #define SOCKADDR struct sockaddr
57 : #define WSAGetLastError() errno
58 : #define WSACleanup()
59 : #define closesocket(s) close(s)
60 : #ifndef SOMAXCONN
61 : #define SOMAXCONN 128
62 : #endif
63 : #endif
64 :
65 :
66 : #include <gdal.h>
67 : #include "cpl_spawn.h"
68 : #include "cpl_string.h"
69 :
70 : CPL_C_START
71 : int CPL_DLL GDALServerLoop(CPL_FILE_HANDLE fin, CPL_FILE_HANDLE fout);
72 : int CPL_DLL GDALServerLoopSocket(CPL_SOCKET nSocket);
73 : CPL_C_END
74 :
75 : CPL_CVSID("$Id: gdalserver.c 25684 2013-02-25 15:40:26Z rouault $");
76 :
77 : /************************************************************************/
78 : /* Usage() */
79 : /************************************************************************/
80 :
81 0 : void Usage(const char* pszErrorMsg)
82 :
83 : {
84 : #ifdef WIN32
85 : printf( "Usage: gdalserver [--help-general] [--help] [-tcpserver port | -stdinout]\n");
86 : #else
87 0 : printf( "Usage: gdalserver [--help-general] [--help] [-tcpserver port | -unixserver filename | -stdinout | [-pipe_in fdin,fdtoclose -pipe_out fdout,fdtoclose]]\n");
88 : #endif
89 0 : printf( "\n" );
90 0 : printf( "-tcpserver : Launch a TCP server on the specified port that can accept.\n");
91 0 : printf( " connections from GDAL clients.\n");
92 0 : printf( "-stdinout : This mode is not meant at being directly used by a user.\n");
93 0 : printf( " It is a helper utility for the client/server working of GDAL.\n");
94 : #ifndef WIN32
95 0 : printf( "-pipe_in/out:This mode is not meant at being directly used by a user.\n");
96 0 : printf( " It is a helper utility for the client/server working of GDAL.\n");
97 : #endif
98 :
99 0 : if( pszErrorMsg != NULL )
100 0 : fprintf(stderr, "\nFAILURE: %s\n", pszErrorMsg);
101 :
102 0 : exit( 1 );
103 : }
104 :
105 :
106 : /************************************************************************/
107 : /* CreateSocketAndBindAndListen() */
108 : /************************************************************************/
109 :
110 2 : int CreateSocketAndBindAndListen(const char* pszService,
111 : int *pnFamily,
112 : int *pnSockType,
113 : int *pnProtocol)
114 : {
115 2 : CPL_SOCKET nListenSocket = INVALID_SOCKET;
116 :
117 : #ifdef HAVE_GETADDRINFO
118 : int nRet;
119 : struct addrinfo sHints;
120 2 : struct addrinfo* psResults = NULL, *psResultsIter;
121 2 : memset(&sHints, 0, sizeof(struct addrinfo));
122 2 : sHints.ai_family = AF_UNSPEC;
123 2 : sHints.ai_socktype = SOCK_STREAM;
124 2 : sHints.ai_flags = AI_PASSIVE;
125 2 : sHints.ai_protocol = IPPROTO_TCP;
126 :
127 2 : nRet = getaddrinfo(NULL, pszService, &sHints, &psResults);
128 2 : if (nRet)
129 : {
130 0 : fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(nRet));
131 0 : return INVALID_SOCKET;
132 : }
133 :
134 6 : for( psResultsIter = psResults;
135 : psResultsIter != NULL;
136 2 : psResultsIter = psResultsIter->ai_next)
137 : {
138 3 : nListenSocket = socket(psResultsIter->ai_family,
139 : psResultsIter->ai_socktype,
140 : psResultsIter->ai_protocol);
141 3 : if (nListenSocket == INVALID_SOCKET)
142 0 : continue;
143 :
144 3 : if (bind(nListenSocket, psResultsIter->ai_addr,
145 : psResultsIter->ai_addrlen) != SOCKET_ERROR)
146 : {
147 1 : if( pnFamily ) *pnFamily = psResultsIter->ai_family;
148 1 : if( pnSockType ) *pnSockType = psResultsIter->ai_socktype;
149 1 : if( pnProtocol ) *pnProtocol = psResultsIter->ai_protocol;
150 :
151 1 : break;
152 : }
153 :
154 2 : closesocket(nListenSocket);
155 : }
156 :
157 2 : freeaddrinfo(psResults);
158 :
159 2 : if (psResultsIter == NULL)
160 : {
161 1 : fprintf(stderr, "Could not bind()\n");
162 1 : return INVALID_SOCKET;
163 : }
164 :
165 : #else
166 :
167 : struct sockaddr_in sockAddrIn;
168 :
169 : if( pnFamily ) *pnFamily = AF_INET;
170 : if( pnSockType ) *pnSockType = SOCK_STREAM;
171 : if( pnProtocol ) *pnProtocol = IPPROTO_TCP;
172 :
173 : nListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
174 : if (nListenSocket == INVALID_SOCKET)
175 : {
176 : fprintf(stderr, "socket() failed with error: %d\n", WSAGetLastError());
177 : return INVALID_SOCKET;
178 : }
179 :
180 : sockAddrIn.sin_family = AF_INET;
181 : sockAddrIn.sin_addr.s_addr = INADDR_ANY;
182 : sockAddrIn.sin_port = htons(atoi(pszService));
183 :
184 : if (bind(nListenSocket, (SOCKADDR *)&sockAddrIn, sizeof (sockAddrIn)) == SOCKET_ERROR)
185 : {
186 : fprintf(stderr, "bind() function failed with error: %d\n", WSAGetLastError());
187 : closesocket(nListenSocket);
188 : return INVALID_SOCKET;
189 : }
190 :
191 : #endif
192 :
193 1 : if (listen(nListenSocket, SOMAXCONN) == SOCKET_ERROR)
194 : {
195 0 : fprintf(stderr, "listen() function failed with error: %d\n", WSAGetLastError());
196 0 : closesocket(nListenSocket);
197 0 : return INVALID_SOCKET;
198 : }
199 :
200 1 : return nListenSocket;
201 : }
202 :
203 : #ifdef WIN32
204 :
205 : /************************************************************************/
206 : /* RunServer() */
207 : /************************************************************************/
208 :
209 : int RunServer(const char* pszApplication,
210 : const char* pszService,
211 : const char* unused_pszUnixSocketFilename)
212 : {
213 : int nRet;
214 : WSADATA wsaData;
215 : SOCKET nListenSocket;
216 : int nFamily, nSockType, nProtocol;
217 :
218 : nRet = WSAStartup(MAKEWORD(2, 2), &wsaData);
219 : if (nRet != NO_ERROR)
220 : {
221 : fprintf(stderr, "WSAStartup() failed with error: %d\n", nRet);
222 : return 1;
223 : }
224 :
225 : nListenSocket = CreateSocketAndBindAndListen(pszService, &nFamily, &nSockType, &nProtocol);
226 : if (nListenSocket == INVALID_SOCKET)
227 : {
228 : WSACleanup();
229 : return 1;
230 : }
231 :
232 : while(TRUE)
233 : {
234 : WSAPROTOCOL_INFO sSocketInfo;
235 : struct sockaddr sockAddr;
236 : socklen_t nLen = sizeof(sockAddr);
237 : CPLSpawnedProcess* psProcess;
238 : CPL_FILE_HANDLE fin, fout;
239 : CPL_PID nPid;
240 : SOCKET nConnSocket;
241 : char szReady[5];
242 : int bOK = TRUE;
243 : const char* apszArgs[] = { NULL, "-newconnection", NULL };
244 :
245 : apszArgs[0] = pszApplication;
246 : nConnSocket = accept(nListenSocket, &sockAddr, &nLen);
247 : if( nConnSocket == SOCKET_ERROR )
248 : {
249 : fprintf(stderr, "accept() function failed with error: %d\n", WSAGetLastError());
250 : closesocket(nListenSocket);
251 : WSACleanup();
252 : return 1;
253 : }
254 :
255 : psProcess = CPLSpawnAsync( NULL,
256 : apszArgs,
257 : TRUE,
258 : TRUE,
259 : FALSE,
260 : NULL);
261 : if( psProcess == NULL )
262 : {
263 : fprintf(stderr, "CPLSpawnAsync() function failed.\n");
264 : closesocket(nConnSocket);
265 : closesocket(nListenSocket);
266 : WSACleanup();
267 : return 1;
268 : }
269 :
270 : nPid = CPLSpawnAsyncGetChildProcessId(psProcess);
271 : fin = CPLSpawnAsyncGetInputFileHandle(psProcess);
272 : fout = CPLSpawnAsyncGetOutputFileHandle(psProcess);
273 :
274 : if( WSADuplicateSocket(nConnSocket, nPid, &sSocketInfo) != 0 )
275 : {
276 : fprintf(stderr, "WSADuplicateSocket() failed: %d\n", WSAGetLastError());
277 : bOK = FALSE;
278 : }
279 :
280 : /* Send socket parameters over the pipe */
281 : if( bOK &&
282 : (!CPLPipeWrite(fout, &sSocketInfo, sizeof(sSocketInfo)) ||
283 : !CPLPipeWrite(fout, &nFamily, sizeof(nFamily)) ||
284 : !CPLPipeWrite(fout, &nSockType, sizeof(nSockType)) ||
285 : !CPLPipeWrite(fout, &nProtocol, sizeof(nProtocol))) )
286 : {
287 : fprintf(stderr, "CPLWritePipe() failed\n");
288 : bOK = FALSE;
289 : }
290 :
291 : /* Wait for child to be ready before closing the socket */
292 : if( bOK && !CPLPipeRead(fin, szReady, sizeof(szReady)) )
293 : {
294 : fprintf(stderr, "CPLReadPipe() failed\n");
295 : bOK = FALSE;
296 : }
297 :
298 : if( !bOK )
299 : {
300 : CPLSpawnAsyncFinish(psProcess, FALSE, TRUE);
301 : closesocket(nConnSocket);
302 : closesocket(nListenSocket);
303 : WSACleanup();
304 : return 1;
305 : }
306 :
307 :
308 : closesocket(nConnSocket);
309 :
310 : CPLSpawnAsyncFinish(psProcess, FALSE, FALSE);
311 : }
312 :
313 : // closesocket(nConnSocket);
314 : // WSACleanup();
315 : }
316 :
317 : /************************************************************************/
318 : /* RunNewConnection() */
319 : /************************************************************************/
320 :
321 : int RunNewConnection()
322 : {
323 : int nRet;
324 : WSADATA wsaData;
325 : WSAPROTOCOL_INFO sSocketInfo;
326 : int nFamily, nSockType, nProtocol;
327 : SOCKET nConnSocket;
328 : CPL_FILE_HANDLE fin = GetStdHandle(STD_INPUT_HANDLE);
329 : CPL_FILE_HANDLE fout = GetStdHandle(STD_OUTPUT_HANDLE);
330 :
331 : /* Get socket parameters from the pipe */
332 : if (!CPLPipeRead(fin, &sSocketInfo, sizeof(sSocketInfo)) ||
333 : !CPLPipeRead(fin, &nFamily, sizeof(nFamily)) ||
334 : !CPLPipeRead(fin, &nSockType, sizeof(nSockType)) ||
335 : !CPLPipeRead(fin, &nProtocol, sizeof(nProtocol)) )
336 : {
337 : fprintf(stderr, "CPLPipeRead() failed\n");
338 : return 1;
339 : }
340 :
341 : nRet = WSAStartup(MAKEWORD(2, 2), &wsaData);
342 : if (nRet != NO_ERROR)
343 : {
344 : fprintf(stderr, "WSAStartup() failed with error: %d\n", nRet);
345 : return 1;
346 : }
347 :
348 : nConnSocket = WSASocket(nFamily, nSockType, nProtocol, &sSocketInfo, 0, WSA_FLAG_OVERLAPPED);
349 : if (nConnSocket == INVALID_SOCKET)
350 : {
351 : fprintf(stderr, "ConnSocket() failed with error: %d\n", WSAGetLastError());
352 : WSACleanup();
353 : return 1;
354 : }
355 :
356 : /* Warn the parent that we are now ready */
357 : if (!CPLPipeWrite(fout, "ready", 5))
358 : {
359 : fprintf(stderr, "CPLPipeWrite() failed\n");
360 : WSACleanup();
361 : return 1;
362 : }
363 : CloseHandle(fout);
364 :
365 : #ifdef _MSC_VER
366 : __try {
367 : #endif
368 : nRet = GDALServerLoopSocket(nConnSocket);
369 : #ifdef _MSC_VER
370 : } __except(1)
371 : {
372 : fprintf(stderr, "gdalserver exited with a fatal error.\n");
373 : nRet = 1;
374 : }
375 : #endif
376 :
377 : closesocket(nConnSocket);
378 : WSACleanup();
379 :
380 : return nRet;
381 : }
382 :
383 : #else
384 :
385 : /************************************************************************/
386 : /* RunServer() */
387 : /************************************************************************/
388 :
389 3 : int RunServer(const char* pszApplication,
390 : const char* pszService,
391 : const char* pszUnixSocketFilename)
392 : {
393 : int nListenSocket;
394 :
395 3 : if( pszUnixSocketFilename != NULL )
396 : {
397 : struct sockaddr_un sockAddrUnix;
398 : int len;
399 :
400 1 : nListenSocket = socket(AF_UNIX, SOCK_STREAM, 0);
401 1 : if (nListenSocket < 0)
402 : {
403 0 : perror("socket");
404 0 : return 1;
405 : }
406 :
407 1 : sockAddrUnix.sun_family = AF_UNIX;
408 1 : CPLStrlcpy(sockAddrUnix.sun_path, pszUnixSocketFilename, sizeof(sockAddrUnix.sun_path));
409 1 : unlink(sockAddrUnix.sun_path);
410 1 : len = strlen(sockAddrUnix.sun_path) + sizeof(sockAddrUnix.sun_family);
411 1 : if (bind(nListenSocket, (struct sockaddr *)&sockAddrUnix, len) == -1)
412 : {
413 0 : perror("bind");
414 0 : closesocket(nListenSocket);
415 0 : return 1;
416 : }
417 1 : if (listen(nListenSocket, SOMAXCONN) == SOCKET_ERROR)
418 : {
419 0 : fprintf(stderr, "listen() function failed with error: %d\n", WSAGetLastError());
420 0 : closesocket(nListenSocket);
421 0 : return 1;
422 : }
423 : }
424 : else
425 : {
426 2 : nListenSocket = CreateSocketAndBindAndListen(pszService, NULL, NULL, NULL);
427 2 : if (nListenSocket < 0)
428 : {
429 1 : return 1;
430 : }
431 : }
432 :
433 : while(TRUE)
434 : {
435 : struct sockaddr sockAddr;
436 4 : socklen_t nLen = sizeof(sockAddr);
437 : int nConnSocket;
438 : pid_t pid;
439 : int nStatus;
440 : struct timeval tv;
441 : fd_set read_fds;
442 :
443 : /* Select on the listen socket, and rip zombie children every second */
444 : do
445 : {
446 4 : FD_ZERO(&read_fds);
447 4 : FD_SET(nListenSocket, &read_fds);
448 4 : tv.tv_sec = 1;
449 4 : tv.tv_usec = 0;
450 4 : waitpid(-1, &nStatus, WNOHANG);
451 : }
452 4 : while( select(nListenSocket + 1, &read_fds, NULL, NULL, &tv) != 1 );
453 :
454 4 : nConnSocket = accept(nListenSocket, &sockAddr, &nLen);
455 4 : if( nConnSocket < 0 )
456 : {
457 0 : fprintf(stderr, "accept() function failed with error: %d\n", errno);
458 0 : close(nListenSocket);
459 0 : return 1;
460 : }
461 :
462 4 : pid = fork();
463 6 : if( pid < 0 )
464 : {
465 0 : fprintf(stderr, "fork() failed: %d\n", errno);
466 0 : close(nListenSocket);
467 0 : close(nConnSocket);
468 0 : return 1;
469 : }
470 6 : else if( pid == 0 )
471 : {
472 : int nRet;
473 4 : close(nListenSocket);
474 4 : nRet = GDALServerLoopSocket(nConnSocket);
475 4 : close(nConnSocket);
476 4 : return nRet;
477 : }
478 : else
479 : {
480 2 : close(nConnSocket);
481 : }
482 2 : }
483 : }
484 :
485 : #endif
486 :
487 : /************************************************************************/
488 : /* main() */
489 : /************************************************************************/
490 :
491 : #define CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(nExtraArg) \
492 : do { if (i + nExtraArg >= argc) \
493 : Usage(CPLSPrintf("%s option requires %d argument(s)", argv[i], nExtraArg)); } while(0)
494 :
495 4 : int main(int argc, char* argv[])
496 : {
497 4 : int i, nRet, bStdinout = FALSE, bPipeIn = FALSE, bPipeOut = FALSE, bNewConnection = FALSE;
498 4 : const char* pszService = NULL, *pszUnixSocketFilename = NULL;
499 : #ifndef WIN32
500 4 : int pipe_in = fileno(stdin);
501 4 : int pipe_out = fileno(stdout);
502 : #endif
503 : /*for( i = 1; i < argc; i++ )
504 : {
505 : if( EQUAL(argv[i], "-daemonize") )
506 : {
507 : daemon(0, 0);
508 : break;
509 : }
510 : }*/
511 :
512 4 : GDALAllRegister();
513 :
514 4 : argc = GDALGeneralCmdLineProcessor( argc, &argv, 0 );
515 4 : if( argc < 1 )
516 0 : exit( -argc );
517 :
518 7 : for( i = 1; i < argc; i++ )
519 : {
520 4 : if( EQUAL(argv[i], "--utility_version") )
521 : {
522 1 : printf("%s was compiled against GDAL %s and is running against GDAL %s\n",
523 : argv[0], GDAL_RELEASE_NAME, GDALVersionInfo("RELEASE_NAME"));
524 1 : return 0;
525 : }
526 3 : else if( EQUAL(argv[i],"--help") )
527 0 : Usage(NULL);
528 3 : else if( EQUAL(argv[i],"-tcpserver") )
529 : {
530 2 : CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
531 2 : i++;
532 2 : pszService = argv[i];
533 : }
534 : #ifndef WIN32
535 1 : else if( EQUAL(argv[i],"-unixserver") )
536 : {
537 1 : CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
538 1 : i++;
539 1 : pszUnixSocketFilename = argv[i];
540 : }
541 : #endif
542 : #ifdef WIN32
543 : else if( EQUAL(argv[i],"-newconnection") )
544 : {
545 : bNewConnection = TRUE;
546 : }
547 : #endif
548 0 : else if( EQUAL(argv[i],"-stdinout") )
549 0 : bStdinout = TRUE;
550 : #ifndef WIN32
551 0 : else if( EQUAL(argv[i],"-pipe_in") )
552 : {
553 : const char* pszComma;
554 0 : CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
555 0 : i++;
556 0 : pipe_in = atoi(argv[i]);
557 0 : bPipeIn = TRUE;
558 0 : pszComma = strchr(argv[i], ',');
559 0 : if( pszComma )
560 0 : close(atoi(pszComma + 1));
561 : }
562 0 : else if( EQUAL(argv[i],"-pipe_out") )
563 : {
564 : const char* pszComma;
565 0 : CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
566 0 : i++;
567 0 : pipe_out = atoi(argv[i]);
568 0 : bPipeOut = TRUE;
569 0 : pszComma = strchr(argv[i], ',');
570 0 : if( pszComma )
571 0 : close(atoi(pszComma + 1));
572 : }
573 : #endif
574 0 : else if( EQUAL(argv[i], "-daemonize") )
575 : ;
576 0 : else if( argv[i][0] == '-' )
577 0 : Usage(CPLSPrintf("Unkown option name '%s'", argv[i]));
578 : else
579 0 : Usage("Too many command options.");
580 : }
581 3 : if( !bStdinout && !(bPipeIn && bPipeOut) &&
582 : pszService == NULL && pszUnixSocketFilename == NULL && !bNewConnection )
583 0 : Usage(NULL);
584 :
585 8 : if( pszService != NULL || pszUnixSocketFilename != NULL )
586 3 : nRet = RunServer(argv[0], pszService, pszUnixSocketFilename);
587 : #ifdef WIN32
588 : else if( bNewConnection )
589 : nRet = RunNewConnection();
590 : #endif
591 :
592 : else
593 : {
594 : #ifdef WIN32
595 : #ifdef _MSC_VER
596 : __try
597 : #endif
598 : {
599 : nRet = GDALServerLoop(GetStdHandle(STD_INPUT_HANDLE),
600 : GetStdHandle(STD_OUTPUT_HANDLE));
601 : }
602 : #ifdef _MSC_VER
603 : __except(1)
604 : {
605 : fprintf(stderr, "gdalserver exited with a fatal error.\n");
606 : nRet = 1;
607 : }
608 : #endif
609 : #else
610 0 : nRet = GDALServerLoop(pipe_in, pipe_out);
611 : #endif
612 : }
613 :
614 5 : CSLDestroy(argv);
615 :
616 5 : return nRet;
617 : }
|