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