xref: /onnv-gate/usr/src/cmd/wbem/provider/tools/rds/rds.c (revision 1914:8a8c5f225b1b)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdio.h>
29 #include <stdio_ext.h>
30 #include <stdlib.h>
31 #include <sys/time.h>
32 #include <strings.h>
33 #include <limits.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <signal.h>
37 #include <fcntl.h>
38 #include <sys/stat.h>
39 #include <pthread.h>
40 #include "rdimpl.h"
41 #include "rdprot.h"
42 #include "rdutil.h"
43 #include "rdlist.h"
44 #include "rdfile.h"
45 
46 #define	RDS_VERSION		"RDS Version 1.0\n"
47 #define	TIMEOUT_MSG		"Timeout"
48 #define	NOTREADY_RESPONSE	"BUSY"
49 
50 #define	DEFAULT_SCAN_INTERVAL	1000   /* milliseconds */
51 #define	MAXIMAL_SCAN_INTERVAL	30000  /* milliseconds */
52 #define	DEFAULT_CMD_TIMEOUT	2000   /* milliseconds */
53 
54 extern list_t	users;		/* list of users */
55 extern list_t	projects;	/* list of projects */
56 extern list_t	sys;		/* list with one sys entry */
57 extern list_t	processes;	/* list of processes */
58 extern list_t	lwps;		/* list of lwps */
59 extern char	errmsg[];	/* global message buffer */
60 
61 static char	greeting[] =		\
62 	"Resource Data Server\n"	\
63 	"Copyright 2001 SMI.\n"		\
64 	"Version 1.0\n";
65 
66 /* ms timeout between successive cmds */
67 static int	timeout = DEFAULT_CMD_TIMEOUT;
68 
69 /* ms interval between successive scans */
70 static int	interval = DEFAULT_SCAN_INTERVAL;
71 
72 /* global signal flag */
73 static int	sigterm = 0;
74 
75 /* print all cmd data on stdout in server mode flag */
76 static int	Po = 0;
77 
78 /* count of scans performed in server mode */
79 static long	scans_done = 0;
80 
81 /* name of rds logging file */
82 static char		*log_file = NULL;
83 
84 /* enable microstate accounting flag */
85 int		mo = 0;
86 
87 /* name of stored data file */
88 char		*ltdb_file = NULL;
89 
90 
91 /* mutex lock for data lists */
92 pthread_mutex_t listLock = PTHREAD_MUTEX_INITIALIZER;
93 
94 /* mutex lock for log */
95 pthread_mutex_t logLock = PTHREAD_MUTEX_INITIALIZER;
96 
97 /* identifiers for the various threads */
98 static pthread_t scanner = 0;
99 static pthread_t server = 0;
100 static pthread_t master = 0;
101 
102 
103 /*
104  * Clean up calling thread's state.
105  */
106 static void
thread_cleanup()107 thread_cleanup()
108 {
109 	pthread_t this = pthread_self();
110 
111 	if (pthread_equal(this, server)) {
112 
113 		/* shut down the command protocol */
114 		(void) fprintf(stderr,
115 			"cleanup_state: server thread shutdown\n");
116 		log_msg("server thread shutdown init\n");
117 		wr_error(errmsg);
118 		log_msg("server thread shutdown complete\n");
119 
120 	} else if (pthread_equal(this, scanner)) {
121 
122 		/* shut down the scanner */
123 		(void) fprintf(stderr,
124 			"cleanup_state: scanner thread shutdown\n");
125 		log_msg("scanner thread shutdown init\n");
126 
127 		log_msg("Waiting for server thread %d join from %d\n",
128 		    (int)server, (int)this);
129 
130 		if (pthread_join(server, NULL) != 0) {
131 			int e = errno;
132 
133 			perror("server join (cleanup)");
134 			log_msg("server join (cleanup) failed with %d\n", e);
135 		}
136 
137 		log_msg("Server thread joined %d.\n", (int)this);
138 
139 		monitor_stop();
140 		log_msg("scanner thread shutdown complete\n");
141 
142 	} else if (pthread_equal(this, master)) {
143 
144 		(void) fprintf(stderr,
145 			"cleanup_state: master thread shutdown\n");
146 		log_msg("master thread shutdown\n");
147 
148 	} else {
149 
150 		(void) fprintf(stderr,
151 		    "cleanup_state: unknown thread id %d\n", (int)this);
152 		log_msg("unknown thread %d shutdown\n", (int)this);
153 
154 	}
155 }
156 
157 
158 /*
159  * Called by any of the threads, this should set state
160  * that the other threads will pick up so they will (eventually)
161  * shut themselves down cleanly, then call pthread_exit
162  * to properly shut down the calling thread.
163  * The calling thread will exit with its code set to 1.
164  */
165 static void
generic_exit(char * msg,int status)166 generic_exit(char *msg, int status)
167 {
168 	char wb[256];
169 
170 	/* cannot be on the stack since thread terminates with pthread_exit */
171 	static int retcode = 0;
172 
173 	retcode = status;
174 
175 	/* worker-specific cleanup */
176 	thread_cleanup();
177 
178 	/* announce the calling thread's demise */
179 	(void) snprintf(wb, sizeof (wb) - 2, "(%d) %s",
180 			(int)pthread_self(), msg);
181 	log_msg(wb);
182 	(void) fprintf(stderr, "%s", wb);
183 
184 	/* everybody checks this periodically */
185 	sigterm = 1;
186 
187 	log_msg("calling thread_exit() from %d\n", (int)pthread_self());
188 
189 	/* return status as the calling thread's exit code */
190 	pthread_exit(&retcode);
191 
192 }
193 
194 
195 /*
196  * Called by any of the threads, this should set state
197  * that the other threads will pick up so they will (eventually)
198  * shut themselves down cleanly, then call pthread_exit
199  * to properly shut down the calling thread.
200  * The calling thread will exit with its code set to 1.
201  */
202 void
err_exit()203 err_exit()
204 {
205 	generic_exit(errmsg, 1);
206 }
207 
208 
209 /*
210  * Called by any of the threads, this should set state
211  * that the other threads will pick up so they will (eventually)
212  * shut themselves down cleanly, then call pthread_exit
213  * to properly shut down the calling thread.
214  * The calling thread will exit with its code set to 0.
215  */
216 static void
ok_exit()217 ok_exit()
218 {
219 	generic_exit("Normal exit.\n", 0);
220 }
221 
222 
223 static void
usage()224 usage()
225 {
226 	(void) printf("rds [ options ]\n" \
227 	    "-u\t\t- print stats for all users\n" \
228 	    "-U<uid>\t\t- print stats for <uid>\n" \
229 	    "-j\t\t- print stats for all projects\n" \
230 	    "-J<projid>\t- print stats for <projid>\n" \
231 	    "-p\t\t- print stats for all processes\n" \
232 	    "-P <pid>\t- print stats for <pid>\n" \
233 	    "-m\t\t- enable microstate accounting\n" \
234 	    "-a\t\t- run in server mode\n" \
235 	    "-t<time>\t- set command timeout to <time>\n" \
236 	    "-i<interval>\t- set interval between scans to <time>\n" \
237 	    "-f<file>\t- use <file> to save/restore state\n" \
238 	    "-d\t\t- in server mode print stats on stdout\n" \
239 	    "-L<file>|stderr - write log messages into <file> or stderr\n" \
240 	    "-v\t\t- print rds version\n");
241 }
242 
243 
244 /*
245  * Initiate the rds command protocol from the server side.
246  * Emits the header and version strings.
247  */
248 static void
start_protocol()249 start_protocol()
250 {
251 	/* emit version and header strings */
252 	if (wr_string(greeting) != 0)
253 		err_exit();
254 	if (wr_phead() != 0)
255 		err_exit();
256 }
257 
258 
259 /*
260  * Emit the "not ready" message and a prompt.
261  */
262 static void
notready()263 notready()
264 {
265 	(void) wr_string(NOTREADY_RESPONSE);
266 	(void) wr_string("\n");
267 	(void) wr_prompt(PROMPT_OK);
268 }
269 
270 
271 /*
272  * process_cmds() implements the rds server running in threaded mode.
273  *
274  * It assumes that the /proc scanner is running in another thread and
275  * guarding access to critical sections.
276  *
277  * This function writes version and header to the output stream and waits
278  * for commands on the input stream.
279  *
280  * Each received command may block on a mutex while the scanner thread is
281  * updating.
282  *
283  * If the timeout expires without receiving a command, it will write an
284  * error message and terminate.	 A received command resets the timeout.
285  *
286  * Each command is acknowledged with a prompt.
287  */
288 
289 /*ARGSUSED*/
290 static void *
process_cmds(void * p)291 process_cmds(void *p)
292 {
293 	fd_set	readfs;
294 	struct	timeval timev;
295 	int	interval_cnt = timeout / interval;
296 	int	ret;
297 	char	*cmd;
298 	hrtime_t t1, t2, wt1, wt2;
299 	double	d;
300 	int 	cmd_is_noop;
301 
302 	/* start the protocol so the client knows we're alive */
303 	start_protocol();
304 
305 	/* establish timeout value */
306 	timev.tv_sec = interval / 1000;
307 	timev.tv_usec = (interval - (timev.tv_sec * 1000)) * 1000;
308 
309 	/* initialize stdin object */
310 	(void) FD_ZERO(&readfs);
311 	FD_SET(STDIN_FILENO, &readfs);
312 
313 	/* emit initial prompt */
314 	(void) wr_prompt(PROMPT_OK);
315 
316 	while (interval_cnt > 0) {
317 
318 		/* time to shut down, exit gracefully */
319 		if (sigterm == 1) {
320 			break;		/* ok_exit(); */
321 		}
322 
323 		/* check for stdin status */
324 		FD_SET(STDIN_FILENO, &readfs);
325 
326 		/* block on stdin, max timeout */
327 		if ((ret = select(1, &readfs, NULL, NULL, &timev)) == 0) {
328 
329 			/* timed out waiting for a command */
330 			--interval_cnt;
331 			continue;
332 		}
333 
334 		/* if interrupted system call then exit gracefully */
335 		if (ret == -1 && errno == EINTR) {
336 			log_msg("select() interrupted\n");
337 			ok_exit();
338 		}
339 
340 		/* weird error condition */
341 		if (ret != 1) {
342 			perror("RDS Select error");
343 			log_msg("select() error = %d\n", errno);
344 			continue;
345 		}
346 
347 		/* process whatever is waiting on stdin */
348 		if (FD_ISSET(STDIN_FILENO, &readfs)) {
349 
350 			cmd_is_noop = 0;
351 
352 			/* try to parse out a valid command */
353 			if ((cmd = r_cmd()) == NULL) {
354 				err_exit();
355 			}
356 			log_msg("received '%s' command\n", cmd);
357 			t1 = gethrtime();
358 
359 			/* handle the various commands */
360 			if (strcmp(cmd, CMD_EXIT) == 0) {
361 
362 				/* exit now */
363 				(void) wr_prompt(PROMPT_OK);
364 				ok_exit();
365 
366 			} else if (strcmp(cmd, CRETURN) == 0) {
367 
368 				/* null command */
369 				(void) wr_prompt(PROMPT_OK);
370 				++cmd_is_noop;
371 
372 			} else if (strcmp(cmd, CMD_ALIVE) == 0) {
373 
374 				/* keepalive, another null command */
375 				(void) wr_prompt(PROMPT_OK);
376 				++cmd_is_noop;
377 
378 			} else if (strcmp(cmd, CMD_GETALL) == 0) {
379 
380 				/* get all project/user data */
381 
382 				/*
383 				 * If the first scan has not yet
384 				 * completed, notify the requester and
385 				 * wait for a new command.  The
386 				 * command timeout counter is
387 				 * suspended until the next command
388 				 * arrives.
389 				 */
390 				if (scans_done == 0) {
391 					notready();
392 					continue;
393 				}
394 
395 				/* grab the mutex */
396 				wt1 = gethrtime();
397 
398 				if ((ret = pthread_mutex_lock(
399 					&listLock)) == 0) {
400 
401 					wt2 = gethrtime();
402 					d = (double)
403 					    (wt2 - wt1) / 1000000000.0;
404 					log_msg("Server lock wait"
405 					    " was %1.5f sec\n", d);
406 
407 					if (wr_lshead(5) != 0)
408 						err_exit();
409 
410 					if (list_write(L_AC_USR, Po) == -1)
411 						break;
412 					if (list_write(L_USR_SI, Po) == -1)
413 						break;
414 					if (list_write(L_AC_PRJ, Po) == -1)
415 						break;
416 					if (list_write(L_PRJ_SI, Po) == -1)
417 						break;
418 					if (list_write(L_SYSTEM, Po) == -1)
419 						break;
420 
421 					/* release the mutex */
422 					if ((ret = pthread_mutex_unlock(
423 						&listLock)) != 0) {
424 						log_msg("pthread_mutex_unlock" \
425 						    "failed with %d\n", ret);
426 					}
427 
428 				} else {
429 					log_msg("pthread_mutex_lock failed" \
430 					    "with %d\n", ret);
431 				}
432 
433 				(void) wr_prompt(PROMPT_OK);
434 
435 			} else if (strcmp(cmd, CMD_GETPL) == 0) {
436 
437 				/* get all process data (deprecated?) */
438 
439 				if (scans_done == 0) {
440 					notready();
441 					continue;
442 				}
443 
444 				/* grab the mutex */
445 				if ((ret = pthread_mutex_lock(
446 					&listLock)) == 0) {
447 
448 					if (wr_lshead(1) != 0)
449 						err_exit();
450 
451 					if (list_write(L_PRC_SI, Po) == -1)
452 						break;
453 
454 					/* release the mutex */
455 					if ((ret = pthread_mutex_unlock(
456 						&listLock)) != 0) {
457 						log_msg("pthread_mutex_unlock"\
458 						    "failed with %d\n", ret);
459 					}
460 
461 				} else {
462 					log_msg("pthread_mutex_lock"\
463 					    "failed with %d\n", ret);
464 				}
465 
466 				(void) wr_prompt(PROMPT_OK);
467 
468 			} else if (strcmp(cmd, CMD_GETUL) == 0) {
469 
470 				/* get the active user list */
471 
472 				if (scans_done == 0) {
473 					notready();
474 					continue;
475 				}
476 
477 				/* grab the mutex */
478 				if ((ret = pthread_mutex_lock(
479 					&listLock)) == 0) {
480 
481 					if (wr_lshead(1) != 0)
482 						err_exit();
483 
484 
485 					if (list_write(L_USR_SI, Po) == -1)
486 						break;
487 
488 					/* release the mutex */
489 					if ((ret = pthread_mutex_unlock(
490 						&listLock)) != 0) {
491 						log_msg("pthread_mutex_unlock"\
492 						    "failed with %d\n", ret);
493 					}
494 
495 				} else {
496 					log_msg("pthread_mutex_lock" \
497 					    "failed with %d\n", ret);
498 				}
499 
500 				(void) wr_prompt(PROMPT_OK);
501 
502 			} else if (strcmp(cmd, CMD_GETAUL) == 0) {
503 
504 				/* get data for a particular user */
505 
506 				if (scans_done == 0) {
507 					notready();
508 					continue;
509 				}
510 
511 				/* grab the mutex */
512 				if ((ret = pthread_mutex_lock(
513 					&listLock)) == 0) {
514 
515 					if (wr_lshead(1) != 0)
516 						err_exit();
517 
518 					if (list_write(L_AC_USR, Po) == -1)
519 						break;
520 
521 					/* release the mutex */
522 					if ((ret = pthread_mutex_unlock(
523 						&listLock)) != 0) {
524 						log_msg("pthread_mutex_unlock" \
525 						    "failed with %d\n", ret);
526 					}
527 
528 				} else {
529 					log_msg("pthread_mutex_lock" \
530 					    "failed with %d\n", ret);
531 				}
532 
533 				(void) wr_prompt(PROMPT_OK);
534 
535 			} else if (strcmp(cmd, CMD_GETJL) == 0) {
536 
537 				if (scans_done == 0) {
538 					notready();
539 					continue;
540 				}
541 
542 				/* grab the mutex */
543 				if ((ret = pthread_mutex_lock(
544 					&listLock)) == 0) {
545 
546 					if (wr_lshead(1) != 0)
547 						err_exit();
548 
549 					/* grab the mutex here */
550 
551 					if (list_write(L_PRJ_SI, Po) == -1)
552 						break;
553 
554 					/* release the mutex */
555 					if ((ret = pthread_mutex_unlock(
556 						&listLock)) != 0) {
557 						log_msg("pthread_mutex_unlock" \
558 						    "failed with %d\n", ret);
559 					}
560 
561 				} else {
562 					log_msg("pthread_mutex_lock" \
563 					    "failed with %d\n", ret);
564 				}
565 
566 				(void) wr_prompt(PROMPT_OK);
567 
568 			} else if (strcmp(cmd, CMD_GETAJL) == 0) {
569 
570 				if (scans_done == 0) {
571 					notready();
572 					continue;
573 				}
574 
575 				/* grab the mutex */
576 				if ((ret = pthread_mutex_lock(
577 					&listLock)) == 0) {
578 
579 					if (wr_lshead(1) != 0)
580 						err_exit();
581 
582 					if (list_write(L_AC_PRJ, Po) == -1)
583 						break;
584 
585 					/* release the mutex */
586 					if ((ret = pthread_mutex_unlock(
587 						&listLock)) != 0) {
588 						log_msg("pthread_mutex_unlock" \
589 						    "failed with %d\n", ret);
590 					}
591 
592 				} else {
593 					log_msg("pthread_mutex_lock" \
594 					    "failed with %d\n", ret);
595 				}
596 
597 				(void) wr_prompt(PROMPT_OK);
598 
599 			} else if (strcmp(cmd, CMD_GETASL) == 0) {
600 
601 				if (scans_done == 0) {
602 					notready();
603 					continue;
604 				}
605 
606 				/* grab the mutex */
607 				if ((ret = pthread_mutex_lock(
608 					&listLock)) == 0) {
609 
610 					if (wr_lshead(1) != 0)
611 						err_exit();
612 
613 					if (list_write(L_SYSTEM, Po) == -1)
614 						break;
615 
616 					/* release the mutex */
617 					if ((ret = pthread_mutex_unlock(
618 						&listLock)) != 0) {
619 						log_msg("pthread_mutex_unlock"
620 						    "failed with %d\n", ret);
621 					}
622 
623 				} else {
624 					log_msg("pthread_mutex_lock"
625 					    "failed with %d\n", ret);
626 				}
627 
628 				(void) wr_prompt(PROMPT_OK);
629 
630 			} else {
631 
632 				/* bad command */
633 				(void) wr_prompt(PROMPT_WHAT);
634 				format_err("RDS protocol error:"
635 				    "unknown command");
636 				++cmd_is_noop;
637 
638 			}
639 
640 			if (!cmd_is_noop) {
641 				t2 = gethrtime();
642 				d = (double)(t2 - t1) / 1000000000.0;
643 				log_msg("Command took %2.3f sec"
644 				    " (%ld scans done)\n",
645 				    d, scans_done);
646 			}
647 
648 			/* reset the interval counter for timeout */
649 			interval_cnt = timeout / interval;
650 
651 			continue;
652 		}
653 
654 		/* timed out, one less interval to wait */
655 		--interval_cnt;
656 	}
657 
658 	/* timed out, print message */
659 	if (interval_cnt == 0) {
660 		format_err("%s %d sec. left", TIMEOUT_MSG, timeout / 1000);
661 		err_exit();
662 	}
663 
664 	/* clean exit */
665 	log_msg("process_cmds exits\n");
666 	ok_exit();			/* calls pthread_exit() */
667 
668 	return (NULL);
669 }
670 
671 
672 /*
673  * The thread procedure for the /proc scanner.
674  * Does a full scan of /proc, then sleeps for a specified time.
675  *
676  * The specified time ('interval') is adjusted according to
677  * the average of the last three scan times.
678  * The sleep time is increase if the average scan duration time
679  * exceeds a threshold. The threshold is set to 50% of the current
680  * sleep time.
681  * The sleep time is decreased in a similar way.
682  *
683  * The update of the project and user lists is guarded by aggregate_list_mutex.
684  * The update of the process list is guarded by process_list_mutex.
685  */
686 
687 /*ARGSUSED*/
688 static void *
scanprocfs(void * p)689 scanprocfs(void *p)
690 {
691 	hrtime_t t1;
692 
693 	double d0; /* duration of the for last scan */
694 	double d1; /* duration of the last scan */
695 	double d2; /* duration of current scan */
696 	double ad; /* average duration of the last three scans */
697 	double threshold_up; /* threshold for increasing scan duration */
698 	double threshold_down; /* threshold for decreasing scan duration */
699 	double thf =  0.5; /* */
700 	int new_interval = interval;
701 	int time_to_sleep;
702 
703 	threshold_up = new_interval * thf;
704 	threshold_down = 0;
705 	d0 = d1 = d2 = ad = 0;
706 
707 
708 	while (sigterm != 1) {
709 		t1 = gethrtime();
710 
711 		if (monitor_update() != 0)
712 			err_exit();
713 
714 		++scans_done;
715 
716 		/* make sure we're sleeping a reasonable amount of time */
717 		d0 = d1; d1 = d2;
718 		d2 = (gethrtime() - t1) / 1000000.0;
719 		ad = (d0 + d1 + d2) / 3.0;
720 
721 		if (threshold_up < ad) {
722 			/* increase the new_interval in 1000 ms steps	*/
723 			new_interval += (int)((ad - threshold_up) / thf);
724 			if (new_interval > MAXIMAL_SCAN_INTERVAL)
725 				new_interval = MAXIMAL_SCAN_INTERVAL;
726 			if ((new_interval % 1000) > 500)
727 				new_interval += 500;
728 			new_interval = (new_interval / 1000) * 1000;
729 			/* pull up the thresholds */
730 			threshold_down = threshold_up;
731 			threshold_up = new_interval * thf;
732 		}
733 
734 		if (threshold_down > ad) {
735 			/* decrease the new_interval in 1000 ms steps	*/
736 			new_interval -= (int)((threshold_down - ad) / thf);
737 			if ((new_interval % 1000) > 500)
738 				new_interval += 500;
739 			new_interval = (new_interval / 1000) * 1000;
740 			/* pull down the thresholds */
741 			if (new_interval < interval) {
742 				/* just as at the beginning	*/
743 				new_interval = interval;
744 				threshold_down = 0;
745 				threshold_up = new_interval * thf;
746 			} else {
747 				threshold_up = threshold_down;
748 				threshold_down = new_interval * thf;
749 			}
750 		}
751 
752 		log_msg("scan %.0f ms, ad %.0f ms, thold_up %.0f ms,"
753 		    " thold_down %.0f ms, interval %d ms\n",
754 		    d2, ad, threshold_up, threshold_down, new_interval);
755 		log_msg("%d files open\n", fd_count());
756 
757 		time_to_sleep = new_interval;
758 		while (time_to_sleep > 0) {
759 			napms(1000);
760 			time_to_sleep -= 1000;
761 			if (sigterm == 1)
762 				break;
763 		}
764 	}
765 
766 	log_msg("scanprocfs exits\n");
767 	ok_exit();
768 
769 	return (NULL);
770 }
771 
772 static void
sig_rds(int sig)773 sig_rds(int sig)
774 {
775 	log_msg("caught signal #%d\n", sig);
776 	switch (sig) {
777 	case SIGINT:
778 	case SIGTERM:
779 		sigterm = 1;
780 		break;
781 	}
782 }
783 
784 
785 /*
786  * Run the command processor, with the /proc scanner and rds command processor
787  * in separate threads.
788  *
789  * Initializes the mutex as a side effect.
790  *
791  * Returns on exit of the command process or as a result of a signal.
792  */
793 static void
runserver()794 runserver()
795 {
796 	int rv;
797 
798 	/* keep track of main()'s thread */
799 	master = pthread_self();
800 	log_msg("master thread = %d\n", (int)master);
801 
802 	/* initialize the mutexes for later use */
803 	rv = pthread_mutex_init(&listLock, NULL);
804 	if (rv != 0) {
805 		(void) sprintf(errmsg, "Mutex init failed with %d", rv);
806 		err_exit();
807 	}
808 
809 	rv = pthread_mutex_init(&listLock, NULL);
810 	if (rv != 0) {
811 		(void) sprintf(errmsg, "Mutex init failed with %d", rv);
812 		err_exit();
813 	}
814 
815 	log_msg("pthread_mutex_init returns %d\n", rv);
816 
817 	/* launch the command processor in its thread */
818 	rv = pthread_create(&server, NULL, process_cmds, NULL);
819 	if (rv != 0) {
820 		(void) sprintf(errmsg,
821 		    "Server thread create failed with %d", rv);
822 		err_exit();
823 
824 	}
825 	log_msg("Server pthread_create = %d returns %d\n",
826 	    (int)server, rv);
827 
828 
829 	/* launch the scanner in its thread */
830 	rv = pthread_create(&scanner, NULL, scanprocfs, NULL);
831 	if (rv != 0) {
832 		(void) sprintf(errmsg,
833 		    "Scanner thread create failed with %d", rv);
834 		err_exit();
835 	}
836 	log_msg("Scanner pthread_create = %d returns %d\n",
837 	    (int)scanner, rv);
838 
839 
840 	/* nothing much else to do here */
841 	while (sigterm != 1)
842 		(void) sleep(1);
843 
844 	/* wait for the scanner & server threads to shut down */
845 	log_msg("Waiting for scanner thread %d join from %d\n",
846 	    (int)scanner, (int)pthread_self());
847 	if (pthread_join(scanner, NULL) != 0) {
848 		int e = errno;
849 		perror("scanner join");
850 		log_msg("scanner join failed with %d\n", e);
851 	}
852 	log_msg("Scanner thread joined.\n");
853 
854 	/* finish cleaning up global state */
855 	(void) pthread_mutex_destroy(&listLock);
856 
857 	log_msg("Global cleanup completed.\n");
858 }
859 
860 
861 int
main(int argc,char * argv[])862 main(int argc, char *argv[])
863 {
864 	int i,	uo = 0, jo = 0, po = 0, do_server_mode = 0,
865 	    selected = 0;
866 	int	lo_arg = 1;
867 	int	uid = -1, pid = -1, jid = -1;
868 	int	rv;
869 
870 	/* parse args */
871 	while ((i = getopt(argc, argv, "uU:jJ:pP:mat:i:l:f:dvL:")) != EOF)
872 		switch (i) {
873 		case 'U':
874 			uid = atoi(optarg);
875 			uo = 1; selected = 1;
876 			break;
877 		case 'u':
878 			uo = 1; selected = 1;
879 			break;
880 		case 'J':
881 			jid = atoi(optarg);
882 			jo = 1; selected = 1;
883 			break;
884 		case 'j':
885 			jo = 1; selected = 1;
886 			break;
887 		case 'P':
888 			pid = atoi(optarg);
889 			po = 1; selected = 1;
890 			break;
891 		case 'p':
892 			po = 1; selected = 1;
893 			break;
894 		case 'a':
895 			do_server_mode = 1;
896 			break;
897 		case 'l':
898 			if ((lo_arg = atoi(optarg)) == 0) {
899 				usage();
900 				exit(1);
901 			}
902 			break;
903 		case 'd':
904 			Po = 1;
905 			break;
906 		case 't':
907 			if ((timeout  = atoi(optarg)) < 1000) {
908 				usage();
909 				exit(1);
910 			}
911 			break;
912 		case 'i':
913 			if ((interval  = atoi(optarg)) < 100) {
914 				usage();
915 				exit(1);
916 			}
917 			break;
918 		case 'f':
919 			ltdb_file = optarg;
920 			break;
921 		case 'L':
922 			log_file = optarg;
923 			break;
924 		case 'm':
925 			mo = 1;
926 			break;
927 		case 'v': (void) printf(RDS_VERSION);
928 			exit(1);
929 			break;
930 		case '?':
931 			usage();
932 			exit(1);
933 		default:
934 			usage();
935 			exit(1);
936 		}
937 
938 
939 	/* set handlers */
940 	(void) signal(SIGINT, sig_rds);
941 	(void) signal(SIGTERM, sig_rds);
942 	(void) sigignore(SIGPIPE);
943 
944 	(void) enable_extended_FILE_stdio(-1, -1);
945 
946 	/* initialize the log mutex */
947 	rv = pthread_mutex_init(&logLock, NULL);
948 	if (rv != 0) {
949 		(void) sprintf(errmsg, "Mutex init failed with %d", rv);
950 		err_exit();
951 	}
952 
953 	if (log_file != NULL)
954 		log_open(log_file);
955 
956 	if (do_server_mode == 1) {
957 
958 		/*
959 		 * Initialize list data structures, possibly
960 		 * reading saved data.
961 		 *
962 		 * As a side effect this messes with the protocol
963 		 * state since the list reader pretends it's reading
964 		 * the protocol.
965 		 *
966 		 * A problem here is that we cannot start the server
967 		 * thread until this has completed because it will try to
968 		 * use the same state hidden inside the protocol code.
969 		 *
970 		 * The consequence is that this may occupy the main
971 		 * thread for an arbitrarily long time *before* the server
972 		 * thread is started and the app becomes able to respond
973 		 * to commands.
974 		 */
975 		if (monitor_start() != 0)
976 			err_exit();
977 
978 		/* Open pipes in and out for the command protocol */
979 		if (open_prot(STDOUT_FILENO, "w") == -1) {
980 			err_exit();
981 		}
982 		if (open_prot(STDIN_FILENO, "r") == -1) {
983 			err_exit();
984 		}
985 
986 		/* Waits for the child threads to end */
987 		runserver();
988 
989 		/* Close command I/O pipes */
990 		close_prot();
991 
992 	} else {
993 
994 		if (monitor_start() != 0)
995 			err_exit();
996 
997 		for (i = 0; i < lo_arg; i ++) {
998 			if (sigterm == 1)
999 				break;
1000 			if (monitor_update()  != 0)
1001 				err_exit();
1002 			if (selected == 0 || uo == 1) {
1003 				list_print(&users, uid);
1004 			}
1005 			if (selected == 0 || jo == 1) {
1006 				list_print(&projects, jid);
1007 			}
1008 			if (selected == 0 || po == 1) {
1009 				list_print(&processes, pid);
1010 			}
1011 			if (i < lo_arg - 1)
1012 				napms(interval);
1013 		}
1014 	}
1015 
1016 	/* clean up the log stuff at the very end */
1017 	log_close();
1018 	(void) pthread_mutex_destroy(&logLock);
1019 
1020 	return (0);
1021 }
1022