- Timestamp:
- Jan 14, 2014, 7:53:10 AM (11 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/packages/vizservers/nanoscale/server.c
r4113 r4114 1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* ====================================================================== 3 * Copyright (c) 2006-2014 HUBzero Foundation, LLC 4 * ---------------------------------------------------------------------- 5 * See the file "license.terms" for information on usage and 6 * redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. 7 * ====================================================================== 8 */ 1 9 2 10 #define _GNU_SOURCE … … 98 106 s++; 99 107 } 100 length = snprintf(message, MSG_LEN, "nanoscale (%d %d) %s: %s:%d ", 101 108 length = snprintf(message, MSG_LEN, "nanoscale (%d %d) %s: %s:%d ", 109 serverPid, getpid(), syslogLevels[priority], s, lineNum); 102 110 length += vsnprintf(message + length, MSG_LEN - length, fmt, lst); 103 111 message[MSG_LEN] = '\0'; 104 112 if (debug) { 105 113 fprintf(stderr, "%s\n", message); 106 114 } else { 107 115 syslog(priority, message, length); 108 116 } 109 117 } 110 118 111 static void 119 static void 112 120 Help(const char *program) 113 121 { 114 122 fprintf(stderr, 115 123 "Syntax: %s [-d] [-f serversFile] [-x numVideoCards]\n", program); 116 124 exit(1); 117 125 } … … 125 133 memset(serverPtr, 0, sizeof(RenderServer)); 126 134 if (serverPtr == NULL) { 127 128 129 135 Tcl_AppendResult(interp, "can't allocate structure for \"", 136 name, "\": ", Tcl_PosixError(interp), (char *)NULL); 137 return NULL; 130 138 } 131 139 serverPtr->name = strdup(name); … … 139 147 140 148 static int 141 ParseSwitches(Tcl_Interp *interp, RenderServer *serverPtr, int *objcPtr, 142 149 ParseSwitches(Tcl_Interp *interp, RenderServer *serverPtr, int *objcPtr, 150 Tcl_Obj ***objvPtr) 143 151 { 144 152 int i, objc; … … 148 156 objv = *objvPtr; 149 157 for (i = 3; i < objc; i += 2) { 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 158 const char *string; 159 char c; 160 161 string = Tcl_GetString(objv[i]); 162 if (string[0] != '-') { 163 break; 164 } 165 c = string[1]; 166 if ((c == 'i') && (strcmp(string, "-input") == 0)) { 167 int f; 168 169 if (Tcl_GetIntFromObj(interp, objv[i+1], &f) != TCL_OK) { 170 return TCL_ERROR; 171 } 172 serverPtr->inputFd = 1; 173 } else if ((c == 'o') && (strcmp(string, "-output") == 0)) { 174 int f; 175 176 if (Tcl_GetIntFromObj(interp, objv[i+1], &f) != TCL_OK) { 177 return TCL_ERROR; 178 } 179 serverPtr->outputFd = 1; 180 } else if ((c == 'l') && (strcmp(string, "-logstdout") == 0)) { 181 int state; 182 183 if (Tcl_GetBooleanFromObj(interp, objv[i+1], &state) != TCL_OK) { 184 return TCL_ERROR; 185 } 186 serverPtr->logStdout = state; 187 } else if ((c == 'l') && (strcmp(string, "-logstderr") == 0)) { 188 int state; 189 190 if (Tcl_GetBooleanFromObj(interp, objv[i+1], &state) != TCL_OK) { 191 return TCL_ERROR; 192 } 193 serverPtr->logStderr = state; 194 } else if ((c == 'c') && (strcmp(string, "-combinelogs") == 0)) { 195 int state; 196 197 if (Tcl_GetBooleanFromObj(interp, objv[i+1], &state) != TCL_OK) { 198 return TCL_ERROR; 199 } 200 serverPtr->combineLogs = state; 201 } else { 202 Tcl_AppendResult(interp, "unknown switch \"", string, "\"", 203 (char *)NULL); 204 return TCL_ERROR; 205 } 198 206 } 199 207 *objcPtr = objc - (i - 3); … … 233 241 static int 234 242 RegisterServerCmd(ClientData clientData, Tcl_Interp *interp, int objc, 235 243 Tcl_Obj *const *objv) 236 244 { 237 245 Tcl_Obj *objPtr; … … 248 256 249 257 if (objc < 4) { 250 Tcl_AppendResult(interp, "wrong # args: should be \"", 251 Tcl_GetString(objv[0]), " serverName port ?flags? cmd ?environ?", 252 253 258 Tcl_AppendResult(interp, "wrong # args: should be \"", 259 Tcl_GetString(objv[0]), " serverName port ?flags? cmd ?environ?", 260 (char *)NULL); 261 return TCL_ERROR; 254 262 } 255 263 serverName = Tcl_GetString(objv[1]); 256 264 if (Tcl_GetIntFromObj(interp, objv[2], &port) != TCL_OK) { 257 265 return TCL_ERROR; 258 266 } 259 267 hPtr = Tcl_CreateHashEntry(&serverTable, (char *)((long)port), &isNew); 260 268 if (!isNew) { 261 Tcl_AppendResult(interp, "a server is already listening on port ", 262 263 269 Tcl_AppendResult(interp, "a server is already listening on port ", 270 Tcl_GetString(objv[2]), (char *)NULL); 271 return TCL_ERROR; 264 272 } 265 273 serverPtr = NewServer(interp, serverName); 266 274 if (serverPtr == NULL) { 267 275 return TCL_ERROR; 268 276 } 269 277 Tcl_SetHashValue(hPtr, serverPtr); 270 278 if (ParseSwitches(interp, serverPtr, &objc, (Tcl_Obj ***)&objv) != TCL_OK) { 271 272 } 273 objPtr = Tcl_SubstObj(interp, objv[3], 274 275 if (Tcl_SplitList(interp, Tcl_GetString(objPtr), &numCmdArgs, 276 277 279 goto error; 280 } 281 objPtr = Tcl_SubstObj(interp, objv[3], 282 TCL_SUBST_VARIABLES | TCL_SUBST_BACKSLASHES); 283 if (Tcl_SplitList(interp, Tcl_GetString(objPtr), &numCmdArgs, 284 (const char ***)&cmdArgs) != TCL_OK) { 285 goto error; 278 286 } 279 287 serverPtr->cmdArgs = cmdArgs; 280 288 serverPtr->numCmdArgs = numCmdArgs; 281 289 282 290 numEnvArgs = 0; 283 291 envArgs = NULL; 284 292 if (objc == 5) { 285 objPtr = Tcl_SubstObj(interp, objv[4], 286 287 if (Tcl_SplitList(interp, Tcl_GetString(objPtr), &numEnvArgs, 288 289 290 291 292 Tcl_AppendResult(interp, "odd # elements in enviroment list", 293 294 295 293 objPtr = Tcl_SubstObj(interp, objv[4], 294 TCL_SUBST_VARIABLES | TCL_SUBST_BACKSLASHES); 295 if (Tcl_SplitList(interp, Tcl_GetString(objPtr), &numEnvArgs, 296 (const char ***)&envArgs) != TCL_OK) { 297 goto error; 298 } 299 if (numEnvArgs & 0x1) { 300 Tcl_AppendResult(interp, "odd # elements in enviroment list", 301 (char *)NULL); 302 goto error; 303 } 296 304 } 297 305 serverPtr->envArgs = envArgs; … … 301 309 f = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 302 310 if (f < 0) { 303 304 305 311 Tcl_AppendResult(interp, "can't create listerner socket for \"", 312 serverPtr->name, "\": ", Tcl_PosixError(interp), (char *)NULL); 313 goto error; 306 314 } 307 315 … … 310 318 bool = TRUE; 311 319 if (setsockopt(f, SOL_SOCKET, SO_REUSEADDR, &bool, sizeof(bool)) < 0) { 312 313 314 320 Tcl_AppendResult(interp, "can't create set socket option for \"", 321 serverPtr->name, "\": ", Tcl_PosixError(interp), (char *)NULL); 322 goto error; 315 323 } 316 324 … … 320 328 addr.sin_addr.s_addr = htonl(INADDR_ANY); 321 329 if (bind(f, (struct sockaddr *)&addr, sizeof(addr)) < 0) { 322 323 324 330 Tcl_AppendResult(interp, "can't bind to socket for \"", 331 serverPtr->name, "\": ", Tcl_PosixError(interp), (char *)NULL); 332 goto error; 325 333 } 326 334 /* Listen on the specified port. */ 327 335 if (listen(f, 5) < 0) { 328 329 330 336 Tcl_AppendResult(interp, "can't listen to socket for \"", 337 serverPtr->name, "\": ", Tcl_PosixError(interp), (char *)NULL); 338 return TCL_ERROR; 331 339 } 332 340 serverPtr->listenerFd = f; … … 338 346 } 339 347 340 static int 348 static int 341 349 ParseServersFile(const char *fileName) 342 350 { … … 345 353 interp = Tcl_CreateInterp(); 346 354 Tcl_MakeSafe(interp); 347 Tcl_CreateObjCommand(interp, "register_server", RegisterServerCmd, NULL, 348 355 Tcl_CreateObjCommand(interp, "register_server", RegisterServerCmd, NULL, 356 NULL); 349 357 if (Tcl_EvalFile(interp, fileName) != TCL_OK) { 350 351 358 ERROR("Can't add server: %s", Tcl_GetString(Tcl_GetObjResult(interp))); 359 return FALSE; 352 360 } 353 361 Tcl_DeleteInterp(interp); … … 355 363 } 356 364 357 int 365 int 358 366 main(int argc, char **argv) 359 367 { … … 385 393 /* Process command line switches. */ 386 394 for (;;) { 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 395 int c; 396 int option_index = 0; 397 struct option long_options[] = { 398 // name, has_arg, flag, val 399 { 0,0,0,0 }, 400 }; 401 402 c = getopt_long(argc, argv, "x:f:d", long_options, &option_index); 403 if (c == -1) { 404 break; 405 } 406 407 switch(c) { 408 case 'x': /* Number of video cards */ 409 maxCards = strtoul(optarg, 0, 0); 410 if ((maxCards < 1) || (maxCards > 10)) { 411 fprintf(stderr, "Bad number of max videocards specified\n"); 412 return 1; 413 } 414 break; 415 case 'd': /* Debug */ 416 debug = TRUE; 417 break; 418 419 case 'f': /* Server file path. */ 420 fileName = strdup(optarg); 421 break; 422 423 default: 424 fprintf(stderr,"Don't know what option '%c'.\n", c); 425 Help(argv[0]); 426 exit(1); 427 } 420 428 } 421 429 422 430 if (!debug) { 423 424 425 426 427 428 429 431 /* Detach this process from the controlling terminal process. The 432 * current directory becomes /tmp and redirect stdin/stdout/stderr to 433 * /dev/null. */ 434 if (daemon(0,0) < 0) { 435 ERROR("Can't daemonize nanoscale: %s", strerror(errno)); 436 exit(1); 437 } 430 438 } 431 439 serverPid = getpid(); 432 440 if (!ParseServersFile(fileName)) { 433 434 } 441 exit(1); 442 } 435 443 436 444 if (serverTable.numEntries == 0) { 437 438 445 ERROR("No servers designated."); 446 exit(1); 439 447 } 440 448 signal(SIGPIPE, SIG_IGN); … … 451 459 maxFd = -1; 452 460 for (hPtr = Tcl_FirstHashEntry(&serverTable, &iter); hPtr != NULL; 453 454 455 456 457 458 459 460 461 hPtr = Tcl_NextHashEntry(&iter)) { 462 RenderServer *serverPtr; 463 464 serverPtr = Tcl_GetHashValue(hPtr); 465 FD_SET(serverPtr->listenerFd, &serverFds); 466 if (serverPtr->listenerFd > maxFd) { 467 maxFd = serverPtr->listenerFd; 468 } 461 469 } 462 470 463 471 for (;;) { 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 continue; 483 484 485 486 487 488 489 490 491 472 fd_set readFds; 473 474 /* Reset using the array of server file descriptors. */ 475 memcpy(&readFds, &serverFds, sizeof(serverFds)); 476 if (select(maxFd+1, &readFds, NULL, NULL, 0) <= 0) { 477 ERROR("Select failed: %s", strerror(errno)); 478 break; /* Error on select. */ 479 } 480 for (hPtr = Tcl_FirstHashEntry(&serverTable, &iter); hPtr != NULL; 481 hPtr = Tcl_NextHashEntry(&iter)) { 482 RenderServer *serverPtr; 483 pid_t child; 484 int sock; 485 socklen_t length; 486 struct sockaddr_in newaddr; 487 488 serverPtr = Tcl_GetHashValue(hPtr); 489 if (!FD_ISSET(serverPtr->listenerFd, &readFds)) { 490 continue; 491 } 492 /* Rotate the display's screen number. If we have multiple video 493 * cards, try to spread the jobs out among them. */ 494 screenNum++; 495 if (screenNum >= maxCards) { 496 screenNum = 0; 497 } 498 /* Accept the new connection. */ 499 length = sizeof(newaddr); 492 500 #ifdef HAVE_ACCEPT4 493 sock = accept4(serverPtr->listenerFd, (struct sockaddr *)&newaddr, 494 501 sock = accept4(serverPtr->listenerFd, (struct sockaddr *)&newaddr, 502 &length, SOCK_CLOEXEC); 495 503 #else 496 sock = accept(serverPtr->listenerFd, (struct sockaddr *)&newaddr, 497 504 sock = accept(serverPtr->listenerFd, (struct sockaddr *)&newaddr, 505 &length); 498 506 #endif 499 500 ERROR("Can't accept server \"%s\": %s", serverPtr->name, 501 502 503 507 if (sock < 0) { 508 ERROR("Can't accept server \"%s\": %s", serverPtr->name, 509 strerror(errno)); 510 exit(1); 511 } 504 512 #ifndef HAVE_ACCEPT4 505 506 507 508 ERROR("Can't set FD_CLOEXEC on socket \"%s\": %s", 509 510 511 513 int flags = fcntl(sock, F_GETFD); 514 flags |= FD_CLOEXEC; 515 if (fcntl(sock, F_SETFD, flags) < 0) { 516 ERROR("Can't set FD_CLOEXEC on socket \"%s\": %s", 517 serverPtr->name, strerror(errno)); 518 exit(1); 519 } 512 520 #endif 513 INFO("Connecting \"%s\" to %s\n", serverPtr->name, 514 inet_ntoa(newaddr.sin_addr)); 515 516 /* Fork the new process. Connect I/O to the new socket. */ 517 child = fork(); 518 if (child < 0) { 519 ERROR("Can't fork \"%s\": %s", serverPtr->name, 520 strerror(errno)); 521 continue; 522 } 523 if (child == 0) { /* Child process. */ 524 int i; 525 526 umask(0); 527 if ((!debug) && (setsid() < 0)) { 528 ERROR("Can't setsid \"%s\": %s", serverPtr->name, 529 strerror(errno)); 530 exit(1); 531 } 532 if ((!debug) && ((chdir("/")) < 0)) { 533 ERROR("Can't change to root directory for \"%s\": %s", 534 serverPtr->name, strerror(errno)); 535 exit(1); 536 } 537 if (serverPtr->combineLogs) { 538 char path[BUFSIZ]; 539 int newFd; 540 541 sprintf(path, "%s/%s-%d.log", LOGDIR, 542 serverPtr->name, getpid()); 543 if (serverPtr->logStdout) { 544 newFd = open(path, O_WRONLY | O_CREAT| O_TRUNC, 0600); 545 } else { 546 newFd = open("/dev/null", O_WRONLY, 0600); 547 } 548 if (newFd < 0) { 549 ERROR("%s: can't open \"%s\": %s", serverPtr->name, 550 path, strerror(errno)); 551 exit(1); 552 } 553 if (dup2(newFd, 1) < 0) { 554 ERROR("%s: can't dup stdout to \"%s\": %s", 555 serverPtr->name, path, strerror(errno)); 556 exit(1); 557 } 558 if (dup2(newFd, 2) < 0) { 559 ERROR("%s: can't dup stderr to \"%s\": %s", 560 serverPtr->name, path, strerror(errno)); 561 exit(1); 562 } 563 } else { 564 char path[BUFSIZ]; 565 int newFd; 566 567 sprintf(path, "%s/%s-%d.stdout", LOGDIR, 568 serverPtr->name, getpid()); 569 if (serverPtr->logStdout) { 570 newFd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600); 571 } else { 572 newFd = open("/dev/null", O_WRONLY, 0600); 573 } 574 if (newFd < 0) { 575 ERROR("%s: can't open \"%s\": %s", serverPtr->name, 576 path, strerror(errno)); 577 exit(1); 578 } 579 if (dup2(newFd, 1) < 0) { 580 ERROR("%s: can't dup stdout to \"%s\": %s", 581 serverPtr->name, path, strerror(errno)); 582 exit(1); 583 } 584 sprintf(path, "%s/%s-%d.stderr", LOGDIR, 585 serverPtr->name, getpid()); 586 if (serverPtr->logStderr) { 587 newFd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600); 588 } else { 589 newFd = open("/dev/null", O_WRONLY, 0600); 590 } 591 if (newFd < 0) { 592 ERROR("%s: can't open \"%s\": %s", serverPtr->name, 593 path, strerror(errno)); 594 exit(1); 595 } 596 if (dup2(newFd, 1) < 0) { 597 ERROR("%s: can't dup stderr to \"%s\": %s", 598 serverPtr->name, path, strerror(errno)); 599 exit(1); 600 } 601 } 602 /* Dup the socket to descriptors, normally 3 and 4 */ 603 if (dup2(sock, serverPtr->inputFd) < 0) { /* Stdin */ 604 ERROR("%s: can't dup stdin: %s", serverPtr->name, 605 strerror(errno)); 606 exit(1); 607 } 608 if (dup2(sock, serverPtr->outputFd) < 0) { /* Stdout */ 609 ERROR("%s: can't dup stdout: %s", serverPtr->name, 610 strerror(errno)); 611 exit(1); 612 } 613 for(i = serverPtr->outputFd + 1; i <= FD_SETSIZE; i++) { 614 close(i); /* Close all the other descriptors. */ 615 } 616 617 /* Set the screen number in the DISPLAY variable. */ 618 display[3] = screenNum + '0'; 619 setenv("DISPLAY", display, 1); 620 /* Set the enviroment, if necessary. */ 621 for (i = 0; i < serverPtr->numEnvArgs; i += 2) { 622 setenv(serverPtr->envArgs[i], serverPtr->envArgs[i+1], 1); 623 } 624 INFO("Executing %s: client %s, %s on DISPLAY=%s", 625 serverPtr->name, inet_ntoa(newaddr.sin_addr), 626 serverPtr->cmdArgs[0], display); 627 /* Replace the current process with the render server. */ 628 execvp(serverPtr->cmdArgs[0], serverPtr->cmdArgs); 629 ERROR("Can't execute \"%s\": %s", serverPtr->cmdArgs[0], 630 strerror(errno)); 631 exit(1); 632 } else { 633 close(sock); 634 } 635 } 521 INFO("Connecting \"%s\" to %s\n", serverPtr->name, 522 inet_ntoa(newaddr.sin_addr)); 523 524 /* Fork the new process. Connect I/O to the new socket. */ 525 child = fork(); 526 if (child < 0) { 527 ERROR("Can't fork \"%s\": %s", serverPtr->name, 528 strerror(errno)); 529 continue; 530 } 531 if (child == 0) { /* Child process. */ 532 int i; 533 534 umask(0); 535 if ((!debug) && (setsid() < 0)) { 536 ERROR("Can't setsid \"%s\": %s", serverPtr->name, 537 strerror(errno)); 538 exit(1); 539 } 540 if ((!debug) && ((chdir("/")) < 0)) { 541 ERROR("Can't change to root directory for \"%s\": %s", 542 serverPtr->name, strerror(errno)); 543 exit(1); 544 } 545 if (serverPtr->combineLogs) { 546 char path[BUFSIZ]; 547 int newFd; 548 549 sprintf(path, "%s/%s-%d.log", LOGDIR, 550 serverPtr->name, getpid()); 551 if (serverPtr->logStdout) { 552 newFd = open(path, O_WRONLY | O_CREAT| O_TRUNC, 0600); 553 } else { 554 newFd = open("/dev/null", O_WRONLY, 0600); 555 } 556 if (newFd < 0) { 557 ERROR("%s: can't open \"%s\": %s", serverPtr->name, 558 path, strerror(errno)); 559 exit(1); 560 } 561 if (dup2(newFd, 1) < 0) { 562 ERROR("%s: can't dup stdout to \"%s\": %s", 563 serverPtr->name, path, strerror(errno)); 564 exit(1); 565 } 566 if (dup2(newFd, 2) < 0) { 567 ERROR("%s: can't dup stderr to \"%s\": %s", 568 serverPtr->name, path, strerror(errno)); 569 exit(1); 570 } 571 } else { 572 char path[BUFSIZ]; 573 int newFd; 574 575 sprintf(path, "%s/%s-%d.stdout", LOGDIR, 576 serverPtr->name, getpid()); 577 if (serverPtr->logStdout) { 578 newFd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600); 579 } else { 580 newFd = open("/dev/null", O_WRONLY, 0600); 581 } 582 if (newFd < 0) { 583 ERROR("%s: can't open \"%s\": %s", serverPtr->name, 584 path, strerror(errno)); 585 exit(1); 586 } 587 if (dup2(newFd, 1) < 0) { 588 ERROR("%s: can't dup stdout to \"%s\": %s", 589 serverPtr->name, path, strerror(errno)); 590 exit(1); 591 } 592 sprintf(path, "%s/%s-%d.stderr", LOGDIR, 593 serverPtr->name, getpid()); 594 if (serverPtr->logStderr) { 595 newFd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600); 596 } else { 597 newFd = open("/dev/null", O_WRONLY, 0600); 598 } 599 if (newFd < 0) { 600 ERROR("%s: can't open \"%s\": %s", serverPtr->name, 601 path, strerror(errno)); 602 exit(1); 603 } 604 if (dup2(newFd, 1) < 0) { 605 ERROR("%s: can't dup stderr to \"%s\": %s", 606 serverPtr->name, path, strerror(errno)); 607 exit(1); 608 } 609 } 610 /* Dup the socket to descriptors, normally 3 and 4 */ 611 if (dup2(sock, serverPtr->inputFd) < 0) { /* input */ 612 ERROR("%s: can't dup socket to fd %d: %s", 613 serverPtr->name, serverPtr->inputFd, 614 strerror(errno)); 615 exit(1); 616 } 617 if (serverPtr->inputFd != serverPtr->outputFd) { 618 if (dup2(sock, serverPtr->outputFd) < 0) { /* output */ 619 ERROR("%s: can't dup socket to fd %d: %s", 620 serverPtr->name, serverPtr->outputFd, 621 strerror(errno)); 622 exit(1); 623 } 624 } 625 for (i = serverPtr->outputFd + 1; i <= FD_SETSIZE; i++) { 626 close(i); /* Close all the other descriptors. */ 627 } 628 629 /* Set the screen number in the DISPLAY variable. */ 630 display[3] = screenNum + '0'; 631 setenv("DISPLAY", display, 1); 632 /* Set the enviroment, if necessary. */ 633 for (i = 0; i < serverPtr->numEnvArgs; i += 2) { 634 setenv(serverPtr->envArgs[i], serverPtr->envArgs[i+1], 1); 635 } 636 INFO("Executing %s: client %s, %s on DISPLAY=%s", 637 serverPtr->name, inet_ntoa(newaddr.sin_addr), 638 serverPtr->cmdArgs[0], display); 639 /* Replace the current process with the render server. */ 640 execvp(serverPtr->cmdArgs[0], serverPtr->cmdArgs); 641 ERROR("Can't execute \"%s\": %s", serverPtr->cmdArgs[0], 642 strerror(errno)); 643 exit(1); 644 } else { 645 close(sock); 646 } 647 } 636 648 } 637 649 exit(1);
Note: See TracChangeset
for help on using the changeset viewer.