collapse collapse
* User Info
 
 
Welcome, Guest. Please login or register.
* Search

* Board Stats
  • stats Total Members: 989
  • stats Total Posts: 18363
  • stats Total Topics: 2500
  • stats Total Categories: 7
  • stats Total Boards: 35
  • stats Most Online: 1144

Author Topic: Linux multiuser startup/shutdown/status script  (Read 4500 times)

0 Members and 1 Guest are viewing this topic.

Offline Hyde

  • BeBot Apprentice
  • ***
  • Posts: 92
  • Karma: +0/-0
Linux multiuser startup/shutdown/status script
« on: January 19, 2009, 07:12:41 am »
So I finally got around to creating a script to help manage my bots. Since we have 3 or 4 bot admins things can get a little odd, and it isn't trivial to train someone new who isn't savvy with Linux on how to start/stop bots. This script means to fix that.

If you already have a working solution, I'm not suggesting this is better :) Just giving it to those who are in need.

Just to be clear, if set up and working properly, you can run your bot processes as a non-privileged user but have the commands to start/stop/etc the bots executable by multiple other accounts. So you could log in, type `bebot start botname` and your friend 12 hours later could `bebot stop botname` without needing to share accounts and without needing to even know the password to the bot account. That way the bots can be even managed by people who do not have access to the bot code. And without needing to understand the details of 'screen' syntax nor 'sudo'.

I tried to put as much information in the comments of the script as I could to make it easier to get it up and running. A couple of notes:

1) This won't work on Windows.

2) It requires you to allow some setuid execution. If you're a security freak you may want to skip it ... but I'm not doing any setuid to root except for 'screen' which is built for it.

3) The script works very similar to an init script. In fact with some tweaking it would probably work as one.

4) It will do fairly decent error checking.
... makes sure that it is running as the correct linux user
... makes sure that a bot is valid by checking for its config file
... will not start the same name bot twice
... will not do ANYTHING if it detects the same name bot twice (you have to clean it up)

5) Logs all activity in /tmp/[bot user name].log ... example of the format in the third post of this thread.

« Last Edit: January 19, 2009, 07:43:48 am by Hyde »
Doctorhyde/Jexyll/Goraud@RK2

Offline Hyde

  • BeBot Apprentice
  • ***
  • Posts: 92
  • Karma: +0/-0
Re: Linux multiuser startup/shutdown/status script
« Reply #1 on: January 19, 2009, 07:13:27 am »
script:

Code: [Select]
#!/usr/bin/perl

# "Simple" bot start/stop/status/view script

# Author:       Doctorhyde@RK2 / Hyde@BeBot Forums
# Revisions:
# * 2009-01-17.2 ... initial version

# Forums URL:
# http://bebot.link/coding-and-development-discussion/linux-multiuser-startupshutdownstatus-script/

# USAGE:
# bebot
# ... prints a list of all named screens owned by the bot
#     user as well as all configured bots.
# bebot [start|stop|status|view]
# ... start will start a bot
# ... stop will stop a running bot
# ... status will tell you where the bot's config files
#     are located and what the PID of its screen
# ... view will attach you to the screen of the bot

# NOTES:
# * written on Ubuntu 8.04 LTS Server, ymmv
# * IF you always start your bots in a named screen like
#   this script does then it should never start a duplicate
#   bot (duplicate bots will knock each other offline in a loop).
#   If you start a bot using a different setup then this script
#   probably WILL cause a duplicate until you fix it.
#   (wtb a bot.lock structure for BeBot :)
# * This script won't work if you have 2 bots of the
#   exact same name on multiple dimensions. Just wasn't
#   something I worried about.
# * The bot log file referenced by 'status' doesn't really check to
#   make sure you have logging turned on.
# * This script will log to /tmp/$botusername.log

# BACKGROUND:
#   If you login directly to your bot account then you maye
#   not need the setuid stuff. However we have multiple admins
#   so the goal was a single command that all could share
#   to control an unprivileged bot user.

