source: branches/blt4/packages/vizservers/nanoscale/server2.c @ 2338

Last change on this file since 2338 was 2338, checked in by gah, 13 years ago
File size: 7.5 KB
Line 
1
2#include <stdio.h>
3#include <unistd.h>
4#include <stdlib.h>
5#include <string.h>
6#include <signal.h>
7#include <sys/types.h>
8#include <sys/socket.h>
9#include <sys/wait.h>
10#include <sys/select.h>
11#include <sys/time.h>
12#include <arpa/inet.h>
13#include <fcntl.h>
14#include <netinet/in.h>
15#include <getopt.h>
16#include <errno.h>
17
18
19static void
20Help(const char *program)
21{
22    fprintf(stderr,
23        "Syntax: %s [-d] [-f serversFile] [-x numVideoCards]\n", program);
24    exit(1);
25}
26
27typedef struct {
28    const char *name;
29    int port;
30    int argc;
31    char **argv;
32    int f;
33} RenderServer;
34
35static Tcl_HashTable serverTable;
36
37static int
38RegisterServerCmd(ClientData clientData, Tcl_Interp *interp, int objc,
39                  Tcl_Obj *const *objv)
40{
41    Tcl_Obj *objPtr;
42    const char *serverName;
43    int bool, isNew;
44    int f;
45    int port;
46    struct sockaddr_in addr;
47    RenderServer *serverPtr;
48    Tcl_HashEntry *hPtr;
49
50    if (objc != 4) {
51        Tcl_AppendResult("wrong # args: should be \"", Tcl_GetString(objv[0]),
52                         " serverName port cmd", (char *)NULL);
53        return TCL_ERROR;
54    }
55    serverName = Tcl_GetString(objv[1]);
56    if (Tcl_GetInt(interp, objv[2], &port) != TCL_OK) {
57        return TCL_ERROR;
58    }
59    hPtr = Tcl_CreateHashEntry(&serverTable, (char *)port, &isNew);
60    if (!isNew) {
61        Tcl_AppendResult("a server is already listening on port ",
62                Tcl_GetString(objv[2]), (char *)NULL);
63        return TCL_ERROR;
64    }
65    objPtr = Tcl_SubstObj(interp, objv[3],
66                          TCL_SUBST_VARIABLES | TCL_SUBST_BACKSLASHES);
67    if (Tcl_SplitList(interp, Tcl_GetString(objPtr), &argc, &argv) != TCL_OK) {
68        return TCL_ERROR;
69    }
70
71    // Create a socket for listening.
72    f = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
73    if (f < 0) {
74        Tcl_AppendResult(interp, "can't create listerner socket for \"",
75                serverName, "\": ", Tcl_PosixError(interp), (char *)NULL);
76        return TCL_ERROR;
77    }
78 
79    // If program is killed, drop the socket address reservation immediately.
80    bool = 1;
81    status = setsockopt(f, SOL_SOCKET, SO_REUSEADDR, &bool, sizeof(bool));
82    if (status < 0) {
83        Tcl_AppendResult(interp, "can't create set socket option for \"",
84                serverName, "\": ", Tcl_PosixError(interp), (char *)NULL);
85        return TCL_ERROR;
86    }
87
88    /* Bind this address to the socket. */
89    addr.sin_family = AF_INET;
90    addr.sin_port = htons(port);
91    addr.sin_addr.s_addr = htonl(INADDR_ANY);
92    status = bind(f, (struct sockaddr *)&addr, sizeof(addr));
93    if (status < 0) {
94        Tcl_AppendResult(interp, "can't bind to socket for \"",
95                serverName, "\": ", Tcl_PosixError(interp), (char *)NULL);
96        return TCL_ERROR;
97    }
98    /* Listen on the specified port. */
99    status = listen(f, 5);
100    if (status < 0) {
101        Tcl_AppendResult(interp, "can't listen to socket for \"",
102                serverName, "\": ", Tcl_PosixError(interp), (char *)NULL);
103        return TCL_ERROR;
104    }
105    serverPtr = malloc(sizeof(RenderServer));
106    if (serverPtr == NULL) {
107        Tcl_AppendResult(interp, "can't allocate structure for \"",
108                serverName, "\": ", Tcl_PosixError(interp), (char *)NULL);
109        return TCL_ERROR;
110    }
111    serverPtr->name = strdup(serverName);
112    serverPtr->argv = argv;
113    serverPtr->argc = argc;
114    serverPtr->listenerFd = f;
115    return TCL_OK;
116}
117
118static int
119ParseServersFile(const char *fileName)
120{
121    Tcl_Interp *interp;
122
123    interp = Tcl_CreateInterp();
124    Tcl_MakeSafe(interp);
125    Tcl_CreateObjCommand(interp, "register_server", RegisterServerCmd, NULL,
126                         NULL);
127    if (Tcl_EvalFile(interp, fileName) != TCL_OK) {
128        ERROR("can't add server: %s", Tcl_GetString(Tcl_GetObjResult(interp)));
129        return FALSE;
130    }
131    Tcl_DeleteInterp(interp);
132    return TRUE;
133}
134
135int
136main(int argc, char *argv[])
137{
138    int status;
139    struct sockaddr_in recvAddr;
140    int serverFds[FD_SETSIZE];
141    int recvPort;
142    int n;
143    char displayVar[200];
144    int maxCards;
145    int dispNum;
146    Tcl_HashEntry *hPtr;
147    Tcl_HashSearch iter;
148
149    dispNum = 0;
150    maxCards = 1;
151    recvPort = 2000;
152    debug_flags = FALSE;
153    strcpy(displayVar, "DISPLAY=:0.0");
154    if (putenv(displayVar) < 0) {
155        ERROR("can't set DISPLAY variable: ", strerror(errno));
156        exit(1);
157    }
158    Tcl_InitHashTable(&serverTable, TCL_ONE_WORD_KEYS);
159    while (1) {
160        int c;
161        int option_index = 0;
162        struct option long_options[] = {
163            // name, has_arg, flag, val
164            { 0,0,0,0 },
165        };
166
167        c = getopt_long(argc, argv, "+p:x:d", long_options, &option_index);
168        if (c == -1) {
169            break;
170        }
171
172        switch(c) {
173        case 'x': /* Number of video cards */
174            maxCards = strtoul(optarg, 0, 0);
175            if ((maxCards < 1) || (maxCards > 10)) {
176                fprintf(stderr, "bad number of max videocards specified\n");
177                return 1;
178            }
179            break;
180        case 'd':
181            debug_flag = 1;
182            break;
183        case 'p':
184            recvPort = strtoul(optarg, 0, 0);
185            break;
186
187        case 'f':
188            fileName = strdup(optarg);
189            break;
190
191        default:
192            fprintf(stderr,"Don't know what option '%c'.\n", c);
193            help(argv[0]);
194            return 1;
195        }
196    }
197
198    if (serverTable.numEntries == 0) {
199        ERROR("no servers designated.");
200        exit(0);
201    }
202
203    if (!ParseServersFile(fileName)) {
204        exit(1);
205    }   
206
207    // Bind this address to the socket.
208    recvAddr.sin_family = AF_INET;
209    recvAddr.sin_port = htons(recvPort);
210    recvAddr.sin_addr.s_addr = htonl(INADDR_ANY);
211
212    status = bind(sendFd, (struct sockaddr *)&recvAddr, sizeof(recvAddr));
213    if (status < 0) {
214        ERROR("can't bind to address: ", strerror(errno));
215        exit(1);
216    }
217
218    if (!debug_flag) {
219        if (daemon(0,1) < 0) {
220            ERROR("can't daemonize nanoscale: ", strerror(errno));
221            exit(1);
222        }
223    }
224
225    /* Build the array of servers listener file descriptors. */
226    FD_ZERO(&serverFds);
227    for (hPtr = Tcl_FirstHashEntry(&serverTable, &iter); hPtr != NULL;
228         hPtr = Tcl_NextHashEntry(&iter)) {
229        RenderServer *serverPtr;
230
231        serverPtr = Tcl_GetHashValue(hPtr);
232        FD_SET(serverPtr->listenerFd, &serverFds);
233        if (serverPtr->listenerFd > maxFd) {
234            maxFs = serverPtr->listenerFd;
235        }
236    }
237    while (select(maxFd+1, serverFds, NULL, NULL, 0) > 0) {
238
239        for (hPtr = Tcl_FirstHashEntry(&serverTable, &iter); hPtr != NULL;
240             hPtr = Tcl_NextHashEntry(&iter)) {
241            RenderServer *serverPtr;
242
243            serverPtr = Tcl_GetHashValue(hPtr);
244            if (FD_ISSET(serverPtr->listenerFd, &serverFds)) {
245                struct sockaddr_in newaddr;
246                unsigned int addrlen;
247                int f;
248                int child;
249
250                /* Accept the new connection. */
251                unsigned int addrlen = sizeof(newaddr);
252                f = accept(serverPtr->listenerFd, (struct sockaddr *)&newaddr,
253                           sizeof(newaddr));
254                if (f < 0) {
255                    ERROR("can't accept server \"", serverPtr->name,
256                          "\": ", strerror(errno));
257                    continue;
258                }
259                INFO("Connected to %s\n", inet_ntoa(newaddr.sin_addr));
260
261                dispNum++;
262                if (dispNum >= maxCards) {
263                    dispNum = 0;
264                }
265                /* Fork the new process.  Connect I/O to the new socket. */
266                child = fork();
267                if (child < 0) {
268                    ERROR("can't fork \"", serverPtr->name, "\": ",
269                        strerror(errno));
270                    continue;
271                } else if (child == 0) {
272                    int i;
273
274                    /* Child process. */
275                    if (!debug_flag) {
276                        /* Disassociate server from */
277                        status = daemon(0,1);
278                        if (status < 0) {
279                            ERROR("can't daemonize \"", serverPtr->name,
280                                  "\": ", strerror(errno));
281                        }
282                    }                       
283                    dup2(f, 0);         /* Stdin */
284                    dup2(f, 1);         /* Stdout */
285
286                    for(i = 3; i <= FD_SETSIZE; i++) {
287                        close(i);       /* Close all the other descriptors. */
288                    }
289                    if (maxCards > 1) {
290                        displayVar[11] = dispNum + '0';
291                    }
292                    status = execvp(serverPtr->argv[0], serverPtr->argv);
293                    ERROR("can't execute \"", serverPtr->argv[0],
294                          "\": ", strerror(errno));
295                    _exit(1);
296                }
297                _exit(EINVAL);
298            } else {
299                /* Reap initial child which will exit immediately
300                 * (grandchild continues) */
301                waitpid(status, NULL, 0);
302            }
303        }
304    }
305}
306
Note: See TracBrowser for help on using the repository browser.