1
0
Fork 0
mirror of git://git.psyced.org/git/psyced synced 2024-08-15 03:25:10 +00:00
psyced/bin/psyconf
2011-05-20 18:03:28 +02:00

766 lines
22 KiB
Perl
Executable file

#!/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 either ./psyced.ini
or /etc/psyc/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*$/;
$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 $execs = "$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 $execs/$driver\n";
unless (-x $execs && -x "$execs/$driver") {
$execs = "$base/bin-$_type_system";
say "Looking for driver in $execs/$driver\n";
unless (-x $execs && -x "$execs/$driver") {
$execs = "$base/bin-linux";
say "Looking for driver in $execs/$driver\n";
unless (-x $execs && -x "$execs/$driver") {
$execs = "$base/bin";
say "Looking for driver in $execs/$driver\n";
unless (-x $execs && -x "$execs/$driver") {
$execs = "/usr/local/sbin";
say "Looking for driver in $execs/$driver\n";
unless (-x $execs && -x "$execs/$driver") {
$execs = "/usr/sbin";
say "Looking for driver in $execs/$driver\n";
die <<X unless -x $execs && -x "$execs/$driver";
Could not find any directory containing $driver.
X
}
}
}
}
}
say "Found driver in $execs/$driver\n";
# is it really that hard?
die "but $execs/erq is missing\n" unless -x "$execs/erq";
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 = "$execs/$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#^/#;
print STDERR <<X unless -r $t;
WARNING: Key file $t does not exist.
X
$psyced .= " --tls-key $t";
}
if ($t = $c{_basic_path_PEM_certificate}) {
$t = "$config/$t" unless $t =~ m#^/#;
print STDERR <<X unless -r $t;
WARNING: Certificate file $t does not exist.
X
$psyced .= " --tls-cert $t";
}
if ($t = $c{_basic_path_trust}) {
$t = "$config/$t" unless $t =~ m#^/#;
print STDERR <<X unless -r $t;
WARNING: Trust directory $t does not exist.
X
$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 ""
echo "Fetching update from public git repository..."
echo ""
git fetch origin
echo ""
echo "You can now look at 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
# this \$0 is for psyconf, not for psyced.. don't escape it
echo ""
echo "It may be a good idea to run ${hi}$0${lo} again before you continue."
fi
exit
fi
if test "\$1" = "-d"
then
## cvs -q diff -ur HEAD|\$PAGER
git diff master..origin/master | \$PAGER
exit
fi
if test "\$1" = "-m"
then
git stash save 'changes stashed automatically by psyced -m'
git merge -s resolve origin
# restore local changes?
# git stash apply
echo ""
echo "If you had local changes to your files you can now restore them using"
echo "${hi}git stash apply${lo}"
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 $execs/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.
#
### BEGIN INIT INFO
# Provides: psyced
# Required-Start: \$local_fs \$remote_fs
# Required-Stop: \$local_fs \$remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: S 0 1 6
# Short-Description: psyced
# Description: psyced, the Enhanced PSYC Daemon
### END INIT INFO
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