Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 18 additions & 13 deletions manage-procs.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Increase verbosity level. (may be specified multiple times)

# COMMANDS

**manage-procs add** \[-h\] \[-f\] \[-A\] \[-C *dir*\] \[-P *port*\] \[-U *user*\] \[-G *group*\] *name* *command*…​
**manage-procs add** \[-h\] \[-f\] \[-A\] \[-C *dir*\] \[-P *port*\] \[-U *user*\] \[-G *group*\] *name* *command*…​
Create a new procServ instance.

**-h, --help**
Expand All @@ -86,14 +86,19 @@ User name for instance to run as.
**-G, --group** *groupname*
Group name for instance to run as.

**name**
**--grace-period** *n*
Grace period (in seconds) that procServ will wait for the child to shut
down after receiving a termination signal. (See procServ(1) for more
details.)

**name**
Instance name.

**command…​**
**command…​**
The remaining line is interpreted as the command (with arguments) to run
inside the procServ instance.

**manage-procs remove** \[-h\] \[-f\] *name*
**manage-procs remove** \[-h\] \[-f\] *name*
Remove an existing procServ instance from the configuration.

**-h, --help**
Expand All @@ -102,30 +107,30 @@ Show a help message and exit.
**-f, --force**
Remove without asking for confirmation.

**name**
**name**
Instance name.

**manage-procs start** \[-h\] \[*pattern*\]
**manage-procs start** \[-h\] \[*pattern*\]
Start procServ instances.

**-h, --help**
Show a help message and exit.

**pattern**
**pattern**
Pattern to match existing instance names against. (default: "\*" = start
all procServ instances)

**manage-procs stop** \[-h\] \[*pattern*\]
**manage-procs stop** \[-h\] \[*pattern*\]
Stop procServ instances.

**-h, --help**
Show a help message and exit.

**pattern**
**pattern**
Pattern to match existing instance names against. (default: "\*" = stop
all procServ instances)

**manage-procs attach** \[-h\] *name*
**manage-procs attach** \[-h\] *name*
Attach to the control port of a running procServ instance.

For this, manage-procs is using one of two existing CLI client
Expand All @@ -137,10 +142,10 @@ For both connection types, press `^D` to detach from the session.
**-h, --help**
Show a help message and exit.

**name**
**name**
Instance name.

**manage-procs list** \[-h\] \[--all\]
**manage-procs list** \[-h\] \[--all\]
List all procServ instances.

**-h, --help**
Expand All @@ -149,7 +154,7 @@ Show a help message and exit.
**--all**
Also list inactive instances.

**manage-procs status** \[-h\]
**manage-procs status** \[-h\]
Report the status of all procServ instances.

**-h, --help**
Expand Down
61 changes: 57 additions & 4 deletions procServ.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#endif /* __CYGWIN__ */

#include "procServ.h"
#include "processClass.h"

// Wrapper to ignore return values
template<typename T>
Expand Down Expand Up @@ -174,6 +175,7 @@ void printHelp()
" -d --debug debug mode (keeps child in foreground)\n"
" -e --exec <str> specify child executable (default: arg0 of <command>)\n"
" -f --foreground keep child in foreground (interactive)\n"
" -G --grace-period <n> wait <n> seconds for child to shut down\n"
" -h --help print this message\n"
" --holdoff <n> set holdoff time [sec] between child restarts\n"
" -i --ignore <str> ignore all chars in <str> (^ for ctrl)\n"
Expand Down Expand Up @@ -214,6 +216,7 @@ int main(int argc,char * argv[])
const size_t BUFLEN = 512;
char buff[BUFLEN];
std::string infofile;
unsigned int gracePeriod = 0;

time(&procServStart); // remember start time
procservName = argv[0];
Expand All @@ -235,6 +238,7 @@ int main(int argc,char * argv[])
{"debug", no_argument, 0, 'd'},
{"exec", required_argument, 0, 'e'},
{"foreground", no_argument, 0, 'f'},
{"grace-period", required_argument, 0, 'G'},
{"help", no_argument, 0, 'h'},
{"holdoff", required_argument, 0, 'H'},
{"ignore", required_argument, 0, 'i'},
Expand All @@ -261,7 +265,7 @@ int main(int argc,char * argv[])
/* getopt_long stores the option index here. */
int option_index = 0;