# SETUP:
# * expects to have user permissions and setuid all correct:
#   ... your system must have sperl (suid perl)
#       for Ubuntu you can `apt-get install perl-suid`
#   ... "screen" must be setuid to root for multiuser access
#   ... bots should be run as a separate user (I use "bebot")
#   ... your bot user and login user(s) should be a member
#       shared group (I use "bots")
#   ... this script should be group-owned by that same group
#   ... this script should be setuid to your bot user
#   ... your bot files should be owned by your bot user
#   ... your bot user should have a .screenrc allowing your
#       login users permission to its screens
#       http://www.delorie.com/gnu/docs/screen/screen_25.html

# EXAMPLE (for an Ubuntu 8.x system):
# * I have a login account called "hyde", a bot account called "bebot".
# * Both are members of the group "bots"
# * My login account has permissions to use sudo as root and other users
# * "bebot"'s home is /home/bots/bebot
# * my bebot code in ~bebot/current
# * This script is /usr/local/bin/bebot
# * This script is setuid and owned by "bebot" and group "bots"
#   `sudo chown bebot:bots /usr/local/bin/bebot`
#   `sudo chmod 6750 /usr/local/bin/bebot`
# * NOTE: Remember to set the permissions whenever you edit the script
#         since when you save it it loses the setuid bit
# * Screen is set to allow multiuser mode
#   `sudo chmod u+s /usr/bin/screen`
#   `sudo chmod 0755 /var/run/screen`
# * "bebot" has a multiuser .screenrc (replace "user1", etc with your login users)
#   `sudo -u bebot echo 'multiuser on' >> ~bebot/.screenrc`
#   `sudo -u bebot echo 'acladd root,user1,user2,user3' >> ~bebot/.screenrc`
#
# At this point, unless I forgot something, my system was running

# Parameters to configure for your system:
$botusername = "bebot";                 # your bot's linux user
$botuserdir = "/home/bots/bebot";       # your bot's user directory (for .screenrc)
$botdir = "$botuserdir/current";        # contains your bot's StartBot.php
$command_php = "/usr/bin/php";          # point to your PHP binary
$command_kill = "/bin/kill";            # point to your kill binary
$command_screen = "/usr/bin/screen";    # point to your screen binary

# Hopefully nothing below this needs editing
$help = "$0 [start|stop|status|screen] [name of bot]\n(run with no arguments to list all bots)";

# untaint the ENV var before we delete it
if ($ENV{'SSH_TTY'} =~ /^(.*)$/) { $terminal = $1; }
delete @ENV{qw(PATH IFS CDPATH ENV BASH_ENV)};   # Make %ENV safer

# grab our UIDs, we need these later
$uid_real = $<;
$uid_effective = $>;

### Initial Error Conditions
# We should only be starting/stopping bots as the unprivileged bot user account:
($scriptusername,$scriptuserpass,$scriptuid,@JUNK) = getpwuid($uid_effective);
($realusername,$realuserpass,$realuid,@JUNK) = getpwuid($uid_real);
if ($botusername ne $scriptusername) {
  &error("Bots must only be started as user '$botusername'!");
}
# open the log file
open(LOGFILE,">> /tmp/$botusername.log");
# get a datestamp
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
$datestamp = sprintf "%4d-%02d-%02d %02d:%02d:%02d",$year+1900,$mon+1,$mday,$hour,$min,$sec;

