Gentoo Archives: gentoo-commits

From: "Caleb Tennis (caleb)" <caleb@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] gentoo-x86 commit in dev-util/lockrun/files: lockrun.c
Date: Tue, 05 Feb 2008 18:22:44
Message-Id: E1JMSRL-0007Nx-Bq@stork.gentoo.org
1 caleb 08/02/05 18:22:39
2
3 Added: lockrun.c
4 Log:
5 Initial import
6 (Portage version: 2.1.4.1)
7
8 Revision Changes Path
9 1.1 dev-util/lockrun/files/lockrun.c
10
11 file : http://sources.gentoo.org/viewcvs.py/gentoo-x86/dev-util/lockrun/files/lockrun.c?rev=1.1&view=markup
12 plain: http://sources.gentoo.org/viewcvs.py/gentoo-x86/dev-util/lockrun/files/lockrun.c?rev=1.1&content-type=text/plain
13
14 Index: lockrun.c
15 ===================================================================
16 /*
17 * $Id: lockrun.c,v 1.1 2008/02/05 18:22:38 caleb Exp $
18 *
19 * written by : Stephen J. Friedl
20 * Software Consultant
21 * steve@×××××××.net
22 * http://www.unixwiz.net/tools/
23 *
24 * ===================================================================
25 * ======== This software is in the public domain, and can be ========
26 * ======== used by anybody for any purpose ========
27 * ===================================================================
28 *
29 * Lockrun: This program is used to launch a program out with a lockout
30 * so that only one can run at a time. It's mainly intended for use out
31 * of cron so that our five-minute running jobs which run long don't get
32 * walked on. We find this a *lot* with Cacti jobs which just get hung
33 * up: it's better to miss a polling period than to stack them up and
34 * slow each other down.
35 *
36 * So we use a file which is used for locking: this program attempts to
37 * lock the file, and if the lock exists, we have to either exit with
38 * an error, or wait for it to release.
39 *
40 * lockrun --lockfile=FILE -- my command here
41 *
42 * COMMAND LINE
43 * ------------
44 *
45 * --lockfile=F
46 *
47 * Specify the name of a file which is used for locking. The file is
48 * created if necessary (with mode 0666), and no I/O of any kind is
49 * done. The file is never removed.
50 *
51 * --maxtime=N
52 *
53 * The script being controlled should run for no more than <N> seconds,
54 * and if it's beyond that time, we should report it to the standard
55 * error (which probably gets routed to the user via cron's email).
56 *
57 * --wait
58 *
59 * When a lock is hit, we normally exit with error, but --wait causes
60 * it to loop until the lock is released.
61 *
62 * --verbose
63 *
64 * Show a bit more runtime debugging.
65 *
66 * --
67 *
68 * Mark the end of the options: the command to run follows.
69 *
70 */
71
72 #include <stdio.h>
73 #include <stdarg.h>
74 #include <errno.h>
75 #include <stdlib.h>
76 #include <string.h>
77 #include <fcntl.h>
78 #include <unistd.h>
79 #include <time.h>
80 #include <sys/types.h>
81 #include <sys/wait.h>
82 #include <sys/file.h>
83
84 #ifndef __GNUC__
85 # define __attribute__(x) /* nothing */
86 #endif
87
88
89 #define STRMATCH(a,b) (strcmp((a),(b)) == 0)
90
91 #define UNUSED_PARAMETER(v) ((void)(v))
92
93 #define TRUE 1
94 #define FALSE 0
95
96 static const char *lockfile = 0;
97 static int wait_for_lock = FALSE;
98 static mode_t openmode = 0666;
99 static int sleeptime = 10; /* seconds */
100 static int Verbose = FALSE;
101 static int Maxtime = 0;
102
103 static char *getarg(char *opt, char ***pargv);
104
105 static void die(const char *format, ...)
106 __attribute__((noreturn))
107 __attribute__((format(printf, 1, 2)));
108
109 int main(int argc, char **argv)
110 {
111 char *Argv0 = *argv;
112 int rc;
113 int lfd;
114 pid_t childpid;
115 time_t starttime;
116
117 UNUSED_PARAMETER(argc);
118
119 time(&starttime);
120
121 for ( argv++ ; *argv ; argv++ )
122 {
123 char *arg = *argv;
124 char *opt = strchr(arg, '=');
125
126 /* the -- token marks the end of the list */
127
128 if ( strcmp(*argv, "--") == 0 )
129 {
130 argv++;
131 break;
132 }
133
134 if (opt) *opt++ = '\0'; /* pick off the =VALUE part */
135
136 if ( STRMATCH(arg, "-L") || STRMATCH(arg, "--lockfile"))
137 {
138 lockfile = getarg(opt, &argv);
139 }
140
141 else if ( STRMATCH(arg, "-W") || STRMATCH(arg, "--wait"))
142 {
143 wait_for_lock = TRUE;
144 }
145
146 else if ( STRMATCH(arg, "-S") || STRMATCH(arg, "--sleep"))
147 {
148 sleeptime = atoi(getarg(opt, &argv));
149 }
150
151 else if ( STRMATCH(arg, "-T") || STRMATCH(arg, "--maxtime"))
152 {
153 Maxtime = atoi(getarg(opt, &argv));
154 }
155
156 else if ( STRMATCH(arg, "-V") || STRMATCH(arg, "--verbose"))
157 {
158 Verbose++;
159 }
160
161 else
162 {
163 die("ERROR: \"%s\" is an invalid cmdline param", arg);
164 }
165 }
166
167 /*----------------------------------------------------------------
168 * SANITY CHECKING
169 *
170 * Make sure that we have all the parameters we require
171 */
172 if (*argv == 0)
173 die("ERROR: missing command to %s (must follow \"--\" marker) ", Argv0);
174
175 if (lockfile == 0)
176 die("ERROR: missing --lockfile=F parameter");
177
178 /*----------------------------------------------------------------
179 * Open or create the lockfile, then try to acquire the lock. If
180 * the lock is acquired immediately (==0), then we're done, but
181 * if the lock is not available, we have to wait for it.
182 *
183 * We can either loop trying for the lock (for --wait), or exit
184 * with error.
185 */
186
187 if ( (lfd = open(lockfile, O_RDWR|O_CREAT, openmode)) < 0)
188 die("ERROR: cannot open(%s) [err=%s]", lockfile, strerror(errno));
189
190 while ( flock(lfd, LOCK_EX | LOCK_NB) != 0 )
191 {
192 if ( ! wait_for_lock )
193 {
194 die("ERROR: cannot launch %s - run is locked", argv[0]);
195 }
196
197 /* waiting */
198 if ( Verbose ) printf("(locked: sleeping %d secs)\n", sleeptime);
199
200 sleep(sleeptime);
201 }
202
203 fflush(stdout);
204
205 /* run the child */
206
207
208 if ( (childpid = fork()) == 0 )
209 {
210 close(lfd); // don't need the lock file
211
212 execvp(argv[0], argv);
213 }
214 else if ( childpid > 0 )
215 {
216 time_t endtime;
217 pid_t pid;
218
219 if ( Verbose )
220 printf("Waiting for process %ld\n", (long) childpid);
221
222 pid = waitpid(childpid, &rc, 0);
223
224 time(&endtime);
225
226 endtime -= starttime;
227
228 if ( Verbose || (Maxtime > 0 && endtime > Maxtime) )
229 printf("pid %d exited with status %d (time=%ld sec)\n", pid, rc, endtime);
230 }
231 else
232 {
233 die("ERROR: cannot fork [%s]", strerror(errno));
234 }
235
236 exit(rc);
237 }
238
239
240 /*! \fn static char *getarg(char *opt, char ***pargv)
241 * \brief A function to parse calling parameters
242 *
243 * This is a helper for the main arg-processing loop: we work with
244 * options which are either of the form "-X=FOO" or "-X FOO"; we
245 * want an easy way to handle either one.
246 *
247 * The idea is that if the parameter has an = sign, we use the rest
248 * of that same argv[X] string, otherwise we have to get the *next*
249 * argv[X] string. But it's an error if an option-requiring param
250 * is at the end of the list with no argument to follow.
251 *
252 * The option name could be of the form "-C" or "--conf", but we
253 * grab it from the existing argv[] so we can report it well.
254 *
255 * \return character pointer to the argument
256 *
257 */
258 static char *getarg(char *opt, char ***pargv)
259 {
260 const char *const optname = **pargv;
261
262 /* option already set? */
263 if (opt) return opt;
264
265 /* advance to next argv[] and try that one */
266 if ((opt = *++(*pargv)) == 0)
267 die("ERROR: option %s requires a parameter", optname);
268
269 return opt;
270 }
271
272 /*
273 * die()
274 *
275 * Given a printf-style argument list, format it to the standard error,
276 * append a newline, then exit with error status.
277 */
278
279 static void die(const char *format, ...)
280 {
281 va_list args;
282
283 va_start(args, format);
284 vfprintf(stderr, format, args);
285 putc('\n', stderr);
286 va_end(args);
287
288 exit(EXIT_FAILURE);
289 }
290
291
292
293
294 --
295 gentoo-commits@l.g.o mailing list