#!/usr/bin/perl ### -I/opt/psyced/utility # the include path would help finding INI.pm, but what if you the user # have decided to install psyced elsewhere? # # psyconf(8) - tool that generates psyc configuration # files out of a common psyced.ini # # $Id: psyconf,v 1.80 2008/10/26 15:24:01 lynx Exp $ =head1 NAME psyconf - a configuration tool for PSYCED =head1 SYNOPSIS Usage: psyconf [ <file> ] The default configuration file used is /etc/psyced.ini =cut use File::Spec; # this approach still doesn't work when installing on gentoo # we need a seperate ebuild for INI.pm. until then we will use # the stupid parser # #BEGIN { # BEGIN just in case somebody ever accesses INI::something by hand # unless (%INI::) { # get INI, if it isn't in this file (somewhere above) # my @dir; # my $inilocation; # # @dir = File::Spec->splitdir($0); # pop @dir; # $inilocation = File::Spec->catfile(@dir, qw".. utility INI.pm"); # # if (-f $inilocation) { # require $inilocation; # } else { # require INI; # } # } #} # append something while testing #my $test = "-NEW"; use Data::Dumper; sub say { print join('', @_); # if $test; } ### MAIN ### # if you are manually compiling an ldmud, rename it or change here. my $driver = 'psyclpc'; use Getopt::Std; &getopt; print STDERR '$Id: psyconf,v 1.80 2008/10/26 15:24:01 lynx Exp $', "\n"; my $conf = shift || 'psyced.ini'; $conf = '/etc/psyc/psyced.ini' unless -r $conf; # should we complain if two psyced.ini files exist? # lymeca got confused at this point, expecting the systemwide # psyced.ini to be used, but actually having a local copy say "The PSYCED configuration tool. Looking at $conf\n\n"; # this is the super-duper fancy amazing INI handler # #use INI; # ":verbose"; # my %c; # tie(%c, INI, $conf, 5); # and this is the stupider INI parser in a couple of lines # cute, but we'd like to have the features of INI.pm one happy day my %c, $f = ""; open C, $conf; while(<C>) { next if /^;/; next if /^\s*$/; $f = $1, next if /^\[(\w+)\]\s*$/; # Room name 'ornia ' is not permitted. # isn't this stripping trailing whitespace properly? # should it read \S.+\S ? or at least .+\S ? $c{"$f$1"} = $2, next if /^(\w+)\s*=\s*(.+)\s*$/; die "cannot parse line $. in $conf:\n\t$_\n"; } close C; if ($opt_D) { if ($c{_basic_list_script_init}) { foreach (split /\s+/, $c{_basic_list_script_init}) { say "Deinstallation! Removing $_\n"; unlink $_; } } exit; } use POSIX; my @uname = POSIX::uname(); # if POSIX.pm is unavailable, this also works: # @uname = split ' ', `uname -s -r -n -m -v`; #print Dumper(@uname); my $_type_machine = $uname[$#uname]; $_ = $uname[2]; /^(\w+\.\w+)\b/ or /^(\w+)\b/; my $_type_system = "\L$uname[0]\E"; my $_version_system = "$uname[0]/$1"; my $_type_architecture = lc "$_type_machine-$uname[0]"; say "Machine Type: $_type_machine. OS Type: $_type_system. ", "Arch: $_type_architecture\n"; #Typical outputs: # Machine Type: i686. OS Type: linux. Arch: i686-linux # Machine Type: x86_64. OS Type: linux. Arch: x86_64-linux # Machine Type: Power Macintosh. OS Type: darwin. Arch: power macintosh-darwin my $base = $c{_basic_path_base}; die "_basic_path_base not defined in config" unless $base; die "Base directory $base defined as _basic_path_base not available" unless -x $base; my $sandbox = "$base/world"; die "psyced library $sandbox does not exist" unless -x $sandbox; my $arch = "$base/bin-$_type_architecture"; my $config = $c{_basic_path_configuration} || $base; # this isn't taking $ARCH_DIR from install.sh into account # but maybe it shouldn't anyway.. say "Looking for driver in $arch/$driver\n"; unless (-x $arch && -x "$arch/$driver") { $arch = "$base/bin-$_type_system"; say "Looking for driver in $arch/$driver\n"; unless (-x $arch && -x "$arch/$driver") { $arch = "$base/bin-linux"; say "Looking for driver in $arch/$driver\n"; if (-x $arch && -x "$arch/$driver") { say "\nBug in $0: We didn't find $base/bin-linux in the appropriate way.\nThat's not nice. Continuing however.\n" } elsif (not -x ($arch = "$base/bin") && -x "$arch/$driver") { say "Looking for driver in $arch/$driver\n"; # bad place for psyclpc really.. # since it is usually of no commandline use # but where else should psyclpc.ebuild # install it? in /opt/psyclpc? for two files? $arch = "/usr/sbin"; say "Looking for driver in $arch/$driver\n"; # the following is very unlikely to happen: die <<X unless -x $arch && -x "$arch/$driver"; Could not find any directory containing $driver. X } } } say "Found driver in $arch/$driver\n"; my $user = $c{_basic_system_user} || 'daemon'; my ($login,$pass,$uid,$gid) = getpwnam($user) or die "uid '$user' not defined on this system"; # this doesn't mean psyconf is intended to run as $user too!! say "psyced must later be running as user $user ($uid:$gid)\n"; # will automatically, when started via etc/init.d but if you # start it manually, you have to switch to that user yourself my $ho = $c{_basic_host_name} || $ENV{HOST} || 'psyc'; my $chatname = $c{_basic_nick_server} || $c{_basic_host_name} || 'psyced'; $chatname = 'psyced' if $chatname eq 'psyc'; # say "Generating control files in $c{_basic_path_base} ..\n\n"; my $t = "$base/local/ports.h$test"; say "Generating control file $t ..\n"; rename $t, "$t~"; open O, '>', $t or die "Cannot write to $t"; print O <<X; // CAUTION! This file has been generated using $0. // Don't edit unless you are no longer going to use the configuration tool. // Edit $conf instead, then run $0 again. /* the values in here may be the same as in services.h * or they may be not. so always be aware which one's you * are using to which purpose. -lynX */ #ifndef PORTS_H #define PORTS_H #define PSYC_PORT $c{_protocols_port_PSYC} #define PSYCS_PORT $c{_protocols_port_PSYC_encrypted} #define HTTP_PORT $c{_protocols_port_HTTP} #define HTTPS_PORT $c{_protocols_port_HTTP_encrypted} #define IRC_PORT $c{_protocols_port_IRC} #define IRCS_PORT $c{_protocols_port_IRC_encrypted} #define JABBER_PORT $c{_protocols_port_jabber_clients} #define JABBERS_PORT $c{_protocols_port_jabber_clients_encrypted} #define JABBER_S2S_PORT $c{_protocols_port_jabber_S2S} #define TELNET_PORT $c{_protocols_port_telnet} #define TELNETS_PORT $c{_protocols_port_telnet_encrypted} #define APPLET_PORT $c{_protocols_port_applet} #define POP3_PORT $c{_protocols_port_POP3} #define POP3S_PORT $c{_protocols_port_POP3_encrypted} #define SMTP_PORT $c{_protocols_port_SMTP} #define SMTPS_PORT $c{_protocols_port_SMTP_encrypted} #define NTTP_PORT $c{_protocols_port_NNTP} #define NNTPS_PORT $c{_protocols_port_NNTP_encrypted} // experimental PSYC 1.0 implementation #define SPYC_PORT $c{_protocols_port_SPYC} #endif X close O; chmod 0644, $t; my $ports="$c{_protocols_port_PSYC} $c{_protocols_port_jabber_S2S} $c{_protocols_port_applet} $c{_protocols_port_telnet} $c{_protocols_port_HTTP} $c{_protocols_port_IRC} $c{_protocols_port_jabber_clients} $c{_protocols_port_SMTP} $c{_protocols_port_NNTP} $c{_protocols_port_SPYC}"; $ports .= " $c{_protocols_port_PSYC_encrypted} $c{_protocols_port_telnet_encrypted} $c{_protocols_port_HTTP_encrypted} $c{_protocols_port_IRC_encrypted} $c{_protocols_port_jabber_clients_encrypted} $c{_protocols_port_SMTP_encrypted} $c{_protocols_port_NNTP_encrypted}" if $c{_protocols_use_encryption}; my @adm; unless ($c{_administrators_list_nicks}) { print STDERR <<X; You have decided to run an unadministered psyced. That's a socially quite interesting idea, but the software can't deal with that yet. Continue nonetheless? (hit ctrl-c for mental sanity) X <STDIN>; } else { foreach (split /\s+/, $c{_administrators_list_nicks}) { my $ni = lc($_); push @adm, $ni; $t = "$base/data/person/$ni.o$test"; next if -e $t; say "Creating administrator in $t\n"; open O, '>', $t or die "Cannot write to $t"; my $pw = $c{_administrators_password_default}; unless ($pw) { print STDERR "Please input a password for $_: "; $pw = <STDIN>; chomp $pw; } print O <<X; #0:0 _v (["password":"$pw","name":"$_",]) X close O; chmod 0600, $t; chown $uid, $gid, $t if $uid; } } my $adm = join '", "', @adm; $t = "$base/local/psyconf.h$test"; say "Generating control file $t ..\n"; rename $t, "$t~"; open O, '>', $t or die "Cannot write to $t"; print O <<X; // CAUTION! This file has been generated using $0. // Don't edit unless you are no longer going to use the configuration tool. // Edit $conf instead, then run $0 again. // If you want to tune psyced, edit local.h instead! X if ( $c{_optional_config_HTTP} ) { print O <<X; # define WEB_CONFIGURE X print STDERR <<X unless $c{_protocols_port_HTTP} or $c{_protocols_port_HTTP_encrypted}; Warning: _optional_config_HTTP activated without any HTTP port. You won't be able to use the web configuration tool without webserver. X } print STDERR <<X if $c{_optional_charset_system}; Warning: _optional_charset_system is obsolete. Please remove it. X # print O <<X if $c{_optional_charset_system}; ## define SYSTEM_CHARSET "$c{_optional_charset_system}" # #X print O <<X if $c{_optional_charset_console}; // optional # define CONSOLE_CHARSET "$c{_optional_charset_console}" X # if ( $c{_basic_host_domain} ) { # print O <<X; ## undef __DOMAIN_NAME__ ## define __DOMAIN_NAME__ "$c{_basic_host_domain}" ## define SERVER_HOST "$ho." __DOMAIN_NAME__ # #X # } else { # print O <<X; ## if __DOMAIN_NAME__ != "unknown" ## define SERVER_HOST "$ho." __DOMAIN_NAME__ ## else ## if __HOST_IP_NUMBER__ == "127.0.0.1" ## define SERVER_HOST __HOST_NAME__ ## else ## define SERVER_HOST "$ho" ## endif ## endif # #X # } print O <<X if $c{_basic_host_domain}; # define CHATDOMAIN "$c{_basic_host_domain}" X my $pl = lc($c{_basic_place_default}) || 'rendezvous'; my $la = lc($c{_basic_language_default}) || 'en'; print O <<X; # define CHATHOST "$ho" # define CHATNAME "$chatname" # define DEFPLACE "$pl" # define DEFLANG "$la" // only required field, really # define ADMINISTRATORS "$adm" // architecture and operating system (used in PSYC headers etc.) # define MACHTYPE "$_type_machine" # define OSTYPE "$_version_system" X close O; chmod 0640, $t; chown $uid, $gid, $t if $uid; # PSYCED LAUNCHER $t = "$base/bin/psyced$test"; say "Generating control file $t ..\n"; rename $t, "$t~"; open O, '>', $t or die "Cannot write to $t"; my $db = $c{_optional_level_debug} || '0'; my $psyced = "$arch/$driver -DDEBUG=$db"; my $debugfile; if ( $c{_optional_use_file_debug} ) { $debugfile = "$sandbox/log/psyced.debug"; $psyced .= " --debug-file ". $debugfile; # since psyclpc 4.0.4 backtraces are also in the regular console log.. yippie! # say "ATTENTION: $debugfile will contain runtime error backtraces.\n"; } # else: ldmud tries to create $hostname.debug.log in $sandbox # psyclpc is good boy instead. so we can keep this optional. my $hc = 0; ## ldmud bug number one: you have to provide hostname AFTER hostaddr if ($c{_basic_host_IP} =~ /^[0-9\.]+$/) { $psyced .= " --hostaddr $c{_basic_host_IP}"; $hc = 1; # problem? } if ($c{_basic_host_domain} and $c{_basic_host_name}) { my $fqdn = "$c{_basic_host_name}.$c{_basic_host_domain}"; $psyced .= " --hostname $fqdn"; $hc = 0; # no problem } ## ldmud bug number two #WARNING: Because of some funny bug the driver will probably not bind properly #to _basic_host_IP as it also needs _basic_host_name and _basic_host_domain #to be provided. Hopefully this shortcoming will soon be history. #In the meantime please re-edit the psyced.ini to provide all variables. print STDERR <<X if $hc == 1; WARNING: _basic_host_IP, _basic_host_name and _basic_host_domain need to be provided together as the driver won't figure out the proper name for the IP number or vice versa. Please re-edit the psyced.ini to provide all variables. X if ($c{_protocols_use_encryption}) { my $t = $c{_basic_path_PEM_key}; if ($t) { $t = "$config/$t" unless $t =~ m#^/#; $psyced .= " --tls-key $t"; } if ($t = $c{_basic_path_PEM_certificate}) { $t = "$config/$t" unless $t =~ m#^/#; $psyced .= " --tls-cert $t"; } if ($t = $c{_basic_path_trust}) { $t = "$config/$t" unless $t =~ m#^/#; $psyced .= " --tls-trustdirectory $t"; } # ldmud doesn't support this yet # $psyced .= " --tlscrldirectory $config/$c{_basic_path_revocation}" # if $c{_basic_path_revocation}; } $psyced .= ' '. $c{_optional_extra_debug} if $c{_optional_extra_debug}; # you can divert UDP if you know what you are doing. my $portUDP = $c{_protocols_port_UDP} || $c{_protocols_port_PSYC}; my $umask = $c{_optional_umask} || '007'; print O <<X; #!/bin/sh # # CAUTION! This file has been generated using $0. # Don't edit unless you are no longer going to use the configuration tool. # Edit $conf instead, then run $0 again. X # if the installation isn't intentionally using root rights # make sure the admin won't run psyced or do "psyced -u" as root # as it will most probably mess up file permissions and break psyced print O <<X if $user ne 'root'; userid=`id | sed "s/).*//" | sed "s/.*(//"` # We could even test for != "x$user" here. Should we? if test "x\$userid" = "xroot" then echo "${hi}ERROR: ${lo}You can't run \$0 with root privileges unintentionally.\nUse 'su $user' or edit psyced.ini." exit 2 fi X # it's not a security issue really, but shouldn't control files # like status-0-beta-OK be outside the sandbox? -lynX print O <<X; umask $umask # with this chdir, old ldmud should be able to find its keys anyhow cd $base # available flags: -u, -d and -m # TODO: i'd like for -b to make a backup archive of the psyced tree before # updating, and -r to revert to the last backupped version. whatchathink? # the following if-block handles the -u option for updating your # installation by git. it ensures your installation is still basically # compatible with the one in the git or complains. # if test "\$1" = "-u" then if test -e $sandbox/status-0-beta-OK then # echo "automated update functionality no longer available. try" # echo "'git fetch origin', optionally 'git diff master..origin/master'" # echo "and finally 'git merge -s resolve origin' instead" # exit 1 # echo "${hi}ATTENTION: ${lo}Please give an empty password to log in (-> press enter)" ## (cd $sandbox/.. && cvs login && cvs -q update -dP && cvs logout) echo "" echo "Fetching update from public git repository..." echo "" (cd $sandbox/.. && git fetch origin) echo "" echo "You can now read the changes using ${hi}\$0 -d${lo}" echo "or activate the changes using ${hi}\$0 -m${lo}" fi if ! test -e $sandbox/status-0-beta-OK then echo "" echo "${hi}ATTENTION:${lo}" echo "The version of psyced you are using is outdated. You cannot" echo "get new updates because of incompatibility." echo "" echo "More information following.." echo "" cat $sandbox/status-0-DEPRECATED echo "" echo "The file you just saw is: ${hi}${lib}/status-0-DEPRECATED${lo}." exit 1 else echo "It may be a good idea to run ${hi}\$0${lo} again before you continue." fi exit fi if test "\$1" = "-d" then ## (cd $base && cvs -q diff -ur HEAD|\$PAGER) (cd $base && git diff master..origin/master | \$PAGER) exit fi if test "\$1" = "-m" then (cd $base && git merge -s resolve origin) exit fi # if your ldmud is too old, it may not understand --pidfile and --tls-* commandline="$psyced -s 0 -s v0 --swap-file $base/psyced.swap --erq $arch/erq --pidfile $sandbox/data/$driver.pid -m $sandbox -u $portUDP $ports" # keep the pid of this wrapper process, too echo \$\$ >data/psyced.pid # neat feature of gnu mv VERSION_CONTROL=numbered; export VERSION_CONTROL # rm'ing this allows the administrator commands in psyced to stop the loop touch $sandbox/data/.autorestart while [ -r $sandbox/data/.autorestart ] do X # we can presume gnu mv on linux #my $domv = $_type_system eq 'linux' ? 'mv -b' : 'mv -f'; # yes, but all these backups are overkill # who needs these debug logs archived for all eternity anyway? my $domv = 'mv -f'; # maybe we should develop a strategy for all the OTHER logfiles..... # but luckily psyclpc has some support for that itself print O <<X if $debugfile; touch $debugfile $domv $debugfile $debugfile-old X if ($c{_optional_console_debug}) { print O "\t\$commandline\n"; } else { say "The file $sandbox/log/psyced.out will contain the runtime output.\n"; print O <<X; touch $sandbox/log/psyced.out $sandbox/log/psyced.err $domv $sandbox/log/psyced.err $sandbox/log/psyced.err-old # the gnu date manual doesn't mention if +FORMAT is a posix # standard or a gnu extension. is this call going to work # in most current environments? it does work on bsd, *phew* #$domv $sandbox/log/psyced.out $sandbox/log/`date +%Y-%m-%d`.out # ah whatever.. let's not make it too complicated.. # who needs these debug logs archived for all eternity anyway? $domv $sandbox/log/psyced.out $sandbox/log/psyced.out-old echo ---------------------------------------- >>$sandbox/log/psyced.out date >>$sandbox/log/psyced.out echo ---------------------------------------- >>$sandbox/log/psyced.out \$commandline >>$sandbox/log/psyced.out 2>>$sandbox/log/psyced.err X } print O <<X; sleep 3 done X close O; chmod 0744, $t; chown $uid, $gid, $t if $uid; # TODO: generate a psyced.gdb file kind of like this print <<X if 0; file /bin/psyclpc run -DDEBUG=1 -u 4404 4404 2323 6667 5222 9999 etc. whatever flags necessary X # PSYCED INIT.D SCRIPT $t = "$base/etc"; mkdir($t); $t .= "/init.d"; mkdir($t); $t .= "/psyced$test"; say "Generating control file $t ..\n"; rename $t, "$t~"; # HerraBRE: the init script uses "su -s" which os x doesn't understand. # # alright, so now i could let it do something else in case of OS X # but I need to know (1) how to recognize OSX (2) what to put here! $su = 'su -s'; # or die.. mention at this point that we should # probably be running as root? open O, '>', $t or die "Cannot write to $t"; print O <<X; #!/bin/sh # # CAUTION! This file has been generated using $0. # Don't edit unless you are no longer going to use the configuration tool. # Edit $conf instead, then run $0 again. test -x $base/bin/psyced || exit 0 case "\$1" in start) echo -n "Starting psyced in background ... " # in nei's environment a zsh would be started to execute the # psyced script. so he suggests to add a -s parameter. in our # environment both strategies work. $su /bin/sh $user $base/bin/psyced & # some distributions no longer have /bin/sh in /etc/shells. # use a better distribution if they don't! :) # this script has to remain posix compatible. bash is no option here. echo "OK" ;; stop) rm $sandbox/data/.autorestart if test -r $sandbox/data/$driver.pid then echo -n "Instructing psyced to gently shut down ... " kill -1 \`cat $sandbox/data/$driver.pid\` # this should be enough ideally # but we can remove the pid file so a # second stop call will kill bill rm $sandbox/data/$driver.pid && echo "OK" else echo -n "Killing psyced brutally ... " kill -1 \`cat $sandbox/data/$driver.pid\` >/dev/null 2>&1 kill -3 \`cat $sandbox/data/psyced.pid\` && echo "OK" fi ;; kill) echo -n "Killing psyced brutally ... " kill -3 \`cat $sandbox/data/psyced.pid\` && echo "OK" ;; restart) echo -n "Instructing psyced to restart ... " kill -1 \`cat $sandbox/data/$driver.pid\` && echo "OK" ;; status) ;; *) echo "Usage: \$0 {start|stop|restart|kill}" exit 1 ;; esac X close O; chmod 0744, $t; if ($c{_basic_list_script_init}) { foreach (split /\s+/, $c{_basic_list_script_init}) { say "... copying to $_\n"; `cp "$t" "$_"`; } } chdir "$sandbox/net/place" or die "Where is my net/place? $!"; ## start of former archetype.pl # generate psyced place models using combinations of archetype flags my @opts, @predef, %map; open(I, "archetype.gen") or die <<X; $0: Cannot open archetype.gen in $sandbox/net/place. X while (<I>) { if (m!define\s(\w+)\s*//\s\[(.)\]!) { if ($2 eq '+') { push @predef, $1; } else { $map{$2} = $1; push(@opts, $2); } } elsif (/^#endif/) { last; } } close I; print "Generating archetype place models...\n"; # print join("\n", @opts), "\n\n"; # print "$_\n" foreach ( %map ); open (O, ">../include/place.i") or die $!; print O <<X; // generated by '$0': place.i for place.gen X my $file = ''; foreach $o (@opts) { print O <<X; #ifdef $map{$o} # define O$o "$o" #else # define O$o "" #endif X $file .= " O$o"; } print O <<X; inherit NET_PATH "place/_"$file; X $predef = ''; foreach $p (@predef) { $predef .= "#define $p\n"; } # open(IG, ">.cvsignore") or print <<X; #Warning: cannot create .cvsignore. Well, doesn't matter. #X # # funny how it likes to see itself in there # print IG ".cvsignore\n"; my $bits = 1 + $#opts; for ($v = 1 << $bits; $v;) { $v--; $f = ''; $model = ''; for ($i = 0; $i < $bits; $i++) { if ($v & 1 << $i) { my $o = $opts[$i]; $f .= $o; $model .= "#define $map{$o}\n"; } } # special case: skip all exports without history next if $f =~ /^e/; # same special case is also handled in place.gen printf " (%02d _%s)", $v, $f; #print " ($v _$f)"; # print IG "_$f.c\n"; open (O, ">_$f.c") or die $!; # proud and noisy.. for a week or two at least ;) #echo loading model '_$f' generated by '$0' print O <<X; // model '_$f' generated by '$0' #define ESSENTIALS $model #include "archetype.gen" X # used to output $predef but it's easier to # have archetype.gen sort out ESSENTIALS close O; } # close IG; print "\nModel creation completed successfully.\n"; ## end of former archetype.pl say "\nCaution: You may have to completely shut down and restart psyced\n"; say "to ensure the newly generated start-up scripts are actually used.\n"; # vim:ts=8