# We must have exactly 2 commands to work unless we want a list of bots running
if ($#ARGV != 1) {
# if the first argument is "list" then show all running bots
  if (! $ARGV[0]) {
    print "Named screens for user '$botusername'\n";
    print "     [screen] [pid]\n";
# build a list of all running named SCREENS (not just bots) for the bot user
    open(SCREEN,"$command_screen -ls $botusername/ |");
    @screens = <SCREEN>;
    for $screen (@screens) {
      if ($screen =~ /^[\s]*([0-9]{1,})\.([A-Za-z0-9]*)[\s]{1,}/) {
        printf " %12s", $2;
        print " $1\n";
      }
    }
    print "\nKnown Bots:\n";
    opendir(BOTCONFDIR,"$botdir/conf");
    @botconfs = grep(/.*\.Bot\.conf/,readdir(BOTCONFDIR));
    for $bot (@botconfs) {
      if ($bot =~ /^([A-Za-z0-9]{1,})\.Bot\.conf$/) { printf " %12s\n", $1; }
    }
    print "\n";
    exit;
  } else {
    &error("Wrong number of commands");
  }
}
# the first command must be a known command:
if ($ARGV[0] =~ /^(start|stop|status|view)$/) {
  $botcommand = $1;
} else {
  &error("Unknown command as first argument");
}
# the second command must be a configured bot:
# format the botname from the argument
# untaint the bot name
if ($ARGV[1] =~ /^([A-Za-z0-9]*)$/) { # bot should ONLY contain chars/#s
  $botnamelower = lc($1);
  $botnamecap = $botnamelower;
  $botnamecap =~ s/\b(\w)/\u$1/;
} else {
  &error("Invalid bot name given. Untainting failed.");
}
opendir(BOTCONFDIR,"$botdir/conf");
@botconfs = grep(/.*\.Bot\.conf/,readdir(BOTCONFDIR));
if (! grep(/^$botnamecap\.Bot\.conf$/,@botconfs)) {
  &error("Can't locate bot names $botnamecap");
}

### Main program
# make sure we clean up our dead screens:
&logdata(0,"COMMAND - $0 $botnamelower $botcommand");
$botpid = &screentest; # gets the PID of the bot if it is running in a screen already
if ($botpid =~ /^([0-9]{1,})$/) { $botpid = $1; } # untaint for setuid
# time to go through each of the commands
if ($botcommand eq "start") {
  &screenprep;
# make sure we don't already have a running bot of that name:
  if ($botpid >= 1) { # negative PIDs should NOT be acted on, and 0 means not running.
    &error("Bot '$botnamecap' already running as process $botpid.");
  } else {
# if we got here then we should start the bot
    $startdir = cwd;
    chdir($botdir);
    @command_bot = ("$command_screen","-c","$botuserdir/.screenrc","-dmS","$botnamelower","$command_php","StartBot.php","$botnamecap");
    system(@command_bot) == 0 or die "system(@command_bot) failed: $?";
    chdir($startdir);
    $botpid = &screentest;
    &logdata(1,"START - Bot ($botnamecap) started on PID $botpid.");
  }
} elsif ($botcommand eq "stop") {
  &screenprep;
  if ($botpid >= 1) {
    @command_killbot = ("$command_kill","-9","$botpid");
    system(@command_killbot) == 0 or die "system(@command_killbot) failed: $?";
    @command_wipe = ("$command_screen","-wipe");
    system(@command_wipe);
    &logdata(1,"STOP - Bot ($botnamecap) on PID $botpid was killed.");
  } else {
    if ($botpid == 0) {
      &error("No screen running for $botnamelower was found.");
    } else {
      &error("PID ($botpid) may be negative, I refuse to kill that.");
    }
  }
} elsif ($botcommand eq "status") {
  &screenprep;
  print "STATUS:\n";
  print "* config file:         $botdir/conf/$botnamecap.Bot.conf\n";
  if (-f "$botdir/conf/$botnamecap.MySQL.conf") {
    print "* MySQL config: $botdir/conf/$botnamecap.MySQL.conf\n";
  }
  if ($botpid) {
    print "* PID:               $botpid\n";
    print "  To view this bot run:\n    \`$0 view $botnamelower\`\n";
  } else { print "* Bot does NOT appear to be running (in a screen at least)\n"; }
  print "\nIf enabled, logs should be in $botdir/log/$botnamelower\@RK\*\n";
} elsif ($botcommand eq "view") {
  print "VIEW:\n";
  print "This will, if your user has permissions, allow you to view the\n";
  print "'screen' of the bot. To exit this view press Control+A then D \n";
  print "(Control+A allows you to send commands, D is for 'detach').\n\n";
  print "While you CAN kill the bot by pressing Control+C, it is recommended\n";
  print "that you kill the bot via:\n`$0 stop $botnamelower`.\n\n";
  print "Press RETURN to view the bot ...\n";
  $viewme = <STDIN>;
# ugly hack to open our TTY for the view command:
  if ($terminal) { # continuing the ugly TTY hack
    $> = $uid_real;
    @command_pts = ("/bin/chmod","g+r","$terminal");
    system(@command_pts) == 0 or die "system(@command_pts) failed: $?";
    @command_view = ("$command_screen","-rx","$botusername/$botnamelower");
    system(@command_view) == 0 or die "system(@command_view) failed: $?";
    @command_pts = ("/bin/chmod","g-r","$terminal");
    system(@command_pts) == 0 or die "system(@command_pts) failed: $?";
  } else {
    &error("We expect to only run this script from a SSH TTY.");
  }
}

