1 /*
2  * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of version 2 of the GNU General Public License as
6  * published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it would be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11  *
12  * Further, this software is distributed without any warranty that it is
13  * free of the rightful claim of any third person regarding infringement
14  * or the like.  Any license provided herein, whether implied or
15  * otherwise, applies only to this software file.  Patent licenses, if
16  * any, provided herein do not apply to combinations of this program with
17  * other software, or any other product whatsoever.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  *
23  * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24  * Mountain View, CA  94043, or:
25  *
26  * http://www.sgi.com
27  *
28  * For further information regarding this notice, see:
29  *
30  * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
31  *
32  */
33 /* $Id: zoolib.c,v 1.8 2009/06/09 17:59:46 subrata_modak Exp $ */
34 /*
35  * ZooLib
36  *
37  * A Zoo is a file used to record what test tags are running at the moment.
38  * If the system crashes, we should be able to look at the zoo file to find out
39  * what was currently running.  This is especially helpful when running multiple
40  * tests at the same time.
41  *
42  * The zoo file is meant to be a text file that fits on a standard console.
43  * You should be able to watch it with `cat zoofile`
44  *
45  * zoo file format:
46  * 	80 characters per line, ending with a \n
47  * 	available lines start with '#'
48  * 	expected line fromat: pid_t,tag,cmdline
49  *
50  */
51 
52 #include <signal.h>
53 #include <stdlib.h>		/* for getenv */
54 #include <string.h>
55 #include "zoolib.h"
56 
57 char zoo_error[ZELEN];
58 
59 #ifdef __linux__
60 /* glibc2.2 definition needs -D_XOPEN_SOURCE, which breaks other things. */
61 extern int sighold(int __sig);
62 extern int sigrelse(int __sig);
63 #endif
64 
65 /* zoo_mark(): private function to make an entry to the zoo
66  * 	returns 0 on success, -1 on error */
67 static int zoo_mark(zoo_t z, char *entry);
68 static int zoo_lock(zoo_t z);
69 static int zoo_unlock(zoo_t z);
70 /* cat_args(): helper function to make cmdline from argc, argv */
71 char *cat_args(int argc, char **argv);
72 
73 /* zoo_getname(): create a filename to use for the zoo */
zoo_getname(void)74 char *zoo_getname(void)
75 {
76 	char buf[1024];
77 	char *zoo;
78 
79 	zoo = getenv("ZOO");
80 	if (zoo) {
81 		snprintf(buf, 1024, "%s/%s", zoo, "active");
82 		return strdup(buf);
83 	} else {
84 		/* if there is no environment variable, we don't know where to put it */
85 		return NULL;
86 	}
87 }
88 
89 /* zoo_open(): open a zoo for use */
zoo_open(char * zooname)90 zoo_t zoo_open(char *zooname)
91 {
92 	zoo_t new_zoo;
93 
94 	new_zoo = (zoo_t) fopen(zooname, "r+");
95 	if (!new_zoo) {
96 		if (errno == ENOENT) {
97 			/* file doesn't exist, try fopen(xxx, "a+") */
98 			new_zoo = (zoo_t) fopen(zooname, "a+");
99 			if (!new_zoo) {
100 				/* total failure */
101 				snprintf(zoo_error, ZELEN,
102 					 "Could not open zoo as \"%s\", errno:%d %s",
103 					 zooname, errno, strerror(errno));
104 				return 0;
105 			}
106 			fclose(new_zoo);
107 			new_zoo = fopen(zooname, "r+");
108 		} else {
109 			snprintf(zoo_error, ZELEN,
110 				 "Could not open zoo as \"%s\", errno:%d %s",
111 				 zooname, errno, strerror(errno));
112 		}
113 	}
114 	return new_zoo;
115 }
116 
zoo_close(zoo_t z)117 int zoo_close(zoo_t z)
118 {
119 	int ret;
120 
121 	ret = fclose(z);
122 	if (ret) {
123 		snprintf(zoo_error, ZELEN,
124 			 "closing zoo caused error, errno:%d %s",
125 			 errno, strerror(errno));
126 	}
127 	return ret;
128 }
129 
zoo_mark(zoo_t z,char * entry)130 static int zoo_mark(zoo_t z, char *entry)
131 {
132 	FILE *fp = (FILE *) z;
133 	int found = 0;
134 	long pos;
135 	char buf[BUFLEN];
136 
137 	if (fp == NULL)
138 		return -1;
139 
140 	if (zoo_lock(z))
141 		return -1;
142 
143 	/* first fit */
144 	rewind(fp);
145 
146 	do {
147 		pos = ftell(fp);
148 
149 		if (fgets(buf, BUFLEN, fp) == NULL)
150 			break;
151 
152 		if (buf[0] == '#') {
153 			rewind(fp);
154 			if (fseek(fp, pos, SEEK_SET)) {
155 				/* error */
156 				snprintf(zoo_error, ZELEN,
157 					 "seek error while writing to zoo file, errno:%d %s",
158 					 errno, strerror(errno));
159 				return -1;
160 			}
161 			/* write the entry, left justified, and padded/truncated to the
162 			 * same size as the previous entry */
163 			fprintf(fp, "%-*.*s\n", (int)strlen(buf) - 1,
164 				(int)strlen(buf) - 1, entry);
165 			found = 1;
166 			break;
167 		}
168 	} while (1);
169 
170 	if (!found) {
171 		if (fseek(fp, 0, SEEK_END)) {
172 			snprintf(zoo_error, ZELEN,
173 				 "error seeking to end of zoo file, errno:%d %s",
174 				 errno, strerror(errno));
175 			return -1;
176 		}
177 		fprintf(fp, "%-*.*s\n", 79, 79, entry);
178 	}
179 	fflush(fp);
180 
181 	if (zoo_unlock(z))
182 		return -1;
183 	return 0;
184 }
185 
zoo_mark_cmdline(zoo_t z,pid_t p,char * tag,char * cmdline)186 int zoo_mark_cmdline(zoo_t z, pid_t p, char *tag, char *cmdline)
187 {
188 	char new_entry[BUFLEN];
189 
190 	snprintf(new_entry, 80, "%d,%s,%s", p, tag, cmdline);
191 	return zoo_mark(z, new_entry);
192 }
193 
zoo_mark_args(zoo_t z,pid_t p,char * tag,int ac,char ** av)194 int zoo_mark_args(zoo_t z, pid_t p, char *tag, int ac, char **av)
195 {
196 	char *cmdline;
197 	int ret;
198 
199 	cmdline = cat_args(ac, av);
200 	ret = zoo_mark_cmdline(z, p, tag, cmdline);
201 
202 	free(cmdline);
203 	return ret;
204 }
205 
zoo_clear(zoo_t z,pid_t p)206 int zoo_clear(zoo_t z, pid_t p)
207 {
208 	FILE *fp = (FILE *) z;
209 	long pos;
210 	char buf[BUFLEN];
211 	pid_t that_pid;
212 	int found = 0;
213 
214 	if (fp == NULL)
215 		return -1;
216 
217 	if (zoo_lock(z))
218 		return -1;
219 	rewind(fp);
220 
221 	do {
222 		pos = ftell(fp);
223 
224 		if (fgets(buf, BUFLEN, fp) == NULL)
225 			break;
226 
227 		if (buf[0] == '#')
228 			continue;
229 
230 		that_pid = atoi(buf);
231 		if (that_pid == p) {
232 			if (fseek(fp, pos, SEEK_SET)) {
233 				/* error */
234 				snprintf(zoo_error, ZELEN,
235 					 "seek error while writing to zoo file, errno:%d %s",
236 					 errno, strerror(errno));
237 				return -1;
238 			}
239 			if (ftell(fp) != pos) {
240 				printf("fseek failed\n");
241 			}
242 			fputs("#", fp);
243 			found = 1;
244 			break;
245 		}
246 	} while (1);
247 
248 	fflush(fp);
249 
250 	/* FIXME: unlock zoo file */
251 	if (zoo_unlock(z))
252 		return -1;
253 
254 	if (!found) {
255 		snprintf(zoo_error, ZELEN,
256 			 "zoo_clear() did not find pid(%d)", p);
257 		return 1;
258 	}
259 	return 0;
260 
261 }
262 
zoo_getpid(zoo_t z,char * tag)263 pid_t zoo_getpid(zoo_t z, char *tag)
264 {
265 	FILE *fp = (FILE *) z;
266 	char buf[BUFLEN], *s;
267 	pid_t this_pid = -1;
268 
269 	if (fp == NULL)
270 		return -1;
271 
272 	if (zoo_lock(z))
273 		return -1;
274 
275 	rewind(fp);
276 	do {
277 		if (fgets(buf, BUFLEN, fp) == NULL)
278 			break;
279 
280 		if (buf[0] == '#')
281 			continue;	/* recycled line */
282 
283 		if ((s = strchr(buf, ',')) == NULL)
284 			continue;	/* line was not expected format */
285 
286 		if (strncmp(s + 1, tag, strlen(tag)))
287 			continue;	/* tag does not match */
288 
289 		this_pid = atoi(buf);
290 		break;
291 	} while (1);
292 
293 	if (zoo_unlock(z))
294 		return -1;
295 	return this_pid;
296 }
297 
zoo_lock(zoo_t z)298 int zoo_lock(zoo_t z)
299 {
300 	FILE *fp = (FILE *) z;
301 	struct flock zlock;
302 	sigset_t block_these;
303 	int ret;
304 
305 	if (fp == NULL)
306 		return -1;
307 
308 	zlock.l_whence = zlock.l_start = zlock.l_len = 0;
309 	zlock.l_type = F_WRLCK;
310 
311 	sigemptyset(&block_these);
312 	sigaddset(&block_these, SIGINT);
313 	sigaddset(&block_these, SIGTERM);
314 	sigaddset(&block_these, SIGHUP);
315 	sigaddset(&block_these, SIGUSR1);
316 	sigaddset(&block_these, SIGUSR2);
317 	sigprocmask(SIG_BLOCK, &block_these, NULL);
318 
319 	do {
320 		ret = fcntl(fileno(fp), F_SETLKW, &zlock);
321 	} while (ret == -1 && errno == EINTR);
322 
323 	sigprocmask(SIG_UNBLOCK, &block_these, NULL);
324 	if (ret == -1) {
325 		snprintf(zoo_error, ZELEN,
326 			 "failed to unlock zoo file, errno:%d %s",
327 			 errno, strerror(errno));
328 		return -1;
329 	}
330 	return 0;
331 
332 }
333 
zoo_unlock(zoo_t z)334 int zoo_unlock(zoo_t z)
335 {
336 	FILE *fp = (FILE *) z;
337 	struct flock zlock;
338 	sigset_t block_these;
339 	int ret;
340 
341 	if (fp == NULL)
342 		return -1;
343 
344 	zlock.l_whence = zlock.l_start = zlock.l_len = 0;
345 	zlock.l_type = F_UNLCK;
346 
347 	sigemptyset(&block_these);
348 	sigaddset(&block_these, SIGINT);
349 	sigaddset(&block_these, SIGTERM);
350 	sigaddset(&block_these, SIGHUP);
351 	sigaddset(&block_these, SIGUSR1);
352 	sigaddset(&block_these, SIGUSR2);
353 	sigprocmask(SIG_BLOCK, &block_these, NULL);
354 
355 	do {
356 		ret = fcntl(fileno(fp), F_SETLKW, &zlock);
357 	} while (ret == -1 && errno == EINTR);
358 
359 	sigprocmask(SIG_UNBLOCK, &block_these, NULL);
360 
361 	if (ret == -1) {
362 		snprintf(zoo_error, ZELEN,
363 			 "failed to lock zoo file, errno:%d %s",
364 			 errno, strerror(errno));
365 		return -1;
366 	}
367 	return 0;
368 }
369 
cat_args(int argc,char ** argv)370 char *cat_args(int argc, char **argv)
371 {
372 	int a, size;
373 	char *cmd;
374 
375 	for (size = a = 0; a < argc; a++) {
376 		size += strlen(argv[a]);
377 		size++;
378 	}
379 
380 	if ((cmd = malloc(size)) == NULL) {
381 		snprintf(zoo_error, ZELEN,
382 			 "Malloc Error, %s/%d", __FILE__, __LINE__);
383 		return NULL;
384 	}
385 
386 	*cmd = '\0';
387 	for (a = 0; a < argc; a++) {
388 		if (a != 0)
389 			strcat(cmd, " ");
390 		strcat(cmd, argv[a]);
391 	}
392 
393 	return cmd;
394 }
395 
396 #if defined(UNIT_TEST)
397 
zt_add(zoo_t z,int n)398 void zt_add(zoo_t z, int n)
399 {
400 	char cmdline[200];
401 	char tag[10];
402 
403 	snprintf(tag, 10, "%s%d", "test", n);
404 	snprintf(cmdline, 200, "%s%d %s %s %s", "runtest", n, "one", "two",
405 		 "three");
406 
407 	zoo_mark_cmdline(z, n, tag, cmdline);
408 }
409 
main(int argc,char * argv[])410 int main(int argc, char *argv[])
411 {
412 
413 	char *zooname;
414 	zoo_t test_zoo;
415 	char *test_tag = "unittest";
416 	int i, j;
417 
418 	zooname = zoo_getname();
419 
420 	if (!zooname) {
421 		zooname = strdup("test_zoo");
422 	}
423 	printf("Test zoo filename is %s\n", zooname);
424 
425 	if ((test_zoo = zoo_open(zooname)) == NULL) {
426 		printf("Error opennning zoo\n");
427 		exit(-1);
428 	}
429 
430 	zoo_mark_args(test_zoo, getpid(), test_tag, argc, argv);
431 
432 	for (j = 0; j < 5; j++) {
433 		for (i = 0; i < 20; i++) {
434 			zt_add(test_zoo, i);
435 		}
436 
437 		for (; i >= 0; i--) {
438 			zoo_clear(test_zoo, i);
439 		}
440 	}
441 
442 	zoo_clear(test_zoo, getpid());
443 
444 	return 0;
445 }
446 
447 #endif
448