qa: ensure test 1087 places bind2 config file in the right spot
[pcp.git] / src / pmlc / pmlc.c
blobcb477fb2d963189b045d6bf7cb1e63fc0e1d531d
1 /*
2 * Copyright (c) 2014 Red Hat.
3 * Copyright (c) 1995-2005 Silicon Graphics, Inc. All Rights Reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
16 #include "pmapi.h"
17 #include "impl.h"
18 #include "pmlc.h"
19 #include "gram.h"
21 char *configfile;
22 __pmLogCtl logctl;
23 int parse_done;
24 int pid = PM_LOG_NO_PID;
25 int port = PM_LOG_NO_PORT;
26 int is_unix; /* host spec is a unix: url. */
27 int is_local; /* host spec is a local: url. */
28 int is_socket_path; /* host spec is a url with a path. */
29 char *tz; /* for -Z timezone */
30 int tztype = TZ_LOCAL; /* timezone for status cmd */
31 int eflag;
32 int iflag;
34 extern int parse_stmt;
35 extern int tzchange;
37 static char title[] = "Performance Co-Pilot Logger Control (pmlc), Version %s\n\n%s\n";
38 static char menu[] =
39 "pmlc commands\n\n"
40 " show loggers [@<host>] display <pid>s of running pmloggers\n"
41 " connect _logger_id [@<host>] connect to designated pmlogger\n"
42 " status information about connected pmlogger\n"
43 " query metric-list show logging state of metrics\n"
44 " new volume start a new log volume\n"
45 "\n"
46 " log { mandatory | advisory } on <interval> _metric-list\n"
47 " log { mandatory | advisory } off _metric-list\n"
48 " log mandatory maybe _metric-list\n"
49 "\n"
50 " timezone local|logger|'<timezone>' change reporting timezone\n"
51 " help print this help message\n"
52 " quit exit from pmlc\n"
53 "\n"
54 " _logger_id is primary | <pid> | port <n>\n"
55 " _metric-list is _metric-spec | { _metric-spec ... }\n"
56 " _metric-spec is <metric-name> | <metric-name> [ <instance> ... ]\n";
58 static int overrides(int, pmOptions *);
60 static pmLongOptions longopts[] = {
61 PMAPI_OPTIONS_HEADER("Options"),
62 PMOPT_DEBUG,
63 { "echo", 0, 'e', 0, "echo input" },
64 { "host", 1, 'h', "HOST", "connect to pmlogger using host specification" },
65 { "interactive", 0, 'i', 0, "be interactive and prompt" },
66 PMOPT_NAMESPACE,
67 { "primary", 0, 'P', 0, "connect to primary pmlogger" },
68 { "port", 1, 'p', "N", "connect to pmlogger on this TCP/IP port" },
69 PMOPT_TIMEZONE,
70 { "logzone", 0, 'z', 0, "set reporting timezone to local time for pmlogger" },
71 PMOPT_HELP,
72 PMAPI_OPTIONS_END
75 static pmOptions opts = {
76 .short_options = "D:eh:in:Pp:zZ:?",
77 .long_options = longopts,
78 .short_usage = "[options] [pid]",
79 .override = overrides,
82 static int
83 overrides(int opt, pmOptions *opts)
85 if (opt == 'h' || opt == 'p')
86 return 1;
87 return 0;
90 int
91 main(int argc, char **argv)
93 int c;
94 int sts = 0; /* initialize to pander to gcc */
95 char *host = NULL;
96 char *endnum;
97 int primary;
98 size_t prefix_len;
99 char *prefix_end;
101 iflag = isatty(0);
103 while ((c = pmGetOptions(argc, argv, &opts)) != EOF) {
104 switch (c) {
106 case 'e': /* echo input */
107 eflag++;
108 break;
110 case 'h': /* hostspec */
112 * We need to know if a socket path has been specified.
114 host = opts.optarg;
115 prefix_end = strchr(host, ':');
116 if (prefix_end != NULL) {
117 prefix_len = prefix_end - host + 1;
118 if (prefix_len == 6 && strncmp(host, "local:", prefix_len) == 0)
119 is_local = 1;
120 else if (prefix_len == 5 && strncmp(host, "unix:", prefix_len) == 0)
121 is_unix = 1;
122 if (is_local || is_unix) {
123 const char *p;
125 * Find out is a path was specified.
126 * Skip any initial path separators.
128 for (p = host + prefix_len; *p == __pmPathSeparator(); ++p)
130 if (*p != '\0')
131 is_socket_path = 1;
134 break;
136 case 'i': /* be interactive */
137 iflag++;
138 break;
140 case 'P': /* connect to primary logger */
141 if (port != PM_LOG_NO_PORT || (is_unix && is_socket_path)) {
142 pmprintf("%s: at most one of -P, -p, unix socket, or PID may be specified\n",
143 pmProgname);
144 opts.errors++;
145 } else {
146 port = PM_LOG_PRIMARY_PORT;
148 break;
150 case 'p': /* connect via port */
151 if (port != PM_LOG_NO_PORT || is_unix) {
152 pmprintf("%s: at most one of -P, -p, unix socket, or PID may be specified\n",
153 pmProgname);
154 opts.errors++;
155 } else {
156 port = (int)strtol(opts.optarg, &endnum, 10);
157 if (*endnum != '\0' || port <= PM_LOG_PRIMARY_PORT) {
158 pmprintf("%s: port must be numeric and greater than %d\n",
159 pmProgname, PM_LOG_PRIMARY_PORT);
160 opts.errors++;
163 break;
167 if (opts.optind < argc - 1)
168 opts.errors++;
169 else if (opts.optind == argc - 1) {
170 /* pid was specified */
171 if (port != PM_LOG_NO_PORT || (is_unix && is_socket_path)) {
172 pmprintf("%s: at most one of -P, -p, unix socket, or PID may be specified\n",
173 pmProgname);
174 opts.errors++;
176 else {
177 pid = (int)strtol(argv[opts.optind], &endnum, 10);
178 if (*endnum != '\0' || pid <= PM_LOG_PRIMARY_PID) {
179 pmprintf("%s: PID must be a numeric process ID and greater than %d\n",
180 pmProgname, PM_LOG_PRIMARY_PID);
181 opts.errors++;
186 if (!opts.errors && host && pid == PM_LOG_NO_PID &&
187 port == PM_LOG_NO_PORT && !is_socket_path) {
188 pmprintf("%s: -h may not be used without -P or -p or a socket path or a PID\n",
189 pmProgname);
190 opts.errors++;
193 if (opts.errors) {
194 pmUsageMessage(&opts);
195 exit(1);
198 if (opts.tzflag) {
199 tztype = TZ_LOGGER;
200 tzchange = 1;
201 } else if (opts.timezone) {
202 tz = opts.timezone;
203 tztype = TZ_OTHER;
204 tzchange = 1;
207 if (host == NULL)
208 host = "local:";
210 primary = 0;
211 if (port == PM_LOG_PRIMARY_PORT || pid == PM_LOG_PRIMARY_PID)
212 primary = 1;
214 if (pid != PM_LOG_NO_PID || port != PM_LOG_NO_PORT || is_socket_path)
215 sts = ConnectLogger(host, &pid, &port);
217 if (iflag)
218 printf(title, PCP_VERSION, menu);
220 if (pid != PM_LOG_NO_PID || port != PM_LOG_NO_PORT || is_socket_path) {
221 if (sts < 0) {
222 if (primary) {
223 fprintf(stderr, "Unable to connect to primary pmlogger at %s: ",
224 host);
225 if (still_connected(sts))
226 fprintf(stderr, "%s\n", pmErrStr(sts));
228 else if (is_socket_path) {
229 fprintf(stderr, "Unable to connect to pmlogger via the local socket at %s: ",
230 host);
231 if (still_connected(sts))
232 fprintf(stderr, "%s\n", pmErrStr(sts));
234 else if (port != PM_LOG_NO_PORT) {
235 fprintf(stderr, "Unable to connect to pmlogger on port %d at %s: ",
236 port, host);
237 if (still_connected(sts))
238 fprintf(stderr, "%s\n", pmErrStr(sts));
240 else {
241 fprintf(stderr, "Unable to connect to pmlogger pid %d at %s: ",
242 pid, host);
243 if (still_connected(sts))
244 fprintf(stderr, "%s\n", pmErrStr(sts));
247 else {
248 if (primary)
249 printf("Connected to primary pmlogger at %s\n", host);
250 else if (is_socket_path)
251 printf("Connected to pmlogger via local socket at %s\n", host);
252 else if (port != PM_LOG_NO_PORT)
253 printf("Connected to pmlogger on port %d at %s\n", port, host);
254 else
255 printf("Connected to pmlogger pid %d at %s\n", pid, host);
259 for ( ; ; ) {
260 char *realhost;
262 is_local = 0;
263 is_unix = 0;
264 is_socket_path = 0;
265 parse_stmt = -1;
266 metric_cnt = 0;
267 yyparse();
268 if (yywrap()) {
269 if (iflag)
270 putchar('\n');
271 break;
273 if (metric_cnt < 0)
274 continue;
275 #ifdef PCP_DEBUG
276 if (pmDebug & DBG_TRACE_APPL1)
277 printf("stmt=%d, state=%d, control=%d, hostspec=%s, pid=%d, port=%d\n",
278 parse_stmt, state, control, hostname, pid, port);
279 #endif
281 realhost = (hostname == NULL) ? host : hostname;
282 switch (parse_stmt) {
284 case SHOW:
285 ShowLoggers(realhost);
286 break;
288 case CONNECT:
289 /* The unix: url requres either 'primary', a pid or a socket path. */
290 if (is_unix && pid == PM_LOG_NO_PID && ! is_socket_path) {
291 fprintf(stderr, "The 'unix:' url requires either 'primary', a pid or a socket path");
292 if (still_connected(sts))
293 fprintf(stderr, "\n");
294 break;
296 /* The local: url requres either 'primary', a pid a port or a socket path. */
297 if (is_local && pid == PM_LOG_NO_PID && port == PM_LOG_NO_PORT && ! is_socket_path) {
298 fprintf(stderr, "The 'local:' url requires either 'primary', a pid, a port or a socket path");
299 if (still_connected(sts))
300 fprintf(stderr, "\n");
301 break;
303 primary = 0;
304 if (port == PM_LOG_PRIMARY_PORT || pid == PM_LOG_PRIMARY_PID)
305 primary = 1;
306 if ((sts = ConnectLogger(realhost, &pid, &port)) < 0) {
307 if (primary) {
308 fprintf(stderr, "Unable to connect to primary pmlogger at %s: ",
309 realhost);
311 else if (is_socket_path) {
312 fprintf(stderr, "Unable to connect to pmlogger via local socket at %s: ",
313 realhost);
315 else if (port != PM_LOG_NO_PORT) {
316 fprintf(stderr, "Unable to connect to pmlogger on port %d at %s: ",
317 port, realhost);
319 else {
320 fprintf(stderr, "Unable to connect to pmlogger pid %d at %s: ",
321 pid, realhost);
323 if (still_connected(sts))
324 fprintf(stderr, "%s\n", pmErrStr(sts));
326 else
327 /* if the timezone is "logger time", it has changed
328 * because the logger may be in a different zone
329 * (note that tzchange may already be set (e.g. -Z and
330 * this connect is the first).
332 tzchange |= (tztype == TZ_LOGGER);
333 break;
335 case HELP:
336 puts(menu);
337 break;
339 case LOG:
340 if (state == PM_LOG_ENQUIRE) {
341 Query();
342 break;
344 if (logfreq == -1)
345 logfreq = 0;
346 if (state == PM_LOG_ON) {
347 if (logfreq < 0) {
348 fprintf(stderr, "Logging delta (%d msec) must be positive\n", logfreq);
349 break;
351 else if (logfreq > PMLC_MAX_DELTA) {
352 fprintf(stderr, "Logging delta (%d msec) cannot be bigger than %d msec\n", logfreq, PMLC_MAX_DELTA);
353 break;
356 LogCtl(control, state, logfreq);
357 break;
359 case QUIT:
360 printf("Goodbye\n");
361 DisconnectLogger();
362 exit(0);
363 break;
365 case STATUS:
366 Status(pid, primary);
367 break;
369 case NEW:
370 NewVolume();
371 break;
373 case TIMEZONE:
374 tzchange = 1;
375 break;
377 case SYNC:
378 Sync();
379 break;
381 case QA:
382 if (qa_case == 0)
383 fprintf(stderr, "QA Test Case deactivated\n");
384 else
385 fprintf(stderr, "QA Test Case #%d activated\n", qa_case);
386 Qa();
389 if (hostname != NULL) {
390 free(hostname);
391 hostname = NULL;
396 DisconnectLogger();
397 exit(0);