#!/usr/bin/perl -w use strict; use warnings; use User::grent; use User::pwent; # use IO::Handle; sub usage() { return "usage: $0 acct logacct group /confdir /devpath baudrate"; } # debug is a subroutine with one argument (should be a reference to a hash): sub debugargs($) { # dereference the first argument and find the hash at the other end: my %arg = %{ shift() }; print( " Owning Account: $arg{acct} Logging Account: $arg{logacct} Shared Group: $arg{group} Config Dir: $arg{confdir} Device Path: $arg{devpath} Baud Rate: $arg{baudrate} "); } sub in_array ($$) { my ($str, $aref) = @_; my $test; for $test (@$aref) { return 1 if ($test eq $str); } return 0; } sub ttyfail($$) { my ($e,$v) = @_; print(STDERR "$0: Fatal: ".$e."\n"); exit $v; } sub ttywarn($) { my ($e,$v) = @_; print(STDERR "$0: Warning: ".$e."\n"); } sub parseargs(@) { my ($acct, $logacct, $ownergroup, $dirpath, $devpath, $baudrate) = @_; if (!defined($baudrate)) { return 0; } # strip trailing slashes from the dirname: $dirpath =~ s|/*$|| ; my $a = getpwnam($acct) || ttyfail("Proposed account '$acct' does not exist.", 1); my $la = getpwnam($logacct) || ttyfail("Proposed logging account '$logacct' does not exist.", 1); my $g = getgrnam($ownergroup) || ttyfail("Proposed shared group '$ownergroup' does not exist.", 1); if (($a->gid != $g->gid) && (! in_array($a->name, $g->members()))) { ttyfail("Account '$acct' is not in Shared Group '$ownergroup'", 1); } return { acct => $acct, logacct => $logacct, group => $ownergroup, confdir => $dirpath, devpath => $devpath, baudrate => $baudrate, acctuid => $a->uid, logacctuid => $la->uid, gid => $g->gid, }; # use \%args to return a reference to the %args hash. # debugargs( \%args ); } my $ax = parseargs(@ARGV); if (! $ax) { print(STDERR usage()."\n"); exit 1; } -e ${$ax}{'confdir'} && ttyfail("Proposed configuration directory ${$ax}{confdir} already exists.", 1); mkdir(${$ax}{confdir}) || ttyfail("Couldn't create service directory ${$ax}{confdir}.", 1); mkdir(${$ax}{confdir}.'/env') || ttyfail("Couldn't create environment directory ${$ax}{confdir}/env.", 1); # set up the log directory, where the logs will go: mkdir(${$ax}{confdir}.'/log') || ttyfail("Couldn't create log service directory ${$ax}{confdir}/log.", 1); mkdir(${$ax}{confdir}.'/log/main') || ttyfail("Couldn't create log directory ${$ax}{confdir}/log/main.", 1); chown(${$ax}{logacctuid}, ${$ax}{gid}, ${$ax}{confdir}.'/log/main') || ttywarn("Couldn't chgrp log directory ${$ax}{confdir}/log/main to group '${$ax}{group}'."); chmod(0750, ${$ax}{confdir}.'/log/main') || ttyfail("Couldn't set privileges on log directory", 1); # set up the target directory: this is where the symlink will be # created that members of the group will use in their # minicom config: mkdir(${$ax}{confdir}.'/target') || ttyfail("Couldn't create target directory ${$ax}{confdir}/target.", 1); chown(${$ax}{acctuid}, ${$ax}{gid}, ${$ax}{confdir}.'/target') || ttyfail("Couldn't chown target directory ${$ax}{confdir}/target to owner '${$ax}{acctuid}'.", 1); chmod(0750, ${$ax}{confdir}.'/target') || ttyfail("Couldn't set privileges on target directory", 1); open(my $logrun, '>', ${$ax}{confdir}.'/log/run') || ttyfail("Couldn't create runfile for logger.", 1); print($logrun '#!/bin/sh exec setuidgid '.${$ax}{logacct}.' multilog t \'+*\' \'-*<*\' ./main '); close($logrun); chmod(0755, ${$ax}{confdir}.'/log/run') || ttyfail("could not set executable bit on log run file", 1); open(my $run, '>', ${$ax}{confdir}.'/run') || ttyfail("Couldn't create runfile for socat service.", 1); # i found setuidgid too restrictive because it stripped all but the primary group: #my $cmd = 'exec setuidgid '.${$ax}{acct}.' softlimit -o6 -d "$DATALIMIT" socat -x -d "GOPEN:$SRCDEV" "PTY:,link=./target/device,mode=0660,group=$SHAREGROUP"'; my $cmd = 'exec su '.${$ax}{acct}.' --shell /bin/sh --command "softlimit -o6 -d $DATALIMIT socat -x -d FILE:$SRCDEV,b$BAUD,echo=0,raw PTY:,link=./target/device,mode=0660,group=$SHAREGROUP"'; print($run '#!/bin/sh exec 2>&1 exec envdir ./env sh -c \' echo [$$] '.$cmd.' '.$cmd.' \' '); close($run); chmod(0755, ${$ax}{confdir}.'/run') || ttyfail("could not set executable bit on service run file", 1); # what other checks should we make? # FIXME: check that the source device is present and readable by the acct. # FIXME: check that baud rate is supported by socat my $cnf; open($cnf, '>'.${$ax}{confdir}.'/env/BAUD') && print($cnf ${$ax}{baudrate}) && close($cnf) || ttyfail("Failed to write baud rate to config file.", 1); # defaulting to 3MB: open($cnf, '>'.${$ax}{confdir}.'/env/DATALIMIT') && print($cnf '3000000') && close($cnf) || ttyfail("Failed to write memory limit to config file.", 1); open($cnf, '>'.${$ax}{confdir}.'/env/SHAREGROUP') && print($cnf ${$ax}{group}) && close($cnf) || ttyfail("Failed to write shared group to config file.", 1); open($cnf, '>'.${$ax}{confdir}.'/env/SRCDEV') && print($cnf ${$ax}{devpath}) && close($cnf) || ttyfail("Failed to write source device to config file.", 1); # finish cleanly: exit 0;