c = getopt_long (argc, argv, "+c:de:fhi:I:k:l:L:n:op:P:qVwx:",
c = getopt_long (argc, argv, "+c:de:fG:hi:I:k:l:L:n:op:P:qVwx:",
long_options, &option_index);

/* Detect the end of the options. */
Expand Down Expand Up @@ -310,6 +314,14 @@ int main(int argc,char * argv[])
stampFormat = strdup(optarg);
break;

case 'G':
k = atoi(optarg);
if (k < 0) {
k = 0;
}
gracePeriod = (unsigned int) k;
break;

case 'h': // Help
printHelp();
exit(0);
Expand Down Expand Up @@ -594,6 +606,8 @@ int main(int argc,char * argv[])
strncat(infoMessage1, buff, INFO1LEN-strlen(infoMessage1)-1);
}

time_t stopAt = 0;

firstRun = true;
// Run here until something makes it die
while ( ! shutdownServer )
Expand Down Expand Up @@ -621,8 +635,27 @@ int main(int argc,char * argv[])
timeout.tv_sec = 0; // select() timeout: 0.5 sec
timeout.tv_nsec = 500000000l;

if (stopAt != 0) {
time_t now = time(0);
if (now < stopAt) {
long remaining = (long)(stopAt - now);
if (remaining < 1) {
timeout.tv_sec = 0;
timeout.tv_nsec = 500000000l;
} else if (remaining < timeout.tv_sec || (remaining == timeout.tv_sec && 0 < timeout.tv_nsec)) {
timeout.tv_sec = remaining;
timeout.tv_nsec = 0;
}
} else {
timeout.tv_sec = 0;
timeout.tv_nsec = 0;
}
}

ready = pselect(nFd, &fdset, NULL, NULL, &timeout, &sigset_pselect);

time_t now = time(0);

// Handle signals for which signal handlers were called while in pselect.

if (sigPipeSet) {
Expand All @@ -635,7 +668,14 @@ int main(int argc,char * argv[])
sigTermSet = 0;
PRINTF("SigTerm received\n");
processFactorySendSignal(killSig);
shutdownServer = true;
if(killSig==SIGKILL || gracePeriod<=0) {
shutdownServer = true;

} else if(!stopAt) {
restartMode = oneshot; // prevent restart
stopAt = now + gracePeriod; // wait a bit for child to stop
PRINTF("Start child cleanup timer %u sec.\n", gracePeriod);
}
}

if (sigHupSet) {
Expand All @@ -651,8 +691,9 @@ int main(int argc,char * argv[])

// Pick up the process item if it dies
// This call returns NULL if the process item lives
if (processFactoryNeedsRestart())
if (stopAt==0 && processFactoryNeedsRestart())
{

if ((restartMode == oneshot) && !firstRun) {
PRINTF("Option oneshot is set... exiting\n");
shutdownServer = true;
Expand All @@ -663,7 +704,7 @@ int main(int argc,char * argv[])
npi= processFactory(childExec, childArgv);
if (npi) AddConnection(npi);
if (firstRun) {
firstRun = false;
firstRun = false;
}
}
}
Expand All @@ -680,6 +721,18 @@ int main(int argc,char * argv[])
}
OnPollTimeout();
}

if(stopAt!=0) {
if(!processClass::hasRunning()) {
PRINTF("child exits\n");
shutdownServer = true;

} else if(now >= stopAt) {
PRINTF("child cleanup timer expires %ld, %ld\n", stopAt, now);
shutdownServer = true;
// connectionItem dtor will KILL
}
}
}
ttySetCharNoEcho(false);

Expand Down
27 changes: 20 additions & 7 deletions procServ.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ procServ server after the child exits. In that mode, the system daemon
must handle restarts (if required), and all clients will have to
reconnect.

When receiving a termination signal, the server will by default shut
down immediately and kill the child process. Using the **-G**
(**--grace-period**) option, the server can be made to wait up to a
specified number of seconds for the child process to shut down
gracefully before the server itself exits and kills the child. In this
mode, the server will stop restarting the child.

Any connection (control or log) can be disconnected using the client’s
disconnect sequence. Control connections can also be disconnected by
sending the logout command character that can be specified using the
Expand All @@ -100,25 +107,25 @@ plus additional debug messages to stdout.
Both control and log endpoints may be bound to either TCP or UNIX
sockets (where supported). Allowed endpoint specifications are:

**\<port\>**
**\<port\>**
Bind to either 0.0.0.0:*\<port\>* (any) or 127.0.0.1:*\<port\>*
(localhost) depending on the type of endpoint and the setting of **-r**
(**--restrict**) and **--allow** options.

**\<ifaceaddr\>:\<port\>**
**\<ifaceaddr\>:\<port\>**
Bind to the specified interface address and *\<port\>*. The interface IP
address *\<ifaceaddr\>* must be given in numeric form. Uses 127.0.0.1
(localhost) for security reasons unless the **--allow** option is also
used.

