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