print "\n";

### Subroutines
sub error { # generic "print error then quit" routine
  print "Usage:\n$help\n\n";
  &logdata(1,"ERROR - $_[0]");
  exit;
}

sub screentest { # returns "1" if the bot is running, "0" if not
  open(SCREEN,"$command_screen -ls $botusername/ |");
  @screens = <SCREEN>;
  for $screen (@screens) {
# get the pid that matches the requested bot name if it exists
# NOTE: multiple instances of the same bot will error
    if ($screen =~ /^[\s]*([0-9]{1,})\.$botnamelower[\s]{1,}/i) {
      if ($returnpid) {
        &error("Detected multiple instances of bot '$botnamelower'!\nClean up by hand.");
      } else {
        $returnpid = $1;
      }
    }
  }
  if ($returnpid) { return $returnpid; }
}

sub screenprep { # switch to the effective UID and wipe any dead screens
  $< = $uid_effective; # swap UID to effective so we start processes as the right user (except view)
  open(WIPE,"$command_screen -wipe $botusername/ |");
  @wipe = <WIPE>;
  for $wiped (@wipe) {
    if ($wiped =~ /^[\s]*([0-9]{1,})\.([A-Za-z0-9]*)[\s]*\(Removed\)/) {
      print "\n... WARNING: Found bot '$2' on pid '$1' dead, wiped from screen!\n\n";
      if ($botpid == $1) { undef($botpid); }
    }
  }
}

sub logdata { # print output to the log and screen
  if ($_[0]) { print "$_[1]\n\n"; }
  print LOGFILE "$datestamp - $realusername - $_[1]\n";
}
« Last Edit: January 19, 2009, 07:22:00 am by Hyde »
Doctorhyde/Jexyll/Goraud@RK2

Offline Hyde

  • BeBot Apprentice
  • ***
  • Posts: 92
  • Karma: +0/-0
Re: Linux multiuser startup/shutdown/status script
« Reply #2 on: January 19, 2009, 07:14:00 am »
NOTE: Look at the name that is quoted for the actual command used.

Starting a bot:
Quote from: `bebot start botname`
START: Bot (botname) started on PID 24015.


Listing all running screens for the bot user:
Quote from: `bebot`
Named screens for user 'bebot':
     [screen] [pid]
         botname 24015
        botname2 24003
        botname3 24000

Known Bots:
       Botname
      Botname1
      Botname2
      Botname3


Checking the status of a single bot:
Quote from: `bebot status botname`
STATUS:
* config file:  /home/bots/bebot/current/conf/botname.Bot.conf
* MySQL config: /home/bots/bebot/current/conf/botname.MySQL.conf
* PID:          24015
  To view this bot run:
    `/usr/local/bin/bebot view botname`

If enabled, logs should be in /home/bots/bebot/current/log/botname@RK*


Viewing a bot's screen
Quote from: `bebot view botname`
VIEW:
This will, if your user has permissions, allow you to view the
'screen' of the bot. To exit this view press Control+A then D
(Control+A allows you to send commands, D is for 'detach').

While you CAN kill the bot by pressing Control+C, it is recommended
that you kill the bot via:
`/usr/local/bin/bebot stop botname`.

Press RETURN to view the bot ...
(at this point the screen session for the bot will appear, just like you had done `screen -rx bebot/botname`)


Stopping a bot:
Quote from: `bebot stop botname`
There are screens on:
        24015.botname      (Removed)
        24003.botname2    (Detached)
        24000.botname3      (Detached)
1 socket wiped out.
2 Sockets in /var/run/screen/S-bebot.