**unix:\</path/to/socket\>**
**unix:\</path/to/socket\>**
Bind to a named unix domain socket that will be created at the specified
absolute or relative path. The server process must have permission to
create files in the enclosing directory. The socket file will be owned
by the uid and primary gid of the procServ server process with
permissions 0666 (equivalent to a TCP socket bound to localhost).

**unix:\<user\>:\<group\>:\<perm\>:\</path/to/socket\>**
**unix:\<user\>:\<group\>:\<perm\>:\</path/to/socket\>**
Bind to a named unix domain socket that will be created at the specified
absolute or relative path. The server process must have permission to
create files in the enclosing directory. The socket file will be owned
Expand All @@ -128,7 +135,7 @@ Any of *\<user\>*, *\<group\>*, and/or *\<perm\>* may be omitted. E.g.
socket with 0660 permissions and allow the "grp" group connect to it.
This requires that procServ be run as root or a member of "grp".

**unix:@\</path/to/socket\>**
**unix:@\</path/to/socket\>**
Bind to an abstract unix domain socket (Linux specific). Abstract
sockets do not exist on the filesystem, and have no permissions checks.
They are functionally similar to a TCP socket bound to localhost, but
Expand Down Expand Up @@ -170,6 +177,12 @@ Run *file* as executable for child. Default is *command*.
Keep the server process in the foreground and connected to the
controlling terminal.

**-G, --grace-period**=*n*
Wait at most *n* seconds for the child process to shut down after
receiving a termination signal. If the child does not exit within this
period, it will be killed. (Default is 0: the server shuts down and
kills the child immediately.)

**-h, --help**
Print help message.

Expand Down Expand Up @@ -303,11 +316,11 @@ file or through a console access and logging facility (such as

# ENVIRONMENT VARIABLES

**PROCSERV_PID**
**PROCSERV_PID**
Sets the file name to write the PID of the server process into. (See
**-p** option.)

**PROCSERV_DEBUG**
**PROCSERV_DEBUG**
If set, procServ starts in debug mode. (See **-d** option.)

# KNOWN PROBLEMS
Expand Down
1 change: 1 addition & 0 deletions procServUtils/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def getconffiles(user=False):
'group':'nogroup',
'chdir':'/',
'port':'0',
'grace-period':'0',
'instance':'1',
}

Expand Down
2 changes: 2 additions & 0 deletions procServUtils/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def main(args):
chdir = conf.get(name, 'chdir')
cmd = conf.get(name, 'command')
port = conf.get(name, 'port')
grace = conf.get(name, 'grace-period')

rundir = getrundir(user=user)

Expand All @@ -57,6 +58,7 @@ def main(args):
'--chdir',chdir,
'--info-file',os.path.join(rundir, 'procserv-%s'%name, 'info'), #/run/procserv-$NAME/info
'--port', port if port != "0" else 'unix:%s/procserv-%s/control'%(rundir,name),
'--grace-period', grace,
]

if args.debug>1:
Expand Down
7 changes: 6 additions & 1 deletion procServUtils/manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ def addproc(conf, args):
if args.username: F.write("user = %s\n"%args.username)
if args.group: F.write("group = %s\n"%args.group)
if args.port: F.write("port = %s\n"%args.port)
if args.grace_period: F.write("grace-period = %s\n"%args.grace_period)
if args.environment:
env_to_string = ' '.join("\"%s\""%e for e in args.environment)
F.write("environment = %s\n"%env_to_string)
Expand Down Expand Up @@ -174,7 +175,10 @@ def delproc(conf, args):

with open(cfile) as F:
C = ConfigParser({'instance':'1'})
C.readfp(F)
if hasattr(C, 'read_file'):
C.read_file(F)
else:
C.readfp(F)

if not C.has_section(args.name):
continue
Expand Down Expand Up @@ -276,6 +280,7 @@ def getargs(args=None):
S.add_argument('-P','--port', help='telnet port')
S.add_argument('-U','--user', dest='username')
S.add_argument('-G','--group')
S.add_argument('--grace-period', help='grace period for child shutdown')
S.add_argument('-e','--environment', action='append', help='Add an environment variable')
S.add_argument('-E','--env-file', help='Environment file path')
S.add_argument('-f','--force', action='store_true', default=False)
Expand Down
2 changes: 2 additions & 0 deletions processClass.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ friend void processFactorySendSignal(int signal);
static void restartOnce ();
static bool exists() { return _runningItem ? true : false; }
virtual ~processClass();
static inline
bool hasRunning() { return _runningItem; }
protected:
pid_t _pid;
static processClass * _runningItem;
Expand Down
Loading