1 /* 2 * misc os utilities 3 */ 4 /* 5 6 Copyright 1990, 1991, 1998 The Open Group 7 8 Permission to use, copy, modify, distribute, and sell this software and its 9 documentation for any purpose is hereby granted without fee, provided that 10 the above copyright notice appear in all copies and that both that 11 copyright notice and this permission notice appear in supporting 12 documentation. 13 14 The above copyright notice and this permission notice shall be included in 15 all copies or substantial portions of the Software. 16 17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 21 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 24 Except as contained in this notice, the name of The Open Group shall not be 25 used in advertising or otherwise to promote the sale, use or other dealings 26 in this Software without prior written authorization from The Open Group. 27 28 * Copyright 1990, 1991 Network Computing Devices; 29 * Portions Copyright 1987 by Digital Equipment Corporation 30 * 31 * Permission to use, copy, modify, distribute, and sell this software and 32 * its documentation for any purpose is hereby granted without fee, provided 33 * that the above copyright notice appear in all copies and that both that 34 * copyright notice and this permission notice appear in supporting 35 * documentation, and that the names of Network Computing Devices, or Digital 36 * not be used in advertising or publicity pertaining to distribution 37 * of the software without specific, written prior permission. 38 * 39 * NETWORK COMPUTING DEVICES, AND DIGITAL DISCLAIM ALL WARRANTIES WITH 40 * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF 41 * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL NETWORK COMPUTING DEVICES, 42 * OR DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 43 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 44 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 45 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 46 * THIS SOFTWARE. 47 */ 48 49 #include "config.h" 50 51 #include <stdio.h> 52 #include <X11/Xos.h> 53 #include <stdlib.h> 54 #include "misc.h" 55 #include "globals.h" 56 #include <signal.h> 57 #include <sys/wait.h> 58 #include <unistd.h> 59 #include <pwd.h> 60 #include <grp.h> 61 #include <errno.h> 62 #include <sys/types.h> 63 #include <errno.h> 64 #include <string.h> 65 #include "osdep.h" 66 67 #include <stdlib.h> 68 69 static Bool dropPriv = FALSE; /* whether or not to drop root privileges */ 70 #ifdef DEFAULT_DAEMON 71 static Bool becomeDaemon = TRUE; /* whether to become a daemon or not */ 72 #else 73 static Bool becomeDaemon = FALSE; /* whether to become a daemon or not */ 74 #endif 75 #ifdef XFS_INETD 76 static Bool runFromInetd = FALSE; /* whether we were run from inetd or not */ 77 #endif 78 static const char *userId = NULL; 79 char *progname; 80 Bool CloneSelf; 81 Bool portFromCmdline = FALSE; 82 83 OldListenRec *OldListen = NULL; 84 int OldListenCount = 0; 85 86 #ifndef STDERR_FILENO 87 # define STDERR_FILENO fileno(stderr) 88 #endif 89 #define WRITES(s) write(STDERR_FILENO, s, strlen(s)) 90 91 static const char *pidFile = XFSPIDDIR "/xfs.pid"; 92 static int pidFd; 93 static FILE *pidFilePtr; 94 static int StorePid (void); 95 96 /* ARGSUSED */ 97 void 98 AutoResetServer(int n) 99 { 100 int olderrno = errno; 101 102 #ifdef DEBUG 103 WRITES("got a reset signal\n"); 104 #endif 105 106 dispatchException |= DE_RESET; 107 isItTimeToYield = TRUE; 108 109 errno = olderrno; 110 } 111 112 /* ARGSUSED */ 113 void 114 GiveUp(int n) 115 { 116 int olderrno = errno; 117 #ifdef DEBUG 118 WRITES("got a TERM signal\n"); 119 #endif 120 121 dispatchException |= DE_TERMINATE; 122 isItTimeToYield = TRUE; 123 errno = olderrno; 124 } 125 126 /* ARGSUSED */ 127 void 128 ServerReconfig(int n) 129 { 130 int olderrno = errno; 131 132 #ifdef DEBUG 133 WRITES("got a re-config signal\n"); 134 #endif 135 136 dispatchException |= DE_RECONFIG; 137 isItTimeToYield = TRUE; 138 139 errno = olderrno; 140 } 141 142 /* ARGSUSED */ 143 void 144 ServerCacheFlush(int n) 145 { 146 int olderrno = errno; 147 148 #ifdef DEBUG 149 WRITES("got a flush signal\n"); 150 #endif 151 152 dispatchException |= DE_FLUSH; 153 isItTimeToYield = TRUE; 154 155 errno = olderrno; 156 } 157 158 /* ARGSUSED */ 159 void 160 CleanupChild(int n) 161 { 162 int olderrno = errno; 163 pid_t child; 164 165 #ifdef DEBUG 166 WRITES("got a child signal\n"); 167 #endif 168 169 while ( (child = waitpid((pid_t)-1, NULL, WNOHANG)) > 0 ) { 170 #ifdef DEBUG 171 char msgbuf[64]; 172 173 snprintf(msgbuf, sizeof(msgbuf), " child %d exited\n", child); 174 WRITES(msgbuf); 175 #endif 176 } 177 178 errno = olderrno; 179 } 180 181 unsigned int 182 GetTimeInMillis(void) 183 { 184 struct timeval tp; 185 186 X_GETTIMEOFDAY(&tp); 187 return ((tp.tv_sec * 1000) + (tp.tv_usec / 1000)); 188 } 189 190 static void _X_NORETURN 191 usage(const char *errmsg) 192 { 193 if (errmsg != NULL) 194 fprintf (stderr, "%s: %s\n", progname, errmsg); 195 fprintf(stderr, "usage: %s [-config config_file] [-port tcp_port] [-droppriv] [-daemon] [-nodaemon] [-user user_name] [-ls listen_socket]\n" 196 " %s [-version]\n", 197 progname, progname); 198 exit(1); 199 } 200 201 202 /* 203 * The '-ls' option is used for cloning the font server. 204 * 205 * We expect a single string of the form... 206 * 207 * transport_id/fd/portnum[,transport_id/fd/portnum]... 208 * 209 * [] denotes optional and ... denotes repetition. 210 * 211 * The string must be _exactly_ in the above format. 212 */ 213 214 void 215 ProcessLSoption (char *str) 216 { 217 char *ptr = str; 218 char *slash; 219 char number[20]; 220 int count = 0; 221 int len, i; 222 223 if ((OldListenCount != 0) || (OldListen != NULL)) { 224 FatalError("-ls may only be specified once\n"); 225 } 226 227 while (*ptr != '\0') 228 { 229 if (*ptr == ',') 230 count++; 231 ptr++; 232 } 233 234 OldListenCount = count + 1; 235 OldListen = (OldListenRec *) malloc ( 236 OldListenCount * sizeof (OldListenRec)); 237 if (OldListen == NULL) { 238 fprintf(stderr, "ProcessLSoption: malloc error\n"); 239 exit(1); 240 } 241 ptr = str; 242 243 for (i = 0; i < OldListenCount; i++) 244 { 245 slash = (char *) strchr (ptr, '/'); 246 if (slash == NULL) { 247 usage("invalid argument for -ls"); 248 } 249 len = slash - ptr; 250 strncpy (number, ptr, len); 251 number[len] = '\0'; 252 OldListen[i].trans_id = atoi (number); 253 254 ptr = slash + 1; 255 256 slash = (char *) strchr (ptr, '/'); 257 if (slash == NULL) { 258 usage("invalid argument for -ls"); 259 } 260 len = slash - ptr; 261 strncpy (number, ptr, len); 262 number[len] = '\0'; 263 OldListen[i].fd = atoi (number); 264 265 ptr = slash + 1; 266 267 if (i == OldListenCount - 1) 268 OldListen[i].portnum = atoi (ptr); 269 else 270 { 271 char *comma = (char *) strchr (ptr, ','); 272 if (comma == NULL) { 273 usage("invalid argument for -ls"); 274 } 275 len = comma - ptr; 276 strncpy (number, ptr, len); 277 number[len] = '\0'; 278 OldListen[i].portnum = atoi (number); 279 280 ptr = comma + 1; 281 } 282 } 283 } 284 285 286 287 /* ARGSUSED */ 288 void 289 ProcessCmdLine(int argc, char **argv) 290 { 291 int i; 292 293 progname = argv[0]; 294 for (i = 1; i < argc; i++) { 295 if (!strcmp(argv[i], "-port")) { 296 if (argv[i + 1]) { 297 ListenPort = atoi(argv[++i]); 298 portFromCmdline = TRUE; 299 } else 300 usage("-port requires an argument"); 301 } else if (!strcmp(argv[i], "-ls")) { 302 if (argv[i + 1]) 303 ProcessLSoption (argv[++i]); 304 else 305 usage("-ls requires an argument"); 306 } else if (!strcmp(argv[i], "-droppriv")) { 307 dropPriv = TRUE; 308 } else if (!strcmp(argv[i], "-daemon")) { 309 becomeDaemon = TRUE; 310 } else if (!strcmp(argv[i], "-nodaemon")) { 311 becomeDaemon = FALSE; 312 } else if (!strcmp(argv[i], "-inetd")) { 313 #ifdef XFS_INETD 314 runFromInetd = TRUE; 315 #else 316 FatalError("-inetd specified, but xfs was not built" 317 " with inetd support\n"); 318 #endif 319 } else if (!strcmp(argv[i], "-user")) { 320 if (argv[i + 1]) 321 userId = argv[++i]; 322 else 323 usage("-user requires an argument"); 324 } else if (!strcmp(argv[i], "-cf") || !strcmp(argv[i], "-config")) { 325 if (argv[i + 1]) 326 configfilename = argv[++i]; 327 else 328 usage("-config requires an argument"); 329 } 330 else if (!strcmp(argv[i], "-version")) { 331 puts(PACKAGE_STRING); 332 exit(0); 333 } 334 else { 335 fprintf (stderr, "%s: unrecognized option %s\n", progname, argv[i]); 336 usage(NULL); 337 } 338 } 339 } 340 341 342 #ifndef SPECIAL_MALLOC 343 344 unsigned long Must_have_memory; 345 346 347 /* FSalloc -- FS's internal memory allocator. Why does it return unsigned 348 * int * instead of the more common char *? Well, if you read K&R you'll 349 * see they say that alloc must return a pointer "suitable for conversion" 350 * to whatever type you really want. In a full-blown generic allocator 351 * there's no way to solve the alignment problems without potentially 352 * wasting lots of space. But we have a more limited problem. We know 353 * we're only ever returning pointers to structures which will have to 354 * be long word aligned. So we are making a stronger guarantee. It might 355 * have made sense to make FSalloc return char * to conform with people's 356 * expectations of malloc, but this makes lint happier. 357 */ 358 359 pointer 360 FSalloc (unsigned long amount) 361 { 362 register pointer ptr; 363 364 if ((long)amount < 0) 365 return 0; 366 if (amount == 0) 367 amount++; 368 /* aligned extra on long word boundary */ 369 amount = (amount + 3) & ~3; 370 if ((ptr = (pointer)malloc(amount)) != 0) 371 return ptr; 372 if (Must_have_memory) 373 FatalError("out of memory\n"); 374 return 0; 375 } 376 377 /* FSallocarray: Array allocation with overflow check */ 378 379 pointer 380 FSallocarray (unsigned long num, unsigned long size) 381 { 382 if (num != 0 && size != 0) { 383 if (size > (SIZE_MAX / num)) { 384 return NULL; 385 } 386 } 387 return FSalloc(num * size); 388 } 389 390 /***************** 391 * FScalloc 392 *****************/ 393 394 pointer 395 FScalloc (unsigned long num, unsigned long size) 396 { 397 pointer ret; 398 399 ret = FSallocarray(num, size); 400 if (ret) 401 memset (ret, 0, num * size); 402 return ret; 403 } 404 405 /***************** 406 * FSrealloc 407 *****************/ 408 409 pointer 410 FSrealloc (pointer ptr, unsigned long amount) 411 { 412 if ((long)amount <= 0) 413 { 414 if (ptr && !amount) 415 free(ptr); 416 return 0; 417 } 418 amount = (amount + 3) & ~3; 419 if (ptr) 420 ptr = (pointer)realloc((char *)ptr, amount); 421 else 422 ptr = (pointer)malloc(amount); 423 if (ptr) 424 return ptr; 425 if (Must_have_memory) 426 FatalError("out of memory\n"); 427 return 0; 428 } 429 430 /* FSreallocarray: Array reallocation with overflow check */ 431 432 pointer 433 FSreallocarray (pointer ptr, unsigned long num, unsigned long size) 434 { 435 if (num != 0 && size != 0) { 436 if (size > (SIZE_MAX / num)) { 437 return NULL; 438 } 439 } 440 return FSrealloc(ptr, num * size); 441 } 442 443 444 /***************** 445 * FSfree 446 * calls free 447 *****************/ 448 449 void 450 FSfree(pointer ptr) 451 { 452 if (ptr) 453 free((char *)ptr); 454 } 455 456 #endif /* SPECIAL_MALLOC */ 457 458 459 void 460 SetUserId(void) 461 { 462 /* become xfs user (or other specified on command line) if possible */ 463 if ((geteuid() == 0) && (dropPriv || userId)) { 464 const char *user; 465 struct passwd *pwent; 466 467 if (!userId) 468 user = "xfs"; 469 else 470 user = userId; 471 pwent = getpwnam(user); 472 if (pwent) { 473 if (setgid(pwent->pw_gid)) { 474 FatalError("fatal: couldn't set groupid to xfs user's group\n"); 475 } 476 #ifndef __CYGWIN__ 477 if (setgroups(0, NULL)) { 478 FatalError("fatal: couldn't drop supplementary groups\n"); 479 } 480 #endif 481 if (initgroups(user, pwent->pw_gid)) { 482 FatalError("fatal: couldn't init supplementary groups\n"); 483 } 484 if (setuid(pwent->pw_uid)) { 485 FatalError("fatal: couldn't set userid to %s user\n", user); 486 } 487 } 488 } else if (dropPriv || userId) { 489 FatalError("fatal: -droppriv or -user flag specified, but xfs not run as root\n"); 490 } 491 } 492 493 494 void 495 SetDaemonState(void) 496 { 497 int oldpid; 498 499 #ifdef XFS_INETD 500 if (runFromInetd) { 501 int inetdListener; 502 503 /* fd's 0, 1, & 2 are the initial listen socket provided by inetd, 504 * so dup it and then clear them so stdin/out/err aren't in use. 505 */ 506 inetdListener = dup(0); 507 if (inetdListener == -1) { 508 FatalError("failed to dup inetd socket: %s\n", 509 strerror(errno)); 510 } 511 DetachStdio(); 512 513 /* Setup & pass the inetd socket back through the connection setup 514 * code the same way as a cloned listening port 515 */ 516 OldListenCount = 1; 517 OldListen = _FontTransGetInetdListenInfo (inetdListener); 518 if (OldListen == NULL) { 519 FatalError("failed to initialize OldListen to inetd socket: %s\n", 520 strerror(errno)); 521 } 522 ListenPort = OldListen[0].portnum; 523 NoticeF("accepting listener from inetd on fd %d, port %d\n", 524 inetdListener, ListenPort); 525 return; 526 } 527 #endif /* XFS_INETD */ 528 529 if (becomeDaemon) { 530 BecomeDaemon(); 531 if ((oldpid = StorePid ())) { 532 if (oldpid == -1) 533 ErrorF ("error opening process-id file %s\n", pidFile); 534 else 535 ErrorF ("process-id file %s indicates another xfs is " 536 "running (pid %d); exiting\n", pidFile, oldpid); 537 exit(1); 538 } 539 } 540 } 541 542 543 static int 544 StorePid (void) 545 { 546 int oldpid; 547 548 if (pidFile[0] != '\0') { 549 pidFd = open (pidFile, O_RDWR); 550 if (pidFd == -1 && errno == ENOENT) 551 pidFd = open (pidFile, O_RDWR|O_CREAT, 0666); 552 if (pidFd == -1 || !(pidFilePtr = fdopen (pidFd, "r+"))) 553 { 554 ErrorF ("cannot open process-id file %s: %s\n", pidFile, 555 strerror (errno)); 556 return -1; 557 } 558 if (fscanf (pidFilePtr, "%d\n", &oldpid) != 1) 559 oldpid = -1; 560 if (fseek (pidFilePtr, 0L, SEEK_SET) == -1) 561 { 562 ErrorF ("cannot seek process-id file %s: %s\n", pidFile, 563 strerror (errno)); 564 return -1; 565 } 566 if (fprintf (pidFilePtr, "%11ld\n", (long) getpid ()) != 12) 567 { 568 ErrorF ("cannot write to process-id file %s: %s\n", pidFile, 569 strerror (errno)); 570 return -1; 571 } 572 (void) fflush (pidFilePtr); 573 (void) fclose (pidFilePtr); 574 } 575 return 0; 576 } 577 578 static const unsigned char _reverse_byte[0x100] = { 579 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 580 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, 581 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 582 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 583 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 584 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, 585 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 586 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 587 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 588 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 589 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 590 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 591 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 592 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, 593 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 594 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 595 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 596 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 597 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 598 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 599 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 600 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 601 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 602 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 603 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 604 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 605 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 606 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 607 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 608 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 609 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 610 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff 611 }; 612 613 /* 614 * Invert bit order within each BYTE of an array. 615 */ 616 void 617 BitOrderInvert(unsigned char *buf, int nbytes) 618 { 619 const unsigned char *rev = _reverse_byte; 620 621 for (; --nbytes >= 0; buf++) 622 *buf = rev[*buf]; 623 } 624 625 /* 626 * Invert byte order within each 16-bits of an array. 627 */ 628 void 629 TwoByteSwap(unsigned char *buf, int nbytes) 630 { 631 unsigned char c; 632 633 for (; nbytes > 0; nbytes -= 2, buf += 2) 634 { 635 c = buf[0]; 636 buf[0] = buf[1]; 637 buf[1] = c; 638 } 639 } 640 641 /* 642 * Invert byte order within each 32-bits of an array. 643 */ 644 void 645 FourByteSwap(unsigned char *buf, int nbytes) 646 { 647 unsigned char c; 648 649 for (; nbytes > 0; nbytes -= 4, buf += 4) 650 { 651 c = buf[0]; 652 buf[0] = buf[3]; 653 buf[3] = c; 654 c = buf[1]; 655 buf[1] = buf[2]; 656 buf[2] = c; 657 } 658 } 659 660