STOP: Bot (botname) on PID 24015 was killed.


The log file:
(format is "[date] [time] - [real username] - [message type] - [message]")
Quote from: /tmp/bebot.log
2009-01-19 05:24:06 - hyde - COMMAND - /usr/local/bin/bebot botname start
2009-01-19 05:24:06 - hyde - ERROR - Bot 'Botname' already running as process 5247.
2009-01-19 05:25:37 - hyde - COMMAND - /usr/local/bin/bebot botname stop
2009-01-19 05:25:37 - hyde - STOP - Bot (Botname) on PID 5247 was killed.
2009-01-19 05:25:57 - hyde - COMMAND - /usr/local/bin/bebot botname start
2009-01-19 05:25:57 - hyde - START - Bot (Botname) started on PID 5900.
2009-01-19 05:27:27 - hyde - COMMAND - /usr/local/bin/bebot botname status
« Last Edit: January 19, 2009, 07:25:01 am by Hyde »
Doctorhyde/Jexyll/Goraud@RK2

Offline Hyde

  • BeBot Apprentice
  • ***
  • Posts: 92
  • Karma: +0/-0
Re: Linux multiuser startup/shutdown/status script
« Reply #3 on: January 19, 2009, 08:06:13 am »
PS. Please post Q&A on this thread ... not in-game. I don't handle tech support requests when playing :)
Doctorhyde/Jexyll/Goraud@RK2

Offline Alreadythere

  • BeBot Maintainer
  • BeBot Hero
  • ******
  • Posts: 1288
  • Karma: +0/-0
Re: Linux multiuser startup/shutdown/status script
« Reply #4 on: January 20, 2009, 06:12:44 pm »
Looks great (I haven't run it though).

One small comment: shouldn't you put the log file some place else? /tmp/ doesn't seem like the best place to me. Not sure what a good place would be though, especially if you want to allow all users read rights to it.

Offline Hyde

  • BeBot Apprentice
  • ***
  • Posts: 92
  • Karma: +0/-0
Re: Linux multiuser startup/shutdown/status script
« Reply #5 on: January 20, 2009, 10:11:26 pm »
Not sure what a good place would be though, especially if you want to allow all users read rights to it.

That was the main issue. It was late and I didn't want to deal with setting the right user/group permissions on the log file :) It should be doable to create the file in the bot user's directory and set the group ownership to allow bot admins to read.

I've got a couple of other adjustments I'm going through and will update the code post above once I've got that all worked out.
Doctorhyde/Jexyll/Goraud@RK2

Offline Alreadythere

  • BeBot Maintainer
  • BeBot Hero
  • ******
  • Posts: 1288
  • Karma: +0/-0
Re: Linux multiuser startup/shutdown/status script
« Reply #6 on: January 24, 2009, 10:14:43 pm »
It was late and I didn't want to deal with setting the right user/group permissions on the log file :)
Perfect excuse! :D

Offline Pharexys

  • BeBot Apprentice
  • ***
  • Posts: 117
  • Karma: +0/-0
Re: Linux multiuser startup/shutdown/status script
« Reply #7 on: May 17, 2009, 12:00:48 am »
hyde when you got time, help me set up ur script plax :D
i didnt get it how you set bots path's

 

* Recent Posts
[AoC] special char for items module by bitnykk
[February 09, 2024, 09:41:18 pm]


0.8.x updates for AoC by bitnykk
[January 30, 2024, 11:16:08 pm]


0.8.x updates for AO by bitnykk
[January 30, 2024, 11:15:37 pm]


BeBot still alive & kicking ! by bitnykk
[December 17, 2023, 12:58:44 am]


Bebot and Rasberry by bitnykk
[November 29, 2023, 11:04:14 pm]

* Who's Online
  • Dot Guests: 753
  • Dot Hidden: 0
  • Dot Users: 0

There aren't any users online.
* Forum Staff
bitnykk admin bitnykk
Administrator
Khalem admin Khalem
Administrator
WeZoN gmod WeZoN
Global Moderator
SimplePortal 2.3.7 © 2008-2024, SimplePortal