blob: 3d8892826c3e13ae8a01e80581b234539207ffcd [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// master.c -- master object
4//
5// $Id: master.c 9530 2016-03-17 19:59:01Z Zesstra $
6#pragma strict_types
7#pragma no_clone
8#pragma no_shadow
9#pragma no_inherit
10#pragma verbose_errors
11#pragma combine_strings
12#pragma pedantic
13#pragma range_check
14#pragma warn_deprecated
15
16#include "/sys/files.h"
17#include "/sys/interactive_info.h"
18#include "/sys/driver_info.h"
19
20inherit "/secure/master/misc";
21inherit "/secure/master/userinfo";
22inherit "/secure/master/network";
23inherit "/secure/master/domain";
24inherit "/secure/master/guild";
25inherit "/secure/master/file_access";
26inherit "/secure/master/players_deny";
27
28#include "/secure/master.h"
29
30//for limited, set_limit(), etc. Abs. Pfad noetig, da noch kein Include-Hook
31#include "/sys/rtlimits.h"
32#include "/sys/debug_info.h"
33#include "/sys/debug_message.h"
34#include "/sys/configuration.h"
35#include "/sys/regexp.h"
36
37// Module des Master einfuegen. Per #include, damits geringfuegig schneller.
38// Da die Module ja nirgendwo sonst geerbt werden koennten, ist dies
39// ausnahmsweise OK. ;)
40#include "/secure/master/destruct.c"
41#include "/secure/master/autoinclude.c"
42
43// Fuer Logfile(-Rotation)
44#define RESETINT 3600 // jede Stunde
45#define LOGROTATE (24*2) // alle 48h
46#define LOGFILEAGE (RESETINT*LOGROTATE) // alle 2 tage wechseln
47#define LOGNUM 3 // Anzahl der Logfiles
48
49private nosave int logrotate_counter; //READ_FILE alle LOGROTATE Resets rotieren
50// Wird gerade versucht, die simul_efuns zu laden? Wenn ja, duerfen keine
51// sefuns gerufen werden.
52private nosave int loading_simul_efuns;
53// wird gerade ein Fehler bearbeitet? Dient zur Erkennung von Rekursionen in
54// der Fehlerbehandlung
55private nosave int handling_error;
56
57// #####################
58//######################## Start des Masters ############################
59// #####################
60
61// Initialisierung des Masters
62//arg==0: Mud startet gerade, arg==1: Master wurde reaktiviert,
63//arg==2: Master reaktiviert (Variablen weg), arg==3: Master wurde
64//neugeladen.
65protected void inaugurate_master(int arg) {
66
67 set_driver_hook(H_REGEXP_PACKAGE, RE_TRADITIONAL);
68
Zesstra86607bb2017-06-17 19:02:40 +020069 efun::configure_object(this_object(), OC_EUID, ROOTID);
MG Mud User88f12472016-06-24 23:31:02 +020070 userinfo::create();
71 LoadPLDenylists();
72
73 // Was soll vor jede Datei geschrieben werden?
74 set_driver_hook(H_AUTO_INCLUDE, #'autoincludehook);
75
76 //div. Listen einlesen
77 ReloadBanishFile();
78 ReloadDeputyFile();
79 ReloadInsecureFile();
80
81 // Bei Neustart wizinfo initialisieren
82 if (!arg)
83 set_extra_wizinfo(0, allocate(BACKBONE_WIZINFO_SIZE));
84
85 //simul_efun.c nach inaugurate_master() starten und initialisieren,
86 //(so richtig seh ich den Sinn hier momentan nicht ein...
87 //call_out("start_simul_efun",0);
88
89 // Reset festsetzen (1h)
90 set_next_reset(RESETINT);
91
92 // Driver konfigurieren
93 // Lagerkennung erst ne Minute spaeter, waehrend preload darfs momentan
94 // ruhig laggen (Zeit: vorlaeufig 1min)
95 call_out(#'configure_driver, 60, DC_LONG_EXEC_TIME, 100000);
96
97 // Hooks setzen
98
99 // Standardincludeverzeichnisse fuer #include <>
100 set_driver_hook(H_INCLUDE_DIRS, ({"/secure/","/sys/"}) );
101
102 //Nach dem Laden/Clonen create() im Objekt rufen
103 set_driver_hook(H_CREATE_CLONE, "create");
104 set_driver_hook(H_CREATE_OB, "create");
105 set_driver_hook(H_CREATE_SUPER, "create_super");
106
107 // Bei Reset reset() im Objekt aufrufen
108 set_driver_hook(H_RESET, "reset");
109
110 // Zum Aufraeumen clean_up() im Objekt aufrufen
111 set_driver_hook(H_CLEAN_UP, "clean_up");
112
113 // Jede Eingabe wird ueber modify_command gelenkt
114 set_driver_hook(H_MODIFY_COMMAND, "modify_command");
115 // Dieser Hook ist mandatory, aber wird nur benutzt, wenn via
116 // set_modify_command() aktiviert - was wir nicht (mehr) tun. Insofern ist
117 // das Relikt aus alten Zeiten.
118 //set_driver_hook(H_MODIFY_COMMAND_FNAME, "modify_command");
119
120 // Standard-Fehlermeldung
121 //set_driver_hook(H_NOTIFY_FAIL, "Wie bitte?\n");
122 set_driver_hook(H_NOTIFY_FAIL, function string (string cmd, object tp)
Zesstra2bb35892016-12-20 20:56:35 +0100123 {if (tp && stringp(cmd=(string)tp->QueryProp(P_DEFAULT_NOTIFY_FAIL)))
MG Mud User88f12472016-06-24 23:31:02 +0200124 return(cmd);
125 return("Wie bitte?\n");});
126
127 // Was machen bei telnet_neg
128 set_driver_hook(H_TELNET_NEG,"telnet_neg");
129 // set_driver_hook(H_TELNET_NEG,0);
130
131 // Promptbehandlung: Defaultprompt setzen und dafuer sorgen, dass alle
132 // Promptausgaben durch print_prompt im Interactive laufen, damit das EOR
133 // angehaengt wird.
134 set_driver_hook(H_PRINT_PROMPT, "print_prompt");
135 set_driver_hook(H_DEFAULT_PROMPT, "> ");
136
137 // EUID und UID muessen neue Objekte auch noch kriegen, Hooks dafuer setzen
138 set_driver_hook(H_LOAD_UIDS, #'load_uid_hook);
139 set_driver_hook(H_CLONE_UIDS, #'clone_uid_hook);
140
141 // Meldung bei vollem Mud
142 set_driver_hook(H_NO_IPC_SLOT, "Momentan sind leider zuviele Spieler im "
143 +MUDNAME+" eingeloggt.\nBitte komm doch etwas spaeter nochmal "
144 "vorbei!\n");
145
146 // Nun der beruechtigte Move-Hook ...
147 // (bewegt objekt und ruft alle notwendigen inits auf)
148 // Hier wird H_MOVE_OBJECT0 benutzt, d.h. die lambda wird an das aktuelle
149 // Objekt vor Ausfuehrung gebunden, nicht an 'item', was move_objet()
150 // uebergeben wurde.
151 set_driver_hook(H_MOVE_OBJECT0,
152 unbound_lambda(({'item,'dest}),
153 ({#',, // item!=this_object?
154 ({#'?,({#'!=,'item,({#'this_object})}),
155 ({#'raise_error, // ->Fehler!
156 "Illegal to move other object than this_object()\n"}) }),
157 ({#'efun::set_environment,'item,'dest}), // item nach dest bewegen
158 ({#'?,
159 ({#'living,'item}), // living(item)?
160 ({#',,
161 ({#'efun::set_this_player,'item}), // set_this_player(item)
162 ({#'call_other,'dest,"init"}), // dest->init()
163 ({#'?!,
164 ({#'&&, // !objectp(item)||
165 ({#'objectp, 'item}), // env(item)!=dest?
166 ({#'==,({#'environment, 'item}),'dest})}),
167 ({#'return})})})}), // -> fertig
168 ({#'=,'others,({#'-,({#'all_inventory,'dest}),({#'({,'item})})}),
169#ifdef EMACS_NERV
170 }) // Emacs kann ({#'({ nicht parsen // others=all_inv(dest)-
171#endif // ({item})
172 ({#'filter,'others,lambda(({'ob,'item}),
173 ({#'?,
174 ({#'&&,
175 ({#'objectp,'item}), // objectp(item)&&
176 ({#'living,'ob}), // living(ob)&&
177 ({#'==,({#'environment,'item}),({#'environment,'ob})})}),
178 ({#',, // env(item)==env(ob)?
179 ({#'efun::set_this_player, 'ob}), // set_this_player(ob)
180 ({#'call_other,'item, "init"}) // item->init()
181 })})),'item}),
182 ({#'?,
183 ({#'living,'item}), // living(item)?
184 ({#',,
185 ({#'efun::set_this_player,'item}), // set_this_player(item)
186 ({#'filter,'others,lambda(({'ob,'item}),
187 ({#'?,
188 ({#'&&,
189 ({#'objectp,'item}), // objectp(item)&&
190 ({#'objectp,'ob}), // objectp(ob)&&
191 ({#'==,({#'environment,'item}),({#'environment,'ob})})
192 }), // env(item)==env(ob)?
193 ({#'call_other,'ob,"init"}) // ob->init()
194 })),'item})})}),
195 ({#'?,
196 ({#'&&,
197 ({#'objectp,'item}), // objectp(item)&&
198 ({#'living,'dest}), // living(dest)&&
199 ({#'==,({#'environment,'item}),'dest})// env(item)==dest?
200 }),
201 ({#',,
202 ({#'efun::set_this_player,'dest}), // set_this_player(dest)
203 ({#'call_other,'item,"init"})})})})));// item->init()
204 DEBUG("Master inaugurated");
205 return;
206}
207
208// epilog() gibt die Dateien zurueck, die vorgeladen werden muessen
209protected string *epilog(int eflag) {
210 string *files, *domains;
211 int i;
212
Zesstra86607bb2017-06-17 19:02:40 +0200213 efun::configure_object(this_object(), OC_EUID, ROOTID);
MG Mud User88f12472016-06-24 23:31:02 +0200214 ReloadBanishFile();
215 ReloadDeputyFile();
216 ReloadInsecureFile();
217
218 if (eflag) {
219 write("-e angegeben -> Preloading unterdrueckt ...\n");
220 return 0;
221 }
222
223 printf("Preloading gestartet: %s\n\n",ctime(time()));
224
225 //Files fuers Preloading und Regionen holen
226 files=explode_files("/std/preload_file") + explode_files("/d/preload_file");
227 domains=get_domains() + ({"erzmagier"}); // EM haben auch ein Preload File
228
229 for (i=sizeof(domains);i--;)
230 files+=explode_files("/d/"+domains[i]+"/preload_file");
231
232 write("\n");
233
234 return files;
235}
236
237// preload() wird fuer jeder Datei im Preload einmal durchlaufen
238protected void preload(string file) {
239 string err;
240 string name;
241 int *res, zeit;
242
243 // Kein richtiger Dateiname: fertig
244 if(!file || !file[0] || file[0] == ';' || file_size(file+".c") < 0) return;
245
246 // Kein Besitzer -> Meldung ausgeben, fertig
247 if (!(name=creator_file(file)))
248 {
249 printf("Kein Besitzer gefunden fuer Datei %s.\n",file);
250 return;
251 }
252
253 // Datei und Besitzer ausgeben
254 printf("%-50s%-15s",file,name);
255
Zesstra86607bb2017-06-17 19:02:40 +0200256 efun::configure_object(this_object(), OC_EUID, name);
MG Mud User88f12472016-06-24 23:31:02 +0200257
258 // Dann mal laden .. dabei Zeit messen
259 zeit = apply(#'+,rusage()[0..1]);
260
261 // Waehrend des Preloads sind 1.5MTicks leider nicht soviel, insb. wenn
262 // sowas wie der channeld jede menge Objekte laden muss, die wiederum erst
263 // das ganze Geraffel aus /std/ laden. Daher hier einfach mal fuers Preload
264 // die Grenze hoch.
265 err = catch(limited(#'load_object,({5000000}),file));
266
267 if (err != 0)
268 printf("\nFehler beim Laden von %s:\n%s\n",file,err);
269 else
270 {
271 zeit=apply(#'+,rusage()[0..1])-zeit;
272 printf("(%2d.%:02d s)\n",zeit/1000,zeit%1000);
273 }
274
275 // Noch EUID zuruecksetzen
Zesstra86607bb2017-06-17 19:02:40 +0200276 efun::configure_object(this_object(), OC_EUID, ROOTID);
MG Mud User88f12472016-06-24 23:31:02 +0200277
278 return;
279}
280
281//simul_efun.c laden oder die spare_simul_efun.c als Fallback
282//es ist moeglich, hier ein Array von strings zurueckzugeben. Die
283//nachfolgenden gelten als Backup-Objekte, falls eine sefun im Hauptobjekt
284//nicht gefunden wird.
285protected string *get_simul_efun() {
286 string err;
287
288 ++loading_simul_efuns;
289
290 if (!(err=catch(SIMUL_EFUN_FILE->start_simul_efun())) ) {
291 --loading_simul_efuns;
292 return ({SIMUL_EFUN_FILE});
293 }
294
295 write("Failed to load simul efun " + SIMUL_EFUN_FILE + "\n");
296 debug_message("Failed to load simul efun " + SIMUL_EFUN_FILE +
297 " " + err, DMSG_STDOUT | DMSG_LOGFILE);
298
299 if (!(err=catch(SPARE_SIMUL_EFUN_FILE->start_simul_efun())) ) {
300 --loading_simul_efuns;
301 return ({SPARE_SIMUL_EFUN_FILE});
302 }
303
304 write("Failed to load spare simul efun" + SPARE_SIMUL_EFUN_FILE + "\n");
305 debug_message("Failed to load spare simul efun " + SPARE_SIMUL_EFUN_FILE +
306 " " + err, DMSG_STDOUT | DMSG_LOGFILE);
307
308
309 efun::shutdown();
310
311 return 0;
312}
313
314protected mixed call_sefun(string sefun, varargs mixed args) {
315 if (!loading_simul_efuns)
316 return apply(symbol_function(sefun), args);
317 else {
318 switch(sefun) {
319 case "log_file":
320 case "secure_level":
321 case "secure_euid":
322 case "find_player":
323 case "find_netdead":
324 case "find_living":
325 case "process_call":
326 case "replace_personal":
327 case "file_time":
328 return 0;
329 case "dtime":
330 if (pointerp(args) && sizeof(args))
331 return strftime("%a %d. %b %Y, %H:%M:%S",args[0]);
332 else
333 return strftime("%a %d. %b %Y, %H:%M:%S");
334 case "uptime":
335 return to_string(time()-__BOOT_TIME__) + " Sekunden";
336 }
337 }
338 return 0;
339}
340
341
342// Preload manuell wiederholen; Keine GD-Funktion
343void redo_preload()
344{
345 string *to_preload;
346 int i,j;
347
348 to_preload=epilog(0);
349 j=sizeof(to_preload);
350 for (i=0;i<j;i++)
351 catch(preload(to_preload[i]));
352 return;
353}
354
355// ##################
356//########################## Standard-Lfuns #############################
357// ##################
358
359// Keine GD-Funktion, aber sinnvoll.
360public varargs int remove(int silent)
361{
362 write("Der Master will aber nicht zerstoert werden!\n");
363 return 0;
364}
365
366// Einige repetitiven Taetigkeiten kann der Reset erledigen
367protected void reset()
368{
369 int i, *date;
370 mixed *wl;
371
372 //Darf nicht von Hand aufgerufen werden!
373 if (TI||TP) return;
374
375 // Naechsten Reset setzen
376 set_next_reset(RESETINT);
377
378 // Userinfo aufraeumen
379 _cleanup_uinfo();
380
381 // Projekt-Cache loeschen
382 _cleanup_projects();
383
384 // Wenn Zeit abgelaufen, dann READ_FILE-Log rotieren
385 if (!logrotate_counter--)
386 {
387 i=time()/LOGFILEAGE;
388 date=get_dir(sprintf("%s.%d", READ_FILE,i%LOGNUM), GETDIR_DATES);
389 if (pointerp(date)&&sizeof(date)&&
390 (date[0]/LOGFILEAGE)==i) return;
391 if (file_size(READ_FILE)>0)
392 rename(READ_FILE, sprintf("%s.%d", READ_FILE,i%LOGNUM));
393 logrotate_counter=LOGROTATE;
394 }
395 // einmal am Tag die UIDAliase resetten. Hierzu wird der logrotate_counter
396 // missbraucht.
397 if (!(logrotate_counter%24)) {
398 ResetUIDAliase();
399#ifdef _PUREFTPD_
400 expire_ftp_user();
401#endif
402 }
403
404 // Wizlist altert
405 wl = wizlist_info();
406 for (i=sizeof(wl); i--; )
407 set_extra_wizinfo(wl[i][WL_NAME], wl[i][WL_EXTRA] * 99 / 100);
408
409 // Dann noch Mails bearbeiten
410 mails_last_hour=0;
411 mailread();
412
413 return;
414}
415
416
417// #################
418//########################## ID-Management ##############################
419// #################
420
421// Magier-ID fuer Datei str (als Objekt oder als String) ermitteln
422string creator_file(mixed str) {
423 string *strs,tmp;
424 int s;
425
426 // full_path_array nach strs
427 // TODO: was ist mit clones?
428 if(objectp(str))
429 strs=full_path_array(object_name(str), 0);
430 else if(stringp(str))
431 strs=full_path_array(str, 0);
432 else return NOBODY;
433
434 // absolute Pfade interessieren hier gerade nicht.
435 strs -= ({""});
436
437 s=sizeof(strs);
438 if(s<2) return NOBODY;
439
440 switch(strs[0]) {
441 case DOMAINDIR:
442 // Fuer Nicht-Magier bzw. "Pseudo-"Magier die Regionskennung
443 if( s==2 || WIZLVLS[strs[2]] || !IS_LEARNER(strs[2]) )
444 return strs[1];
445
446 // in /d/erzmagier gibt es Magier-IDs
447 if (strs[1]=="erzmagier") return strs[2];
448
449 // Ansonsten d.region.magiername
450 return sprintf("d.%s.%s", strs[1], strs[2]);
451
452 case PROJECTDIR:
453 // In p.service gibt es ids mit uid
454 if (s>2 && strs[1]=="service") return "p.service."+strs[2];
455
456 // Ansonsten nur p.projekt (p.service ist auch hier drin)
457 return "p."+strs[1];
458
459 case WIZARDDIR:
460 if (s>2)
461 return strs[1];
462 if (s==2 && file_size(strs[0]+"/"+strs[1])==FSIZE_DIR)
463 return strs[1];
464 return NOBODY;
465
466 case SECUREDIR:
467 return ROOTID;
468
469 case GUILDDIR:
470 case SPELLBOOKDIR:
471 tmp=strs[1];
472 if (tmp[<2..<2]==".") tmp=tmp[0..<3];
473 if ((s=member(tmp,'.'))>0) tmp=tmp[s+1..];
474 return "GUILD."+tmp;
475
476 case LIBROOMDIR:
477 return ROOMID;
478
479 case STDDIR:
480 case LIBOBJDIR:
481 return BACKBONEID;
482
483 case DOCDIR:
484 return DOCID;
485
486 case MAILDIR:
487 return MAILID;
488 case NEWSDIR:
489 return NEWSID;
490
491 case LIBITEMDIR:
492 return ITEMID;
493
494 /* Fall-Through */
495 default:
496 return NOBODY;
497
498 }
499 return NOBODY; //should never be reached.
500}
501
502// UID und EUID an Objekt geben (oder eben nicht)
503// Keine GD-Funktion, aber von Hooks aufgerufen
504protected mixed give_uid_to_object(string datei,object po)
505{
506 string creator,pouid;
507
508 // Parameter testen
509 if (!stringp(datei)||!objectp(po)) return 1;
510
511 // Keine Objekte in /log, /open oder /data
512 if (strstr(datei, "/"LIBDATADIR"/") == 0
513 || strstr(datei, "/"LIBLOGDIR"/") == 0
514 || strstr(datei, "/"FTPDIR"/") == 0
515 )
516 return 1;
517
518 // Datei muss Besitzer haben
519 if (!(creator=creator_file(datei))) return 1;
520
521 // Hack, damit simul_efun geladen werden kann
522 if (creator==ROOTID && po==TO) return ROOTID;
523
524 // Keine euid, kein Laden ...
525 if (!(pouid=geteuid(po))) return 1; // Disallow if no euid in po
526
527 // Root generiert keine Root-Objekte ;-)
528 // Nur UID setzen
529 if (pouid==ROOTID)
530 return ({creator,NOBODY}); // root does not create root objects!
531
532 // EUID mitsetzen, wenn PO==creator oder creator==BACKBONEID
533 if (creator==pouid || creator==BACKBONEID)
534 return pouid;
535
536 return ({creator,NOBODY});
537}
538
539// Die System-IDs muessen bekannt sein
540string get_master_uid() { return ROOTID;}
541string get_bb_uid() { return BACKBONEID; }
542// TODO: Bei get_wiz_name koennte man - so moeglich - auf 'wirkliche'
543// Magiernamen gehen (Idee von mandragon)
544string get_wiz_name(string file) { return creator_file(file);}
545
546// ##########################
547//###################### Sonstige GD-Funktionen #########################
548// ##########################
549
550// Spieler hat sich eingeloggt
551protected object connect()
552{
553 string err;
554 int errno;
555 object ob,bp;
556
557#if defined(SSLPORT) && __EFUN_DEFINED__(tls_available)
558 if (efun::interactive_info(this_player(), II_MUD_PORT) == SSLPORT) {
559 // reject connection of TLS is not available
560 if (!tls_available())
561 return 0;
562 // establish TLS
563 errno=tls_init_connection();
564 if (errno < 0) {
565 // Fehler im Vebindungsaufbau
566 printf("Can't establish a TLS/SSL encrypted connection: %s\n",
567 tls_error(errno));
568 return 0; // this will close the connection to the client.
569 }
570 }
571#endif
572
573 // Blueprint im Environment? Das geht nun wirklich nicht ...
574 if ((bp=find_object("/secure/login")) && environment(bp))
575 catch(destruct(bp);publish);
576
577 // Login-Shell clonen
578 err = catch(ob = clone_object("secure/login"); publish);
579
580 if (errno == 0 && err)
581 write("Fehler beim Laden von /secure/login.c\n"+err+"\n");
582
583 return ob;
584}
585
586// Was machen bei disconnect?
587protected void disconnect(object who, string remaining) {
588 who->NetDead(); return;
589}
590
591// Es gibt kein File 'filename'. VC aktivieren so vorhanden ...
592protected object compile_object(string filename)
593{
594 object ret;
595 string *str;
596 string compiler;
597
598 if (!sizeof(filename)) return 0;
599 str=efun::explode(filename,"/");
600
601 if(sizeof(str)<2) return 0;
602 compiler=implode(str[0..<2],"/")+"/virtual_compiler";
603
604 if (find_object(compiler)
605 || file_size(compiler+".c")>0)
606 {
607 if(catch(
608 ret=(object)call_other(compiler,"compile_object",str[<1]); publish))
609 return 0;
610 }
611 else
612 return(0);
613
614 if ( objectp(ret) && efun::explode(creator_file(filename), ".")[<1] !=
615 REAL_EUID(ret) ){
616 funcall( symbol_function('log_file/*'*/), "ARCH/VC_MISSBRAUCH",
617 sprintf( "%s: %s versucht per VC %s zu %s umzubenennen!\n",
618 funcall( symbol_function('dtime/*'*/), time() ),
619 (this_interactive() ? getuid(this_interactive()):
620 object_name(previous_object())), object_name(ret),
621 filename) );
622
623 return 0;
624 }
625 return ret;
626}
627
628// ############
629//############################ Shutdown #################################
630// ############
631
632// Spieler bei Shutdown entfernen
633protected void remove_player(object victim)
634{
635 catch(victim->quit());
636 if (victim) catch(victim->remove());
637 if (victim) destruct(victim);
638 return;
639}
640
641// Langsamer Shutdown
642protected void slow_shut_down(int minutes)
643{
644 //sollte nur vom GD gerufen werden. Oder allenfalls Master selbst
645 filter(users(),#'tell_object,
646 "Der Gamedriver ruft: Der Speicher wird knapp ! Bereitet euch auf das Ende vor !\n");
647 "/obj/shut"->shut(minutes);
648 return;
649}
650
651
652// In LD varargs void notify_shutdown(string crash_reason)
653protected varargs void notify_shutdown (string crash_reason) {
654
655 if (PO && PO != TO)
656 return;
657 if (crash_reason) {
658 //Uhoh... Verdammter Crash. :-( Zumindest mal mitloggen,
659 //was der Driver sagt...
660 write_file(CRASH_LOG,sprintf(
661 "\n%s: The driver crashed! Reason: %s\n",
662 ctime(time()),crash_reason));
663 }
664
665 filter(users(), #'tell_object,
666 "Game driver shouts: LDmud shutting down immediately.\n");
667 save_wiz_file();
668 return;
669}
670
671// ##################
672//########################## Berechtigungen #############################
673// ##################
674
675// Darf Verbindung durch name von obfrom nach ob gelegt werden?
676
677//int valid_exec(string name) {return 1;}
678
679int valid_exec(string name, object ob, object obfrom)
680{
681 // Ungueltige Parameter oder Aufruf durch process_string -> Abbruch
682 if (!objectp(ob) || !objectp(obfrom)
683 || !stringp(name) || !sizeof(name)
684 || funcall(symbol_function('process_call)) )
685 return 0;
686
687 // renew_player_object() darf ...
688 if (name=="/secure/master/misc.c"
689 || name=="secure/master/misc.c")
690 return 1;
691
692 // Ansonsten darf sich nur die Shell selber aendern ...
693 if (previous_object() != obfrom) return 0;
694
695 // Die Login-Shell zu jedem Objekt ...
696 if (name=="/secure/login.c"
697 || name=="secure/login.c")
698 return 1;
699
700 // Magier per exec nur, wenn sie damit keine Fremde uid/euid bekommen
701 if (this_interactive() == obfrom && getuid(obfrom) == getuid(ob)
702 && geteuid(obfrom) == geteuid(ob))
703 return 1;
704
705 // Sonst darf niemand was
706 return 0;
707}
708
709
710// Darf me you snoopen?
711int valid_snoop(object me, object you) {
712 return getuid(PO) == ROOTID;
713}
714
715// Darf wiz wissen, ob er gesnoopt wird?
716int valid_query_snoop(object wiz) {
717 return getuid(PO) == ROOTID;
718}
719
720// Darf ob seine EUID auf neweuid setzen?
721int valid_seteuid(object ob, string neweuid)
722{
723 return (ob==this_object() ||
724 getuid(ob) == ROOTID ||
725 getuid(ob) == neweuid ||
726 creator_file(object_name(ob)) == neweuid);
727}
728
729// Darf getraced werden?
730int valid_trace(string what, mixed arg) {
731 return IS_ARCH(TI);
732}
733
734
735// valid_write() und
736// valid_read() befinden sich in file_access.c
737
738
739// Darf PO ob shadowen?
740int query_allow_shadow(object ob)
741{
742 // ROOT darf nicht geshadowed werden
743 if(getuid(ob) == ROOTID)
744 return 0;
745
746 // Access-Rights auch nicht
747 if (efun::explode(object_name(ob),"#")[0][<14..]=="/access_rights")
748 return 0;
749
750 // Ansonsten query_prevent_shadow fragen ...
751 return !(int)ob->query_prevent_shadow(PO);
752}
753
754
755/* privilege_violation is called when objects try to do illegal things,
756 * or files being compiled request a privileged efun.
757 *
758 * return values:
759 * 1: The caller/file is allowed to use the privilege.
760 * 0: The caller was probably misleaded; try to fix the error.
761 * -1: A real privilege violation. Handle it as error.
762 */
763int privilege_violation(string op, mixed who, mixed arg1, mixed arg2)
764{
765
766 if (objectp(who) &&
767 (who==this_object() || geteuid(who)==ROOTID))
768 return 1;
769
770 switch(op)
771 {
772 case "call_out_info":
773 return -1;
774 case "send_udp":
775 return 1;
776
777 case "nomask simul_efun":
778 // <who> ist in diesem Fall ein string (Filename) und damit von dem
779 // Check da oben nicht abgedeckt. Daher explizite Behandlung hier.
780 // Ausserdem hat der Pfad (zur Zeit noch) keinen fuehrenden '/'.
781 // Falls das jemand einschraenken will: der Kram fuer die simul_efuns
782 // muss das duerfen!
783 if (stringp(who)
784 && strstr(who,"secure/") == 0)
785 return 1;
786 return 0; // alle andere nicht.
787
788 case "get_extra_wizinfo":
789 // benutzt von /secure/memory.c und /secure/simul_efun.c
790 case "set_extra_wizinfo":
791 // wird benutzt von simul_efun.c, welche aber als ROOT-Objekt implizit
792 // erlaubt ist (s.o.)
793 return -1; // fuer allen nicht erlaubt.
794
795 case "rename_object":
796 if (object_name(who)=="/secure/impfetch" ||
797 BLUE_NAME(who)=="/secure/login")
798 return 1;
799 return(-1);
800
801 case "configure_driver":
802 return call_sefun("secure_level") >= ARCH_LVL;
803
804 case "limited":
805 // Spielershells duerfen sich zusaetzliche Ticks fuer den Aufruf von
806 // ::die() beschaffen, damit der Tod nicht wegen wenig Ticks buggt.
807 if (strstr(load_name(who), "/std/shells/") == 0
808 && get_type_info(arg1, 2) == who
809 && get_type_info(arg1, 3) == "/std/living/life"
810// && get_type_info(arg1, 4) == "die"
811 && arg2[LIMIT_EVAL] <= 10000000 ) {
812 return 1;
813 }
814 //DEBUG(sprintf("%O, %O, %O", who, arg1, arg2));
815 int *limits=efun::driver_info(DI_CURRENT_RUNTIME_LIMITS);
816 // LIMIT_EVAL darf verringert werden. Alle anderen Limits duerfen nicht
817 // geaendert werden. Achja, LIMIT_EVAL darf nicht 0 (LIMIT_UNLIMITED)
818 // sein. *g*
819 // LIMIT_COST darf entweder 0 oder -100 sein.
820 if (arg2[LIMIT_EVAL]>1000 && (arg2[LIMIT_EVAL] < get_eval_cost())-500
821 && arg2[LIMIT_ARRAY] == limits[LIMIT_ARRAY]
822 && arg2[LIMIT_MAPPING_KEYS] == limits[LIMIT_MAPPING_KEYS]
823 && arg2[LIMIT_MAPPING_SIZE] == limits[LIMIT_MAPPING_SIZE]
824 && arg2[LIMIT_BYTE] == limits[LIMIT_BYTE]
825 && arg2[LIMIT_FILE] == limits[LIMIT_FILE]
826 && arg2[LIMIT_CALLOUTS] == limits[LIMIT_CALLOUTS]
827 && (arg2[LIMIT_COST] == 0 || arg2[LIMIT_COST] == -100) )
828 return(1);
829 //sonst verweigern.
830 return(-1);
831
832 case "sqlite_pragma":
833 return 1;
834 case "attach_erq_demon":
835 case "bind_lambda":
836 case "configure_interactive":
837 case "configure_object":
838 case "execute_command":
839 case "erq":
840 case "input_to":
841 case "mysql":
842 case "net_connect":
843 case "pgsql":
844 case "set_driver_hook":
845 case "set_this_object":
846 case "shadow_add_action":
847 case "symbol_variable":
848 case "variable_list":
849 case "wizlist_info":
850 default:
851 return(-1);
852 }
853 return -1;
854}
855
856// ####################
857//######################## Fehlerbehandlung #############################
858// ####################
859
860// Behandlung von Fehler im Heart_Beat
861protected int heart_beat_error( object culprit, string error, string program,
862 string current_object, int line, int caught)
863{
864 if (!objectp(culprit)) return 0;
865 if (interactive(culprit))
866 {
867 tell_object(culprit,"Der GameDriver teilt Dir mit: "
868 "Dein Herzschlag hat ausgesetzt!\n");
869 if (IS_LEARNER(culprit))
870 tell_object(culprit,
871 sprintf("Fehler: %sProgamm: %s, CurrObj: %s, Zeile: %d, "
872 "the error was %scaught at higher level.\n",
873 error,program,current_object,line,
874 caught ? "" : "not"));
875 }
876
877 if (efun::object_info(culprit, OI_ONCE_INTERACTIVE))
878 call_out("restart_heart_beat", 5, culprit);
879 else
880 catch(culprit->make_immortal(); publish);
881 return 0;
882}
883
884// Ausgabe einer Meldung an Spieler mit Level >= minlevel. Wird genutzt, um
885// Magier ueber Fehler zu informieren, die nicht normal behandelt werden
886// (z.B. rekursive Fehler, d.h. Fehler in der Fehlerbehandlung)
887private void tell_players(string msg, int minlevel) {
888 msg = efun::sprintf("%=-78s","Master: " + msg); // umbrechen
889 efun::filter(efun::users(), function int (object pl)
890 {
891 if (query_wiz_level(pl) >= minlevel)
892 efun::tell_object(pl, msg);
893 return 0;
894 } );
895}
896
897/**
898 * \brief Error handling fuer Fehler beim Compilieren.
899 *
900 * log_error() wird vom GameDriver aufgerufen, wenn zur Compile-Zeit ein
901 * Fehler auftrat. Die Fehlermeldung wird unter /log/error geloggt.
902 * Falls this_interactive() ein Magier ist, bekommt er die Fehlermeldung
903 * direkt angezeigt.
904 * Sollte das Logfile 50kB ueberschreiten, wird es rotiert. Auf [Entwicklung]
905 * wird dann eine Fehlermeldung abgesetzt.
906 *
907 * @param file Die Datei, in der der Fehler auftrat.
908 * @param message Die Fehlermeldung.
909 * @param warn Warnung (warn!=0) oder Fehler (warn==0)?
910 * */
911
912protected void log_error(string file, string message, int warn) {
913 string lfile;
914 string cr;
915 mixed *lfile_size;
916
917 if (handling_error == efun::time()) {
918 // Fehler im Verlauf einer Fehlerbehandlung: Fehlerbehandlung minimieren,
919 // nur Meldung an eingeloggte Erzmagier.
920 tell_players("log_error(): Rekursiver Fehler in Fehlerbehandlung. "
921 "Bitte Debuglog beachten. Aktueller Fehler in " + file + ": "
922 + message, ARCH_LVL);
923 return;
924 }
925 handling_error = efun::time();
926
927 //Fehlerdaten an den Errord zur Speicherung weitergeben, falls wir sowas
928 //haben.
929#ifdef ERRORD
930 // Only call the errord if we are _not_ compiling the sefuns. It uses sefuns
931 // and it will cause a recursing call to get_simul_efuns() which is bound to
932 // fail.
933 if (!loading_simul_efuns) {
934 catch(limited(#'call_other,({200000}),ERRORD,"LogCompileProblem",
935 file,message,warn);publish );
936 }
937#endif // ERRORD
938
939 // Logfile bestimmen
940 cr=creator_file(file);
941 if (!cr) lfile="NOBODY.err";
942 else if (cr==ROOTID) lfile="ROOT";
943 else if (efun::member(cr,' ')!=-1) lfile="STD";
944 else lfile=cr;
945 //je nach Warnung oder Fehler Verzeichnis und Suffix bestimmen
946 if (warn) {
947 lfile="/log/warning/" + lfile + ".warn";
948 }
949 else {
950 lfile="/log/error/" + lfile + ".err";
951 }
952
953 // Bei Bedarf Rotieren des Logfiles
954 if ( !loading_simul_efuns
955 && sizeof(lfile_size = get_dir(lfile,2))
956 && lfile_size[0] >= MAX_ERRLOG_SIZE )
957 {
958 catch(rename(lfile, lfile + ".old"); publish); /* No panic if failure */
959 if (!loading_simul_efuns)
960 {
961 catch(send_channel_msg("Entwicklung","<MasteR>",
962 "Logfile rotiert: "+lfile+"."));
963 }
964 }
965
966 efun::write_file(lfile,message);
967
968 // Magier bekommen die Fehlermeldung angezeigt
969 if (IS_LEARNER(TI)) efun::tell_object(TI, message);
970
971 handling_error = 0;
972}
973
974/* Gegenmassnahme gegen 'too long eval' im Handler vom runtime error:
975 Aufsplitten des Handlers und Nutzung von limited() */
976//keine GD-Funktion
977private void handle_runtime_error(string err, string prg, string curobj,
978 int line, mixed culprit, int caught, object po, int issueid)
979{
980 string code;
981 string debug_txt;
982 object ob, titp;
983
984 //DEBUG(sprintf("Callerstack: (handle_runtime_error()): %O\n",
985 // caller_stack(1)));
986 // Fehlermeldung bauen
987 if (!stringp(err)) err="<NULL>";
988 if (!stringp(prg)) prg="<NULL>";
989 if (!stringp(curobj)) curobj="<NULL>";
990 debug_txt=efun::sprintf("Fehler: %O, Objekt: %s, Programm: %O, Zeile %O (%s), "
991 "ID: %d",
992 err[0..<2], curobj, (prg[0]!='<'?"/":"")+prg, line,
993 (TI?efun::capitalize(efun::getuid(TI)):"<Unbekannt>"),
994 issueid);
995
996 titp = TI || TP;
997 // Fehlermeldung an Kanaele schicken, aber nur wenn der aktuelle Fehler
998 // nicht waehrend des ladens der simulefuns auftrat, bei der Ausgabe der
999 // Meldung ueber die Kaenaele wieder sefuns genutzt werden.
1000 if (!loading_simul_efuns) {
1001 if (titp && (IS_LEARNER(titp) || (titp->QueryProp(P_TESTPLAYER))))
1002 {
1003 catch(send_channel_msg("Entwicklung",
1004 capitalize(objectp(po) ? REAL_UID(po) : ""),
1005 debug_txt));
1006 }
1007 else
1008 {
1009 catch(send_channel_msg("Debug",
1010 capitalize(objectp(po) ? REAL_UID(po) : ""),
1011 debug_txt));
1012 }
1013 }
1014
1015 if (!titp) return;
1016
1017 // Fehlermeldung an Benutzer
1018 if (IS_LEARNER(titp))
1019 efun::tell_object(titp,
1020 efun::sprintf("%'-'78.78s\nfile: %s line: %d object: %s\n" +
1021 "%serror: %s%'-'78.78s\n","",prg,line,curobj,
1022 (prg&&(code=efun::read_file("/"+prg,line,1))?
1023 "\n"+code+"\n":""),err,""));
1024 else
1025 efun::tell_object(titp, "Du siehst einen Fehler im Raum-Zeit-Gefuege.\n");
1026}
1027
1028//Keine GD-Funktion, limitiert die Kosten fuer den handler
1029private void call_runtime_error(string err, string prg, string curobj,
1030 int line, mixed culprit, int caught, object po)
1031{
1032 if (handling_error == efun::time())
1033 {
1034 // Fehler im Verlauf einer Fehlerbehandlung: Fehlerbehandlung minimieren,
1035 // nur Meldung an eingeloggte Regionsmagier.
1036 tell_players("call_runtime_error(): Rekursiver Fehler in "
1037 "Fehlerbehandlung. Bitte Debuglog beachten. Aktueller Fehler in "
1038 + curobj + ": " + err, LORD_LVL);
1039 return;
1040 }
1041 handling_error = efun::time();
1042
1043 //Fehlerdaten an den Errord zur Speicherung weitergeben, falls wir sowas
1044 //haben.
MG Mud User88f12472016-06-24 23:31:02 +02001045 int issueid;
Christian Georg Beckerd66c9602017-01-09 10:37:51 +02001046#ifdef ERRORD
MG Mud User88f12472016-06-24 23:31:02 +02001047 // Wenn die sefuns gerade geladen werden, erfolgt keine Weitergabe an den
1048 // ErrorD, da dabei wieder sefuns gerufen werden, was zu einer Rekursion
1049 // fuehrt.
1050 if (!loading_simul_efuns) {
1051 catch(issueid=efun::limited(#'call_other,({200000}),ERRORD,"LogError",
1052 err,prg,curobj,line,culprit,caught);publish);
1053 }
1054#endif // ERRORD
1055
1056 //eigenen Errorhandler laufzeitbegrenzt rufen
1057 efun::limited(#'handle_runtime_error, ({ 200000 }), err, prg, curobj,
1058 line,culprit,caught, po, issueid);
1059
1060 handling_error = 0;
1061}
1062
1063// Laufzeitfehlerbehandlung, hebt Evalcost-Beschraenkung auf und reicht weiter
1064// an call_runtime_error, die die Grenze wieder auf einen bestimmten Wert
1065// festlegt.
1066protected void runtime_error(string err ,string prg, string curobj, int line,
1067 mixed culprit, int caught) {
1068
1069 //DEBUG(sprintf("Callerstack: (runtime_error()): %O\nPO: %O\nPO(0): %O\n",
1070 // caller_stack(1),previous_object(),previous_object(0)));
1071
1072 limited(#'call_runtime_error, ({ LIMIT_UNLIMITED }),
1073 err,prg,curobj,line,culprit,caught,previous_object());
1074}
1075
1076//Warnungen mitloggen
1077protected void runtime_warning( string msg, string curobj, string prog, int line,
1078 int inside_catch)
1079{
1080 string code;
1081 string debug_txt;
1082 object titp;
1083
1084 //DEBUG(sprintf("Callerstack: in runtime_warning(): %O\nPO(0): %O\n",
1085 // caller_stack(1),previous_object(0)));
1086
1087 //Daten der Warnung an den Errord zur Speicherung weitergeben, falls wir
1088 //sowas haben.
1089 int issueid;
1090#ifdef ERRORD
1091 catch(issueid=limited(#'call_other,({200000}),ERRORD,"LogWarning",
1092 msg,prog,curobj,line,inside_catch); publish);
1093#endif // ERRORD
1094
1095 // Fehlermeldung bauen
1096 if (!stringp(msg)) msg="<NULL>";
1097 if (!stringp(prog)) prog="<NULL>";
1098 if (!stringp(curobj)) curobj="<NULL>";
1099 debug_txt=sprintf("Warnung: %O, Objekt: %s, Programm: %O, Zeile %O (%s) "
1100 "ID: %d",
1101 msg[0..<2], curobj, (prog[0]!='<'?"/":"")+prog, line,
1102 (TI?capitalize(getuid(TI)):"<Unbekannt>"),
1103 issueid);
1104
1105 //Fehlermeldungen an -warnungen schicken
1106 catch(send_channel_msg("Warnungen",
1107 capitalize(objectp(previous_object()) ?
1108 REAL_UID(previous_object()) : ""),
1109 debug_txt));
1110
1111 titp = TI || TP;
1112
1113 if (!titp) return;
1114
1115 // Fehlermeldung an Benutzer
1116 if (IS_LEARNER(titp))
1117 tell_object(titp,
1118 sprintf("%'-'78.78s\nfile: %s line: %d object: %s\n" +
1119 "%sWarnung: %s%'-'78.78s\n","",prog,line,curobj,
1120 (prog&&(code=read_file("/"+prog,line,1))?
1121 "\n"+code+"\n":""),msg,""));
1122 return;
1123}
1124
1125
1126// #####################
1127//######################## Einrichten des ED ############################
1128// #####################
1129
1130// Setup speichern
1131protected int save_ed_setup(object who, int code)
1132{
1133 string file;
1134
1135 if (!intp(code)) return 0;
1136 file = sprintf("/players/%s/.edrc",geteuid(who));
1137 rm(file);
1138 return write_file(file,(string)code);
1139}
1140
1141// Setup einladen
1142protected int retrieve_ed_setup(object who)
1143{
1144 string file;
1145
1146 file = sprintf("/players/%s/.edrc",getuid(who));
1147 if (file_size(file)<1) return 0;
1148 return (int)read_file(file);
1149}
1150
1151// Wo werden Dateien gespeichert?
1152string get_ed_buffer_save_file_name(string file)
1153{
1154 return sprintf("/players/%s/.dead_ed_files/%s",
1155 getuid(this_player()),efun::explode(file, "/")[<1]);
1156}
1157
1158// ################################
1159//################### Ungenutzte Pflichtfunktionen ######################
1160// ################################
1161
1162// Nichts zu tun beim Master-Neustart
1163protected void external_master_reload() { return; }
1164
1165// Keine externen Master-Flags
1166protected void flag(string str) { return; }
1167
1168// Wird benoetigt, falls ERQ angeschlossen ist
1169protected void stale_erq(closure callback) { return; }
1170
1171// Zerstoerten Master wiederbeleben ...
1172// nicht viel zu tun, wenn flag gesetzt ist. Wenn aber nicht, sind alle
1173// Variablen auf 0. Nach dieser Funktion wird wieder inaugurate_master()
1174// gerufen.
1175protected void reactivate_destructed_master(int flag) {
1176 if (flag) {
1177 //re-init
1178 //was machen? TODO
1179 }
1180 return;
1181}
1182
1183// Kein Quota-Demon (potentielles TODO)
1184//Handle quotas in times of memory shortage.
1185//is called during the final phase of a garbage collection, if the user
1186//reserve could not be re-allocated. Last (!) Chance to free (active) objects
1187//from the system and prevent call to slow_shut_down()!
1188protected void quota_demon() {
1189 //was kann man machen?
1190 //uebervolle Seherhaeuser leeren?
1191 return;
1192}
1193
1194// Keine Prepositionen in parse_command
1195//string *parse_command_prepos_list() { return ({}); }
1196
1197// Wie lautet das Wort fuer 'alle' ?
1198//string *parse_command_all_word() { return ({}); }
1199
1200
1201// Keine Besonderen Objektnamen
1202string printf_obj_name(object ob) { return 0; }
1203
1204// ###################
1205//######################### Sonstiges/Hooks #############################
1206// ###################
1207
1208//TODO: save_wiz_file in shutdown() integrieren?
1209// Wizliste speichern: Zeile generieren
1210static string _save_wiz_file_loop(mixed *a)
1211{
1212 return sprintf("%s %d %d\n",a[WL_NAME],a[WL_COMMANDS],a[WL_EXTRA]);
1213}
1214
1215// Wizliste speichern
1216protected void save_wiz_file()
1217{
1218 rm("/WIZLIST");
1219 write_file("/WIZLIST",implode(
1220 map(wizlist_info(),#'_save_wiz_file_loop),""));
1221}
1222
1223// TODO: macht der telnet_neg-hook ueberhaupt was?
1224void telnet_neg(mixed cmd,mixed opt,mixed args)
1225{
1226 if (opt==34 && cmd==251)
1227 binary_message(({255,250,34,1,1,255,240}));
1228}
1229
1230// EUID und UID werden von give_uid_to_object() vergeben, diese sind in
1231// inaugurate_master() als driver hooks angemeldet.
1232
1233/*load_uid_hook() sollte machen, was diese Lambda machte...
1234 unbound_lambda( ({'printf_obj_name}),
1235 ({#'give_uid_to_object,'printf_obj_name,
1236 ({#'previous_object}),0}))); */
1237protected mixed load_uid_hook(string datei) {
1238 return(give_uid_to_object(datei,previous_object()));
1239}
1240
1241/* clone_uid_hook sollte machen, was diese Lambda machte...
1242 unbound_lambda( ({'blueprint, 'new_name}),
1243 ({#'give_uid_to_object,'new_name,
1244 ({#'previous_object}),1}))); */
1245protected mixed clone_uid_hook(string blueprint,string new_name) {
1246 return(give_uid_to_object(new_name,previous_object()));
1247}
1248