1 /* $OpenBSD: hack.main.c,v 1.24 2019/06/28 13:32:52 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica, 5 * Amsterdam 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions are 10 * met: 11 * 12 * - Redistributions of source code must retain the above copyright notice, 13 * this list of conditions and the following disclaimer. 14 * 15 * - Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * - Neither the name of the Stichting Centrum voor Wiskunde en 20 * Informatica, nor the names of its contributors may be used to endorse or 21 * promote products derived from this software without specific prior 22 * written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 25 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 27 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 28 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 30 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 31 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 33 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 34 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37 /* 38 * Copyright (c) 1982 Jay Fenlason <hack@gnu.org> 39 * All rights reserved. 40 * 41 * Redistribution and use in source and binary forms, with or without 42 * modification, are permitted provided that the following conditions 43 * are met: 44 * 1. Redistributions of source code must retain the above copyright 45 * notice, this list of conditions and the following disclaimer. 46 * 2. Redistributions in binary form must reproduce the above copyright 47 * notice, this list of conditions and the following disclaimer in the 48 * documentation and/or other materials provided with the distribution. 49 * 3. The name of the author may not be used to endorse or promote products 50 * derived from this software without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 53 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 54 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 55 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 56 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 57 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 58 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 59 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 60 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 61 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 62 */ 63 64 #include <sys/stat.h> 65 #include <errno.h> 66 67 #include <stdio.h> 68 #include <stdlib.h> 69 #include <signal.h> 70 #include <unistd.h> 71 72 #include "hack.h" 73 74 #ifdef QUEST 75 #define gamename "quest" 76 #else 77 #define gamename "hack" 78 #endif 79 80 extern char plname[PL_NSIZ], pl_character[PL_CSIZ]; 81 extern struct permonst mons[CMNUM+2]; 82 extern char genocided[60], fut_geno[60]; 83 84 void (*afternmv)(void); 85 int (*occupation)(void); 86 char *occtxt; /* defined when occupation != NULL */ 87 88 int hackpid; /* current pid */ 89 int locknum; /* max num of players */ 90 #ifdef DEF_PAGER 91 char *catmore; /* default pager */ 92 #endif 93 char SAVEF[PL_NSIZ + 11] = "save/"; /* save/99999player */ 94 char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */ 95 96 extern char *nomovemsg; 97 extern long wailmsg; 98 99 #ifdef CHDIR 100 static void chdirx(char *, boolean); 101 #endif 102 103 int 104 main(int argc, char **argv) 105 { 106 extern char *__progname; 107 int fd; 108 #ifdef CHDIR 109 char *dir; 110 #endif 111 112 hackpid = getpid(); 113 114 #ifdef CHDIR /* otherwise no chdir() */ 115 /* 116 * See if we must change directory to the playground. 117 * (Perhaps hack runs suid and playground is inaccessible 118 * for the player.) 119 * The environment variable HACKDIR is overridden by a 120 * -d command line option (must be the first option given) 121 */ 122 123 dir = getenv("HACKDIR"); 124 if(argc > 1 && !strncmp(argv[1], "-d", 2)) { 125 argc--; 126 argv++; 127 dir = argv[0]+2; 128 if(*dir == '=' || *dir == ':') dir++; 129 if(!*dir && argc > 1) { 130 argc--; 131 argv++; 132 dir = argv[0]; 133 } 134 if(!*dir) 135 error("Flag -d must be followed by a directory name."); 136 } 137 #endif 138 139 /* 140 * Who am i? Algorithm: 1. Use name as specified in HACKOPTIONS 141 * 2. Use $LOGNAME or $USER (if 1. fails) 142 * 3. Use getlogin() (if 2. fails) 143 * The resulting name is overridden by command line options. 144 * If everything fails, or if the resulting name is some generic 145 * account like "games", "play", "player", "hack" then eventually 146 * we'll ask him. 147 * Note that we trust him here; it is possible to play under 148 * somebody else's name. 149 */ 150 { char *s; 151 152 initoptions(); 153 if(!*plname && (s = getenv("LOGNAME"))) 154 (void) strlcpy(plname, s, sizeof(plname)); 155 if(!*plname && (s = getenv("USER"))) 156 (void) strlcpy(plname, s, sizeof(plname)); 157 if(!*plname && (s = getlogin())) 158 (void) strlcpy(plname, s, sizeof(plname)); 159 } 160 161 /* 162 * Now we know the directory containing 'record' and 163 * may do a prscore(). 164 */ 165 if(argc > 1 && !strncmp(argv[1], "-s", 2)) { 166 #ifdef CHDIR 167 chdirx(dir,0); 168 #endif 169 prscore(argc, argv); 170 return 0; 171 } 172 173 /* 174 * It seems he really wants to play. 175 * Remember tty modes, to be restored on exit. 176 */ 177 gettty(); 178 setvbuf(stdout, obuf, _IOFBF, sizeof obuf); 179 umask(007); 180 startup(); 181 cls(); 182 u.uhp = 1; /* prevent RIP on early quits */ 183 u.ux = FAR; /* prevent nscr() */ 184 (void) signal(SIGHUP, hackhangup); 185 186 /* 187 * Find the creation date of this game, 188 * so as to avoid restoring outdated savefiles. 189 */ 190 gethdate(__progname); 191 192 /* 193 * We cannot do chdir earlier, otherwise gethdate will fail. 194 */ 195 #ifdef CHDIR 196 chdirx(dir,1); 197 #endif 198 199 /* 200 * Process options. 201 */ 202 while(argc > 1 && argv[1][0] == '-'){ 203 argv++; 204 argc--; 205 switch(argv[0][1]){ 206 #ifdef WIZARD 207 case 'D': 208 /* if(!strcmp(getlogin(), WIZARD)) */ 209 wizard = TRUE; 210 /* else 211 printf("Sorry.\n"); */ 212 break; 213 #endif 214 #ifdef NEWS 215 case 'n': 216 flags.nonews = TRUE; 217 break; 218 #endif 219 case 'u': 220 if(argv[0][2]) { 221 (void) strlcpy(plname, argv[0]+2, sizeof(plname)); 222 } else if(argc > 1) { 223 argc--; 224 argv++; 225 (void) strlcpy(plname, argv[0], sizeof(plname)); 226 } else 227 printf("Player name expected after -u\n"); 228 break; 229 default: 230 /* allow -T for Tourist, etc. */ 231 (void) strlcpy(pl_character, argv[0]+1, sizeof(pl_character)); 232 /* printf("Unknown option: %s\n", *argv); */ 233 } 234 } 235 236 if(argc > 1) 237 locknum = atoi(argv[1]); 238 #ifdef MAX_NR_OF_PLAYERS 239 if(!locknum || locknum > MAX_NR_OF_PLAYERS) 240 locknum = MAX_NR_OF_PLAYERS; 241 #endif 242 #ifdef DEF_PAGER 243 if(!(catmore = getenv("HACKPAGER")) && !(catmore = getenv("PAGER"))) 244 catmore = DEF_PAGER; 245 #endif 246 #ifdef MAIL 247 getmailstatus(); 248 #endif 249 #ifdef WIZARD 250 if(wizard) (void) strlcpy(plname, "wizard", sizeof plname); else 251 #endif 252 if(!*plname || !strncmp(plname, "player", 4) 253 || !strncmp(plname, "games", 4)) 254 askname(); 255 plnamesuffix(); /* strip suffix from name; calls askname() */ 256 /* again if suffix was whole name */ 257 /* accepts any suffix */ 258 #ifdef WIZARD 259 if(!wizard) { 260 #endif 261 /* 262 * check for multiple games under the same name 263 * (if !locknum) or check max nr of players (otherwise) 264 */ 265 (void) signal(SIGQUIT,SIG_IGN); 266 (void) signal(SIGINT,SIG_IGN); 267 if(!locknum) 268 (void) strlcpy(lock,plname,sizeof lock); 269 getlock(); /* sets lock if locknum != 0 */ 270 #ifdef WIZARD 271 } else { 272 char *sfoo; 273 (void) strlcpy(lock,plname,sizeof lock); 274 if ((sfoo = getenv("MAGIC"))) 275 while(*sfoo) { 276 switch(*sfoo++) { 277 case 'n': (void) srandom_deterministic(*sfoo++); 278 break; 279 } 280 } 281 if ((sfoo = getenv("GENOCIDED"))) { 282 if(*sfoo == '!'){ 283 struct permonst *pm = mons; 284 char *gp = genocided; 285 286 while(pm < mons+CMNUM+2){ 287 if(!strchr(sfoo, pm->mlet)) 288 *gp++ = pm->mlet; 289 pm++; 290 } 291 *gp = 0; 292 } else 293 strlcpy(genocided, sfoo, sizeof genocided); 294 strlcpy(fut_geno, genocided, sizeof fut_geno); 295 } 296 } 297 #endif 298 setftty(); 299 (void) snprintf(SAVEF, sizeof SAVEF, "save/%u%s", getuid(), plname); 300 regularize(SAVEF+5); /* avoid . or / in name */ 301 if((fd = open(SAVEF, O_RDONLY)) >= 0 && 302 (uptodate(fd) || unlink(SAVEF) == 666)) { 303 (void) signal(SIGINT,done1); 304 pline("Restoring old save file..."); 305 (void) fflush(stdout); 306 if(!dorecover(fd)) 307 goto not_recovered; 308 pline("Hello %s, welcome to %s!", plname, gamename); 309 flags.move = 0; 310 } else { 311 not_recovered: 312 fobj = fcobj = invent = 0; 313 fmon = fallen_down = 0; 314 ftrap = 0; 315 fgold = 0; 316 flags.ident = 1; 317 init_objects(); 318 u_init(); 319 320 (void) signal(SIGINT,done1); 321 mklev(); 322 u.ux = xupstair; 323 u.uy = yupstair; 324 (void) inshop(); 325 setsee(); 326 flags.botlx = 1; 327 makedog(); 328 { struct monst *mtmp; 329 if ((mtmp = m_at(u.ux, u.uy))) 330 mnexto(mtmp); /* riv05!a3 */ 331 } 332 seemons(); 333 #ifdef NEWS 334 if(flags.nonews || !readnews()) 335 /* after reading news we did docrt() already */ 336 #endif 337 docrt(); 338 339 /* give welcome message before pickup messages */ 340 pline("Hello %s, welcome to %s!", plname, gamename); 341 342 pickup(1); 343 read_engr_at(u.ux,u.uy); 344 flags.move = 1; 345 } 346 347 flags.moonphase = phase_of_the_moon(); 348 if(flags.moonphase == FULL_MOON) { 349 pline("You are lucky! Full moon tonight."); 350 u.uluck++; 351 } else if(flags.moonphase == NEW_MOON) { 352 pline("Be careful! New moon tonight."); 353 } 354 355 initrack(); 356 357 for(;;) { 358 if(flags.move) { /* actual time passed */ 359 360 settrack(); 361 362 if(moves%2 == 0 || 363 (!(Fast & ~INTRINSIC) && (!Fast || rn2(3)))) { 364 extern struct monst *makemon(); 365 movemon(); 366 if(!rn2(70)) 367 (void) makemon((struct permonst *)0, 0, 0); 368 } 369 if(Glib) glibr(); 370 hacktimeout(); 371 ++moves; 372 if(flags.time) flags.botl = 1; 373 if(u.uhp < 1) { 374 pline("You die..."); 375 done("died"); 376 } 377 if(u.uhp*10 < u.uhpmax && moves-wailmsg > 50){ 378 wailmsg = moves; 379 if(u.uhp == 1) 380 pline("You hear the wailing of the Banshee..."); 381 else 382 pline("You hear the howling of the CwnAnnwn..."); 383 } 384 if(u.uhp < u.uhpmax) { 385 if(u.ulevel > 9) { 386 if(Regeneration || !(moves%3)) { 387 flags.botl = 1; 388 u.uhp += rnd((int) u.ulevel-9); 389 if(u.uhp > u.uhpmax) 390 u.uhp = u.uhpmax; 391 } 392 } else if(Regeneration || 393 (!(moves%(22-u.ulevel*2)))) { 394 flags.botl = 1; 395 u.uhp++; 396 } 397 } 398 if(Teleportation && !rn2(85)) tele(); 399 if(Searching && multi >= 0) (void) dosearch(); 400 gethungry(); 401 invault(); 402 amulet(); 403 } 404 if(multi < 0) { 405 if(!++multi){ 406 pline("%s", nomovemsg ? nomovemsg : 407 "You can move again."); 408 nomovemsg = 0; 409 if(afternmv) (*afternmv)(); 410 afternmv = 0; 411 } 412 } 413 414 find_ac(); 415 #ifndef QUEST 416 if(!flags.mv || Blind) 417 #endif 418 { 419 seeobjs(); 420 seemons(); 421 nscr(); 422 } 423 if(flags.botl || flags.botlx) bot(); 424 425 flags.move = 1; 426 427 if(multi >= 0 && occupation) { 428 if(monster_nearby()) 429 stop_occupation(); 430 else if ((*occupation)() == 0) 431 occupation = 0; 432 continue; 433 } 434 435 if(multi > 0) { 436 #ifdef QUEST 437 if(flags.run >= 4) finddir(); 438 #endif 439 lookaround(); 440 if(!multi) { /* lookaround may clear multi */ 441 flags.move = 0; 442 continue; 443 } 444 if(flags.mv) { 445 if(multi < COLNO && !--multi) 446 flags.mv = flags.run = 0; 447 domove(); 448 } else { 449 --multi; 450 rhack(save_cm); 451 } 452 } else if(multi == 0) { 453 #ifdef MAIL 454 ckmailstatus(); 455 #endif 456 rhack(NULL); 457 } 458 if(multi && multi%7 == 0) 459 (void) fflush(stdout); 460 } 461 } 462 463 void 464 glo(int foo) 465 { 466 /* construct the string xlock.n */ 467 char *tf; 468 469 tf = lock; 470 while(*tf && *tf != '.') tf++; 471 (void) snprintf(tf, lock + sizeof lock - tf, ".%d", foo); 472 } 473 474 /* 475 * plname is filled either by an option (-u Player or -uPlayer) or 476 * explicitly (-w implies wizard) or by askname. 477 * It may still contain a suffix denoting pl_character. 478 */ 479 void 480 askname(void) 481 { 482 int c,ct; 483 484 printf("\nWho are you? "); 485 (void) fflush(stdout); 486 ct = 0; 487 while((c = getchar()) != '\n'){ 488 if(c == EOF) error("End of input\n"); 489 /* some people get confused when their erase char is not ^H */ 490 if(c == '\010') { 491 if(ct) ct--; 492 continue; 493 } 494 if(c != '-') 495 if(c < 'A' || (c > 'Z' && c < 'a') || c > 'z') c = '_'; 496 if(ct < sizeof(plname)-1) plname[ct++] = c; 497 } 498 plname[ct] = 0; 499 if(ct == 0) askname(); 500 } 501 502 void 503 impossible(const char *s, ...) 504 { 505 va_list ap; 506 507 va_start(ap, s); 508 vpline(s, ap); 509 va_end(ap); 510 pline("Program in disorder - perhaps you'd better Quit."); 511 } 512 513 #ifdef CHDIR 514 static void 515 chdirx(char *dir, boolean wr) 516 { 517 gid_t gid; 518 519 #ifdef SECURE 520 if(dir /* User specified directory? */ 521 #ifdef HACKDIR 522 && strcmp(dir, HACKDIR) /* and not the default? */ 523 #endif 524 ) { 525 /* revoke privs */ 526 gid = getgid(); 527 setresgid(gid, gid, gid); 528 } 529 #endif 530 531 #ifdef HACKDIR 532 if(dir == NULL) 533 dir = HACKDIR; 534 #endif 535 536 if(dir && chdir(dir) == -1) { 537 perror(dir); 538 error("Cannot chdir to %s.", dir); 539 } 540 541 /* warn the player if he cannot write the record file */ 542 /* warn the player if he cannot read the permanent lock file */ 543 /* warn the player if he cannot create the save directory */ 544 /* perhaps we should also test whether . is writable */ 545 /* unfortunately the access systemcall is worthless */ 546 if(wr) { 547 int fd; 548 549 if(dir == NULL) 550 dir = "."; 551 if((fd = open(RECORD, O_RDWR | O_CREAT, FMASK)) == -1) { 552 printf("Warning: cannot write %s/%s", dir, RECORD); 553 getret(); 554 } else 555 (void) close(fd); 556 if((fd = open(HLOCK, O_RDONLY | O_CREAT, FMASK)) == -1) { 557 printf("Warning: cannot read %s/%s", dir, HLOCK); 558 getret(); 559 } else 560 (void) close(fd); 561 if(mkdir("save", 0770) && errno != EEXIST) { 562 printf("Warning: cannot create %s/save", dir); 563 getret(); 564 } 565 } 566 } 567 #endif 568 569 void 570 stop_occupation(void) 571 { 572 if(occupation) { 573 pline("You stop %s.", occtxt); 574 occupation = 0; 575 } 576 } 577