cronutils-1.5/0000755000175000017500000000000012207114010011475 5ustar jaqjaqcronutils-1.5/subprocess.c0000644000175000017500000001000112207114010014021 0ustar jaqjaq/* Copyright 2010 Google, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include #include #include #include #include #include "subprocess.h" int childpid = -1; /* default to a bogus pid */ volatile sig_atomic_t killed_by_us = 0; volatile sig_atomic_t fatal_error_in_progress = 0; void kill_process_group() { int pgid; killed_by_us = 1; pgid = getpgid(childpid); if (killpg(pgid, SIGTERM) < 0) { perror("killpg"); exit(EX_OSERR); } } static void termination_handler(int sig); static void termination_handler(int sig) { int old_errno; if (fatal_error_in_progress) { raise(sig); } fatal_error_in_progress = 1; if (childpid > 0) { old_errno = errno; /* we were killed (SIGTERM), so make sure child dies too */ kill_process_group(); errno = old_errno; } signal(sig, SIG_DFL); raise(sig); } void install_termination_handler(void); void install_termination_handler(void) { struct sigaction sa, old_sa; sa.sa_handler = termination_handler; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGINT); sigaddset(&sa.sa_mask, SIGHUP); sigaddset(&sa.sa_mask, SIGTERM); sigaddset(&sa.sa_mask, SIGQUIT); sa.sa_flags = 0; sigaction(SIGINT, NULL, &old_sa); if (old_sa.sa_handler != SIG_IGN) sigaction(SIGINT, &sa, NULL); sigaction(SIGHUP, NULL, &old_sa); if (old_sa.sa_handler != SIG_IGN) sigaction(SIGHUP, &sa, NULL); sigaction(SIGTERM, NULL, &old_sa); if (old_sa.sa_handler != SIG_IGN) sigaction(SIGTERM, &sa, NULL); } int run_subprocess(char * command, char ** args, void (*pre_wait_function)(void)) { int pid; int status; childpid = fork(); if (childpid == 0) { /* try to detach from parent's process group */ if (setsid() == -1) { syslog(LOG_ERR, "Unable to detach child. Aborting"); return -1; } if (execvp(command, args)) { perror("execvp"); exit(EX_NOINPUT); } else { /* Control almost certainly will not get to this point, ever. If * the call to execvp returned, instead of switching to a new memory * image, there was a problem. This exit will be collected by the * parent's call to waitpid() below. */ exit(EX_UNAVAILABLE); } } else if (childpid < 0) { perror("fork"); exit(EX_OSERR); } else { /* Make sure the child dies if we get killed. */ /* Only the parent should do this, of course! */ install_termination_handler(); if (pre_wait_function != NULL) { pre_wait_function(); } /* blocking wait on the child */ while ((pid = waitpid(childpid, &status, 0)) < 0) { if (errno == EINTR) { if (killed_by_us) { break; } /* else restart the loop */ } else { perror("waitpid"); } } alarm(0); if (pid > 0) { if (pid != childpid) { syslog(LOG_ERR, "childpid %d not retured by waitpid! instead %d", childpid, pid); kill_process_group(); exit(EX_OSERR); } /* exited normally? */ if (WIFEXITED(status)) { /* decode and return exit sttus */ status = WEXITSTATUS(status); syslog(LOG_DEBUG, "child exited with status %d", status); } else { /* This formula is a Unix shell convention */ status = 128 + WTERMSIG(status); syslog(LOG_DEBUG, "child exited via signal %d", WTERMSIG(status)); } } childpid = -1; } return status; } cronutils-1.5/runlock.c0000644000175000017500000001057612207114010013327 0ustar jaqjaq/* Copyright 2010 Google Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* For asprintf */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "subprocess.h" #include "tempdir.h" char * lock_filename = NULL; volatile sig_atomic_t timeout_expired = 0; static void usage(char * prog) { fprintf(stderr, "Usage: %s [options] command [arg [arg] ...]\n\n" "This program prevents concurrent execution of a process\n" "by holding an exclusive lock while the subprocess is running.\n" "Subsequent attempts to run a process with the same lock,\n" "while another process has the lock open, and is still\n" "executing, will exit with a failure exit code.\n", prog); fprintf(stderr, "\noptions:\n" " -d send log messages to stderr as well as syslog.\n" " -f lock_filename path to use as a lock file\n" " -t timeout time in seconds to wait to acquire the lock\n" " -h this help.\n" ); } static void alarm_handler(int signum); static void alarm_handler(int signum) { (void) signum; /* suppress unused variable warning */ timeout_expired = 1; } int main(int argc, char ** argv) { char * progname; int arg; char * command; char ** command_args; int status = 0; struct flock fl; int fd; char buf[BUFSIZ]; struct sigaction sa, old_sa; int debug = 0; int timeout = 5; char * endptr; memset(&fl, 0, sizeof(fl)); fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; progname = argv[0]; while ((arg = getopt(argc, argv, "+df:ht:")) > 0) { switch(arg) { case 'h': usage(progname); exit(EXIT_SUCCESS); break; case 'd': debug = LOG_PERROR; break; case 'f': if (asprintf(&lock_filename, "%s", optarg) == -1) { perror("asprintf"); exit(EX_OSERR); } break; case 't': timeout = strtol(optarg, &endptr, 10); if (*endptr || !optarg) { fprintf(stderr, "invalid timeout specified: %s\n", optarg); exit(EX_DATAERR); } break; default: break; } } if (optind >= argc) { usage(progname); exit(EXIT_FAILURE); } else { command = strdup(argv[optind]); command_args = &argv[optind]; } openlog(progname, debug|LOG_ODELAY|LOG_PID|LOG_NOWAIT, LOG_CRON); if (debug) setlogmask(LOG_UPTO(LOG_DEBUG)); else setlogmask(LOG_UPTO(LOG_INFO)); if (lock_filename == NULL) { if (asprintf(&lock_filename, "%s/%s.pid", make_tempdir(), command) == -1) { perror("asprintf"); exit(EX_OSERR); } } syslog(LOG_DEBUG, "lock filename is %s", lock_filename); sa.sa_handler = alarm_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGALRM, &sa, &old_sa); alarm(timeout); if ((fd = open(lock_filename, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR)) < 0) { perror(lock_filename); exit(EX_NOINPUT); } else { if (fcntl(fd, F_SETLKW, &fl) < 0) { switch (errno) { case EINTR: if (timeout_expired) { syslog(LOG_INFO, "waited 5 seconds, already locked by another process"); exit(EX_CANTCREAT); } break; case EACCES: case EAGAIN: syslog(LOG_INFO, "already locked by another process"); exit(EX_CANTCREAT); break; default: perror("fcntl"); exit(EXIT_FAILURE); } } else { alarm(0); sigaction(SIGALRM, &old_sa, NULL); snprintf(buf, BUFSIZ, "%d\n", getpid()); if (write(fd, buf, strlen(buf)) == -1) { perror("write"); } fsync(fd); syslog(LOG_DEBUG, "lock granted"); status = run_subprocess(command, command_args, NULL); close(fd); } } closelog(); return status; } cronutils-1.5/Makefile0000644000175000017500000000344512207114010013143 0ustar jaqjaq# Copyright 2010 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. all: runalarm runstat runlock runalarm: runalarm.c subprocess.c runlock: runlock.c subprocess.c tempdir.c runstat: runstat.c subprocess.c tempdir.c CFLAGS+=-Wall -Werror -Wextra -D_XOPEN_SOURCE=500 -g -ansi -pedantic-errors -Wwrite-strings -Wcast-align -Wcast-qual -Winit-self -Wformat=2 -Wuninitialized -Wmissing-declarations -Wpointer-arith -Wstrict-aliasing -fstrict-aliasing LDLIBS+=-lrt SOURCES = runalarm.c runlock.c runstat.c subprocess.c subprocess.h tempdir.c tempdir.h Makefile runalarm.1 runlock.1 runstat.1 version examples cronutils.spec runcron prefix = usr/local BINDIR = $(prefix)/bin MANDIR = $(prefix)/share/man/man1 VERSION = $(shell cat version) install: mkdir -p -m 755 $(DESTDIR)/$(BINDIR) $(DESTDIR)/$(MANDIR) install -m 755 runalarm runlock runstat $(DESTDIR)/$(BINDIR) install -m 644 runalarm.1 runlock.1 runstat.1 $(DESTDIR)/$(MANDIR) clean: rm -f runalarm runlock runstat distclean: clean rm -f *~ \#* dist: rm -rf cronutils-$(VERSION) cronutils-$(VERSION).tar cronutils-$(VERSION).tar.gz mkdir cronutils-$(VERSION) cp $(SOURCES) cronutils-$(VERSION) tar cf cronutils-$(VERSION).tar cronutils-$(VERSION) gzip -9 cronutils-$(VERSION).tar rm -rf cronutils-$(VERSION) .PHONY: dist clean install distclean cronutils-1.5/subprocess.h0000644000175000017500000000033012207114010014032 0ustar jaqjaq#ifndef __CRONUTILS_SUBPROCESS_H #define __CRONUTILS_SUBPROCESS_H void kill_process_group(); int run_subprocess(char * command, char ** args, void (*pre_wait_function)(void)); #endif /* __CRONUTILS_SUBPROCESS_H */ cronutils-1.5/examples0000755000175000017500000000153512207114010013245 0ustar jaqjaq#!/bin/sh ./runalarm -d -t 5 /bin/bash -c 'for i in $(seq 1 7); do echo $i; sleep 1; done; echo "exited"' ./runalarm -d -t 5 ./runstat -d -f foo /bin/bash -c 'for i in $(seq 1 7); do echo $i; sleep 1; done; echo "exited"' (./runlock -d -f ../locks/foo bash -c 'for i in $(seq 1 5); do echo $i; sleep 1; done'; echo $?) & sleep 2 ; ./runlock -d -f ../locks/foo bash -c 'for i in $(seq 1 5); do echo $i; sleep 1; done'; echo exited $? ./runlock -d -f ../locks/foo bash -c 'for i in $(seq 1 5); do echo $i; sleep 1; done' & sleep 1; cat ../locks/foo ./runlock -d bash -c 'for i in $(seq 1 5); do echo $i; sleep 1; done' ./runstat -d -f foo bash -c 'for i in $(seq 1 5); do echo $i; done; exit 5' ./runstat -d -f foo ./runalarm -d -t 5 /bin/bash -c 'for i in $(seq 1 7); do echo $i; sleep 1; done; echo exited'; cat foo ./runstat -f foo usleep 4900000; cat foo cronutils-1.5/tempdir.c0000644000175000017500000000362112207114010013307 0ustar jaqjaq/* Copyright 2010 Google, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #define _GNU_SOURCE /* asprintf */ #include #include #include #include #include #include #include #include #include #include #include #include "tempdir.h" const char * template = "/tmp/cronutils-"; char * dirname = NULL; char * make_tempdir() { uid_t uid; struct passwd * pw; struct stat st; uid = geteuid(); if ((pw = getpwuid(uid)) == NULL) { perror("getpwuid"); exit(EX_OSERR); } if (asprintf(&dirname, "%s%s", template, pw->pw_name) == -1) { perror("asprintf"); exit(EX_OSERR); } syslog(LOG_DEBUG, "temp dir is %s\n", dirname); if (mkdir(dirname, S_IRWXU) < 0) { if (errno == EEXIST) { if (stat(dirname, &st) != 0) { perror("stat"); exit(EX_OSERR); } if (!S_ISDIR(st.st_mode)) { syslog(LOG_ERR, "%s is not a directory\n", dirname); exit(EX_IOERR); } if (st.st_uid != uid) { syslog(LOG_ERR, "%s is not owned by %s\n", dirname, pw->pw_name); exit(EXIT_FAILURE); } if (!(st.st_mode & S_IRWXU)) { syslog(LOG_ERR, "%s has insecure permissions %d\n", dirname, st.st_mode); exit(EXIT_FAILURE); } } else { perror("mkdir"); exit(EX_OSERR); } } return dirname; } cronutils-1.5/runstat.10000644000175000017500000000224112207114010013256 0ustar jaqjaq.\" -*- nroff -*- .TH RUNSTAT 1 "October 18, 2010" "Google, Inc." .SH NAME runstat \- collect statistics about execution of a process .SH SYNOPSYS \fBrunstat\fR [ \fB-h\fR ] \fBrunstat\fR [ \fB-d\fR ] [ \fB-f \fIpathname\fR ] \fIcommand\fR [ \fIargs\fR ] .SH DESCRIPTION \fBrunstat\fR tries to execute a command in a subprocess, and upon termination of the subprocess collects some statistics about that subprocess, and writes them to a file. These statistics include time of execution, exit status, and elapsed time to execute, as well as use of various OS resources. .SH USAGE .TP \fB-d\fR Debug mode; send log messages to standard error as well as to the system log. .TP \fB-f \fIpathname\fR Specifies the pathname of the file to save the statistics to. The default is to create a file in /tmp/cronutils-$USER with the name of the command, and suffix ".stat". .TP \fB-h\fR Prints some basic help. .SH SEE ALSO \fBrunalarm\fR(1), \fBrunlock\fR(1), \fBgetrusage\fR(2) .SH AUTHOR \fBrunstat\fR was written by Jamie Wilkinson . .SH COPYRIGHT This program is copyright (C) 2010 Google, Inc. .PP It is licensed under the Apache License, Version 2.0 cronutils-1.5/cronutils.spec0000644000175000017500000000145312207114010014376 0ustar jaqjaqName: cronutils Version: 1.1 Release: 1%{?dist} Summary: Utilities to assist running batch processing jobs. Group: System Environment/Base License: ASL 2.0 URL: http://code.google.com/p/cronutils/ Source0: http://cronutils.googlecode.com/files/%{name}-%{version}.tar.gz BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) %description A set of utilities to complement batch processing jobs, such as those run from cron, by limiting concurrent execution of jobs, setting hard limits on the runtime of a job, and recording execution statistics of a completed job. %prep %setup -q %build make %{?_smp_mflags} %install rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT prefix=usr %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) %{_bindir}/* %{_mandir}/man1/* cronutils-1.5/runalarm.c0000644000175000017500000000600312207114010013461 0ustar jaqjaq/* Copyright 2000-2010 Google, Inc. Licensed under the Apache License, Version 2.0 (the "License"); qyou may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include #include #include #include #include "subprocess.h" int timeout = 60 * 60 * 24; /* 1 day */ volatile sig_atomic_t alarm_triggered = 0; static void usage(char * prog) { fprintf(stderr, "Usage: %s [options] command [arg [arg...]]\n\n" "This program tries to run a command and, if the timeout is\n" "reached before the command exits, kills that process.\n" "Otherwise the errorcode of the command is returned.\n" "\noptions:\n" " -t timeout time in seconds to wait before process is killed\n" " -d send log messages to stderr as well as syslog.\n" " -h print this help\n", prog); } static void alarm_handler(int signum) { int old_errno; (void) signum; /* suppress unused parameter warnings */ old_errno = errno; alarm_triggered = 1; kill_process_group(); errno = old_errno; } static void set_timeout_alarm(void) { alarm(timeout); } int main(int argc, char ** argv) { int arg; char * progname; int status = -1; char * command; char ** command_args; char * endptr; struct sigaction sa, old_sa; int debug = 0; progname = argv[0]; while ((arg = getopt(argc, argv, "+t:hd")) > 0) { switch (arg) { case 'h': usage(progname); exit(EXIT_SUCCESS); break; case 't': timeout = strtol(optarg, &endptr, 10); if (*endptr || !optarg) { fprintf(stderr, "invalid timeout specified: %s\n", optarg); exit(EX_DATAERR); } break; case 'd': debug = LOG_PERROR; break; default: break; } } if (optind >= argc) { usage(progname); exit(EXIT_FAILURE); } else { command = strdup(argv[optind]); command_args = &argv[optind]; } openlog(progname, debug|LOG_ODELAY|LOG_PID|LOG_NOWAIT, LOG_CRON); if (debug) setlogmask(LOG_UPTO(LOG_DEBUG)); else setlogmask(LOG_UPTO(LOG_INFO)); /* set up the alarm handler */ sigemptyset(&sa.sa_mask); sa.sa_handler = alarm_handler; sa.sa_flags = 0; sigaction(SIGALRM, &sa, &old_sa); /* exec the command */ status = run_subprocess(command, command_args, &set_timeout_alarm); alarm(0); /* shutdown the alarm */ if (alarm_triggered) { syslog(LOG_INFO, "command '%s' timed out after %d seconds", command, timeout); status = 128 + SIGALRM; } closelog(); exit(status); } cronutils-1.5/runcron0000644000175000017500000000005712207114010013110 0ustar jaqjaq#!/bin/sh runalarm runlock -f $1 runstat "$@" cronutils-1.5/runalarm.10000644000175000017500000000224212207114010013400 0ustar jaqjaq.\" -*- nroff -*- .TH RUNALARM 1 "October 18, 2010" "Google, Inc." .SH NAME runalarm \- enforce a time limit on execution of a process .SH SYNOPSYS \fBrunalarm\fR [ \fB-h\fR ] \fBrunalarm\fR [ \fB-d\fR ] [ \fB-t \fItimeout\fR ] \fIcommand\fR [ \fIargs\fR ] .SH DESCRIPTION \fBrunalarm\fR tries to execute a command and, if the subprocess does not exit before a timer expires, tries to terminate that subprocess. Otherwise, the exit status of the command is returned. .SH USAGE .TP \fB-d\fR Debug mode; send log messages to standard error as well as to the system log. .TP \fB-t \fItimeout\fR Specifies the duration, in seconds, for \fBrunalarm\fR to allow the command to run. The default is 1d duration (86400 seconds). .TP \fB-h\fR Prints some basic help. .SH BUGS Sending SIGALRM to \fBrunalarm\fR before the timer has expired will cause the subprocess to be killed. .SH SEE ALSO \fBrunlock\fR(1), \fBrunstat\fR(1) .SH AUTHOR \fBrunalarm\fR was written by Jamie Wilkinson , based on some Python code by Craig Silverstein .SH COPYRIGHT This program is copyright (C) 2000-2010 Google, Inc. .PP It is licensed under the Apache License, Version 2.0 cronutils-1.5/version0000644000175000017500000000000412207114010013077 0ustar jaqjaq1.5 cronutils-1.5/runstat.c0000644000175000017500000002221412207114010013342 0ustar jaqjaq/* Copyright 2010 Google, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #define _GNU_SOURCE /* dprintf(), asprintf */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "subprocess.h" #include "tempdir.h" static void usage(char * prog) { fprintf(stderr, "Usage: %s [options] command [arg [arg] ...]\n\n" "This program tries to execute a command in a" "subprocess, and upon termination of the subprocess" "writes some runtime statistics to a file." "These statistics include time of execution, exit" "status, and timestamp of completion.\n" "\noptions:\n" " -f path Path to save the statistics file.\n" " -C path Path to collectd socket.\n" " -d send log messages to stderr as well as syslog.\n" " -h print this help\n", prog); } enum var_kind { GAUGE, ABSOLUTE }; struct variable { struct variable * next; char * name; char * value; char * units; enum var_kind kind; }; void add_variable(struct variable ** var_list, const char * name, const enum var_kind kind, const char * units, const char * fmt, ...); void add_variable(struct variable ** var_list, const char * name, const enum var_kind kind, const char * units, const char * fmt, ...) { char buf[1024]; va_list ap; struct variable * var; var = malloc(sizeof(struct variable)); if (!var) { perror("malloc"); exit(EX_OSERR); } va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); var->name = strdup(name); var->units = units ? strdup(units) : NULL; var->value = strdup(buf); var->kind = kind; var->next = *var_list; *var_list = var; } int main(int argc, char ** argv) { char * progname; int arg; char * collectd_sockname = NULL; char * statistics_filename = NULL; char * temp_filename = NULL; char * command; char ** command_args; struct timeval start_wall_time, end_wall_time; struct timespec start_run_time, end_run_time; long int elapsed_sec, elapsed_nsec; int status; int temp_fd; char buf[1024]; int debug = 0; struct rusage ru; struct variable * var_list = NULL, * var; progname = argv[0]; while ((arg = getopt(argc, argv, "+C:f:hd")) > 0) { switch (arg) { case 'C': if (asprintf(&collectd_sockname, "%s", optarg) == -1) { perror("asprintf collectd_sockname"); exit(EX_OSERR); } break; case 'h': usage(progname); exit(EXIT_SUCCESS); break; case 'f': if (asprintf(&statistics_filename, "%s", optarg) == -1) { perror("asprintf"); exit(EX_OSERR); } break; case 'd': debug = LOG_PERROR; break; default: break; } } if (optind >= argc) { usage(progname); exit(EXIT_FAILURE); } else { command = strdup(argv[optind]); command_args = &argv[optind]; } openlog(progname, debug|LOG_ODELAY|LOG_PID|LOG_NOWAIT, LOG_CRON); if (debug) setlogmask(LOG_UPTO(LOG_DEBUG)); else setlogmask(LOG_UPTO(LOG_INFO)); gettimeofday(&start_wall_time, NULL); clock_gettime(CLOCK_MONOTONIC, &start_run_time); status = run_subprocess(command, command_args, NULL); clock_gettime(CLOCK_MONOTONIC, &end_run_time); gettimeofday(&end_wall_time, NULL); if (statistics_filename == NULL) { if (asprintf(&statistics_filename, "%s/%s.stat", make_tempdir(), command) == -1) { perror("asprintf"); exit(EX_OSERR); } } syslog(LOG_DEBUG, "statistics filename is %s", statistics_filename); if (asprintf(&temp_filename, "%s.XXXXXX", statistics_filename) == -1) { perror("asprintf"); exit(EX_OSERR); } syslog(LOG_DEBUG, "temp filename is %s", temp_filename); if ((temp_fd = mkstemp(temp_filename)) < 0) { perror("mkstemp"); exit(EX_OSERR); } /** process */ add_variable(&var_list, "exit_status", GAUGE, NULL, "%d", status); /** wall time */ /* ABSOLUTE hostname/runstat-progname/last_run-epoch_timestamp_start */ add_variable(&var_list, "start_timestamp", ABSOLUTE, "time_t", "%ld.%.6ld", start_wall_time.tv_sec, start_wall_time.tv_usec); /* ABSOLUTE hostname/runstat-progname/last_run-epoch_timestamp_end */ add_variable(&var_list, "end_timestamp", ABSOLUTE, "time_t", "%ld.%.6ld", end_wall_time.tv_sec, end_wall_time.tv_usec); /** timing */ elapsed_sec = end_run_time.tv_sec - start_run_time.tv_sec; elapsed_nsec = end_run_time.tv_nsec - start_run_time.tv_nsec; if (elapsed_nsec < 0) { elapsed_nsec += 1e9; elapsed_sec--; } /* GAUGE hostname/runstat-progname/last_run-elapsed-time */ add_variable(&var_list, "elapsed_time", GAUGE, "s", "%ld.%.9ld", elapsed_sec, elapsed_nsec); /** resource usage */ if (getrusage(RUSAGE_CHILDREN, &ru) == 0) { add_variable(&var_list, "user_time", GAUGE, "s", "%ld.%.6ld", ru.ru_utime.tv_sec, ru.ru_utime.tv_usec); add_variable(&var_list, "system_time", GAUGE, "s", "%ld.%.6ld", ru.ru_stime.tv_sec, ru.ru_stime.tv_usec); add_variable(&var_list, "rss-max", GAUGE, "B", "%ld", ru.ru_maxrss); add_variable(&var_list, "rss-shared", GAUGE, "B", "%ld", ru.ru_ixrss); add_variable(&var_list, "rss-data_unshared", GAUGE, "B", "%ld", ru.ru_idrss); add_variable(&var_list, "rss-stack_unshared", GAUGE, "B", "%ld", ru.ru_isrss); add_variable(&var_list, "page-reclaims", GAUGE, "pages", "%ld", ru.ru_minflt); add_variable(&var_list, "page-faults", GAUGE, "pages", "%ld", ru.ru_majflt); add_variable(&var_list, "swaps", GAUGE, "swaps", "%ld", ru.ru_nswap); add_variable(&var_list, "block_ios-in", GAUGE, "block_ios", "%ld", ru.ru_inblock); add_variable(&var_list, "block_ios-out", GAUGE, "block_ios", "%ld", ru.ru_oublock); add_variable(&var_list, "messages-sent", GAUGE, "messages", "%ld", ru.ru_msgsnd); add_variable(&var_list, "messages-received", GAUGE, "messages", "%ld", ru.ru_msgrcv); add_variable(&var_list, "signals-received", GAUGE, "signals", "%ld", ru.ru_nsignals); add_variable(&var_list, "ctx_switch-voluntary", GAUGE, "context switches", "%ld", ru.ru_nvcsw); add_variable(&var_list, "ctx_switch-involuntary", GAUGE, "context switches", "%ld", ru.ru_nivcsw); } /* CSV emitter */ for (var = var_list; var != NULL; var = var->next) { snprintf(buf, sizeof(buf), "%s,%s,%s,%s\n", command, var->name, var->value, var->units ? var->units : "" ); if (write(temp_fd, buf, strlen(buf)) == -1) { perror("write"); } } fsync(temp_fd); close(temp_fd); if (rename(temp_filename, statistics_filename) < 0) { perror("rename"); exit(EX_OSERR); } /* Write to collectd */ if (collectd_sockname != NULL) { char * hostname; long hostname_len; struct sockaddr_un sock; int s; if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { perror("socket"); goto end; } sock.sun_family = AF_UNIX; strncpy(sock.sun_path, collectd_sockname, sizeof(sock.sun_path) - 1); sock.sun_path[sizeof(sock.sun_path) - 1] = '\0'; if (connect(s, (struct sockaddr*) &sock, strlen(sock.sun_path) + sizeof(sock.sun_family)) == -1) { perror("connect"); goto end; } hostname_len = sysconf(_SC_HOST_NAME_MAX); if (hostname_len <= 0) hostname_len = _POSIX_HOST_NAME_MAX; if ((hostname = malloc(hostname_len)) == NULL) { perror("malloc hostname"); exit(EX_OSERR); } if (gethostname(hostname, hostname_len) == -1) { perror("gethostname"); exit(EX_OSERR); } for (var = var_list; var != NULL; var = var->next) { char type[10] = {'\0'}; switch (var->kind) { case GAUGE: strncpy(type, "gauge", 9); break; case ABSOLUTE: strncpy(type, "counter", 9); break; default: perror("unknown var->kind"); break; } dprintf(s, "PUTVAL \"%s/runstat-%s/%s-%s\" %ld:%s\n", hostname, basename(command), type, var->name, end_wall_time.tv_sec, var->value); /* This next line is a bit of a hack to clear the pipe.*/ recv(s, buf, sizeof(buf) - 1, 0); } close(s); } end: closelog(); return status; } cronutils-1.5/tempdir.h0000644000175000017500000000125712207114010013317 0ustar jaqjaq/* Copyright 2010 Google, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __CRONUTILS_TEMPDIR_H__ #define __CRONUTILS_TEMPDIR_H__ char * make_tempdir(); #endif /* __CRONUTILS_TEMPDIR_H__ */ cronutils-1.5/runlock.10000644000175000017500000000266212207114010013242 0ustar jaqjaq.\" -*- nroff -*- .TH RUNLOCK 1 "October 18, 2010" "Google, Inc." .SH NAME runlock \- prevent concurrent execution of a process .SH SYNOPSYS \fBrunlock\fR [ \fB-h\fR ] \fBrunlock\fR [ \fB-d\fR ] [ \fB-f \fIpathname\fR ] [ \fB-t \fItimeout\fR ] \fIcommand\fR [ \fIargs\fR ] .SH DESCRIPTION \fBrunlock\fR tries to hold an exclusive lock while it executes a command. Subsequent execution of \fBrunlock\fR with the same lock, while that lock is held, will cause the new instance of \fBrunlock\fR to terminate with a faliure exit code. Otherwise, the exit code of the subprocess is returned. .SH USAGE .TP \fB-d\fR Debug mode; send log messages to standard error as well as to the system log. .TP \fB-f \fIpathname\fR Specifies the pathname of the file to use as a lock file. The default is to create a lock file in /tmp/cronutils-$USER with the name of the command, and suffix ".pid". .TP \fB-t \fItimeout\fR Specifies the duration, in seconds, for \fBrunlock\fR to wait before giving up on trying to acquire the lock. The default is 5 seconds. .TP \fB-h\fR Prints some basic help. .SH BUGS Sending SIGALRM to \fBrunlock\fR before the timer has expired will cause the subprocess to be killed. .SH SEE ALSO \fBrunalarm\fR(1), \fBrunstat\fR(1) .SH AUTHOR \fBrunlock\fR was written by Jamie Wilkinson . .SH COPYRIGHT This program is copyright (C) 2010 Google, Inc. .PP It is licensed under the Apache License, Version 2.0