cfengine  3.15.4
About: CFEngine is a configuration management system for configuring and maintaining Unix-like computers (using an own high level policy language). Community version.
  Fossies Dox: cfengine-3.15.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

processes_select.c
Go to the documentation of this file.
1 /*
2  Copyright 2019 Northern.tech AS
3 
4  This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5 
6  This program is free software; you can redistribute it and/or modify it
7  under the terms of the GNU General Public License as published by the
8  Free Software Foundation; version 3.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program; if not, write to the Free Software
17  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18 
19  To the extent this program is licensed as part of the Enterprise
20  versions of CFEngine, the applicable Commercial Open Source License
21  (COSL) may apply to this file if you as a licensee so wish it. See
22  included file COSL.txt.
23 */
24 
25 #include <processes_select.h>
26 
27 #include <string.h>
28 
29 #include <eval_context.h>
30 #include <files_names.h>
31 #include <conversion.h>
32 #include <matching.h>
33 #include <systype.h>
34 #include <string_lib.h> /* Chop */
35 #include <regex.h> /* CompileRegex,StringMatchWithPrecompiledRegex,StringMatchFull */
36 #include <item_lib.h>
37 #include <file_lib.h> // SetUmask(), RestoreUmask()
38 #include <pipes.h>
39 #include <files_interfaces.h>
40 #include <rlist.h>
41 #include <policy.h>
42 #include <zones.h>
43 #include <printsize.h>
44 #include <known_dirs.h>
45 
46 # ifdef HAVE_GETZONEID
47 #include <sequence.h>
48 #define MAX_ZONENAME_SIZE 64
49 # endif
50 
51 #ifdef _WIN32
52 #define TABLE_STORAGE
53 #else
54 #define TABLE_STORAGE static
55 #endif
57 
58 typedef enum
59 {
60  /*
61  * In this mode, all columns must be present by the occurrence of at least
62  * one non-whitespace character.
63  */
65  /*
66  * In this mode, if a process is a zombie, and if there is nothing but
67  * whitespace in the byte columns directly below a header, that column is
68  * skipped.
69  *
70  * This means that very little text shifting will be tolerated, preferably
71  * none, or small enough that all column entries still remain under their
72  * own header. It also means that a zombie process must be detectable by
73  * reading the 'Z' state directly below the 'S' or 'ST' header.
74  */
77 
78 /*
79  Ideally this should be autoconf-tested, but it's really hard to do so. See
80  SplitProcLine() to see the exact effects this has.
81 */
83 {
85  [PLATFORM_CONTEXT_OPENVZ] = PCA_AllColumnsPresent, /* virt_host_vz_vzps */
90  [PLATFORM_CONTEXT_SOLARIS] = PCA_AllColumnsPresent, /* solaris >= 11 */
105 };
106 
107 #if defined(__sun) || defined(TEST_UNIT_TEST)
108 static StringMap *UCB_PS_MAP = NULL;
109 // These will be tried in order.
110 static const char *UCB_STYLE_PS[] = {
111  "/usr/ucb/ps",
112  "/bin/ps",
113  NULL
114 };
115 static const char *UCB_STYLE_PS_ARGS = "axwww";
116 static const PsColumnAlgorithm UCB_STYLE_PS_COLUMN_ALGORITHM = PCA_ZombieSkipEmptyColumns;
117 #endif
118 
119 static bool SelectProcRangeMatch(char *name1, char *name2, int min, int max, char **names, char **line);
120 static bool SelectProcRegexMatch(const char *name1, const char *name2, const char *regex, bool anchored, char **colNames, char **line);
121 static bool SplitProcLine(const char *proc,
122  time_t pstime,
123  char **names,
124  int *start,
125  int *end,
126  PsColumnAlgorithm pca,
127  char **line);
128 static bool SelectProcTimeCounterRangeMatch(char *name1, char *name2, time_t min, time_t max, char **names, char **line);
129 static bool SelectProcTimeAbsRangeMatch(char *name1, char *name2, time_t min, time_t max, char **names, char **line);
130 static int GetProcColumnIndex(const char *name1, const char *name2, char **names);
131 static void GetProcessColumnNames(const char *proc, char **names, int *start, int *end);
132 static int ExtractPid(char *psentry, char **names, int *end);
133 static void ApplyPlatformExtraTable(char **names, char **columns);
134 
135 /***************************************************************************/
136 
137 static bool SelectProcess(const char *procentry,
138  time_t pstime,
139  char **names,
140  int *start,
141  int *end,
142  const char *process_regex,
143  const ProcessSelect *a,
144  bool attrselect)
145 {
146  bool result = true;
147  char *column[CF_PROCCOLS];
148  Rlist *rp;
149 
150  assert(process_regex);
151  assert(a != NULL);
152 
153  StringSet *process_select_attributes = StringSetNew();
154 
155  memset(column, 0, sizeof(column));
156 
157  if (!SplitProcLine(procentry, pstime, names, start, end,
159  {
160  result = false;
161  goto cleanup;
162  }
163 
164  ApplyPlatformExtraTable(names, column);
165 
166  for (int i = 0; names[i] != NULL; i++)
167  {
168  LogDebug(LOG_MOD_PS, "In SelectProcess, COL[%s] = '%s'",
169  names[i], column[i]);
170  }
171 
172  if (!SelectProcRegexMatch("CMD", "COMMAND", process_regex, false, names, column))
173  {
174  result = false;
175  goto cleanup;
176  }
177 
178  if (!attrselect)
179  {
180  // If we are not considering attributes, then the matching is done.
181  goto cleanup;
182  }
183 
184  for (rp = a->owner; rp != NULL; rp = rp->next)
185  {
186  if (rp->val.type == RVAL_TYPE_FNCALL)
187  {
189  "Function call '%s' in process_select body was not resolved, skipping",
190  RlistFnCallValue(rp)->name);
191  }
192  else if (SelectProcRegexMatch("USER", "UID", RlistScalarValue(rp), true, names, column))
193  {
194  StringSetAdd(process_select_attributes, xstrdup("process_owner"));
195  break;
196  }
197  }
198 
199  if (SelectProcRangeMatch("PID", "PID", a->min_pid, a->max_pid, names, column))
200  {
201  StringSetAdd(process_select_attributes, xstrdup("pid"));
202  }
203 
204  if (SelectProcRangeMatch("PPID", "PPID", a->min_ppid, a->max_ppid, names, column))
205  {
206  StringSetAdd(process_select_attributes, xstrdup("ppid"));
207  }
208 
209  if (SelectProcRangeMatch("PGID", "PGID", a->min_pgid, a->max_pgid, names, column))
210  {
211  StringSetAdd(process_select_attributes, xstrdup("pgid"));
212  }
213 
214  if (SelectProcRangeMatch("VSZ", "SZ", a->min_vsize, a->max_vsize, names, column))
215  {
216  StringSetAdd(process_select_attributes, xstrdup("vsize"));
217  }
218 
219  if (SelectProcRangeMatch("RSS", "RSS", a->min_rsize, a->max_rsize, names, column))
220  {
221  StringSetAdd(process_select_attributes, xstrdup("rsize"));
222  }
223 
224  if (SelectProcTimeCounterRangeMatch("TIME", "TIME", a->min_ttime, a->max_ttime, names, column))
225  {
226  StringSetAdd(process_select_attributes, xstrdup("ttime"));
227  }
228 
230  ("STIME", "START", a->min_stime, a->max_stime, names, column))
231  {
232  StringSetAdd(process_select_attributes, xstrdup("stime"));
233  }
234 
235  if (SelectProcRangeMatch("NI", "PRI", a->min_pri, a->max_pri, names, column))
236  {
237  StringSetAdd(process_select_attributes, xstrdup("priority"));
238  }
239 
240  if (SelectProcRangeMatch("NLWP", "NLWP", a->min_thread, a->max_thread, names, column))
241  {
242  StringSetAdd(process_select_attributes, xstrdup("threads"));
243  }
244 
245  if (SelectProcRegexMatch("S", "STAT", a->status, true, names, column))
246  {
247  StringSetAdd(process_select_attributes, xstrdup("status"));
248  }
249 
250  if (SelectProcRegexMatch("CMD", "COMMAND", a->command, true, names, column))
251  {
252  StringSetAdd(process_select_attributes, xstrdup("command"));
253  }
254 
255  if (SelectProcRegexMatch("TTY", "TTY", a->tty, true, names, column))
256  {
257  StringSetAdd(process_select_attributes, xstrdup("tty"));
258  }
259 
260  if (!a->process_result)
261  {
262  if (StringSetSize(process_select_attributes) == 0)
263  {
264  result = EvalProcessResult("", process_select_attributes);
265  }
266  else
267  {
268  Writer *w = StringWriter();
269  StringSetIterator iter = StringSetIteratorInit(process_select_attributes);
270  char *attr = StringSetIteratorNext(&iter);
271  WriterWrite(w, attr);
272 
273  while ((attr = StringSetIteratorNext(&iter)))
274  {
275  WriterWriteChar(w, '.');
276  WriterWrite(w, attr);
277  }
278 
279  result = EvalProcessResult(StringWriterData(w), process_select_attributes);
280  WriterClose(w);
281  }
282  }
283  else
284  {
285  result = EvalProcessResult(a->process_result, process_select_attributes);
286  }
287 
288 cleanup:
289  StringSetDestroy(process_select_attributes);
290 
291  for (int i = 0; column[i] != NULL; i++)
292  {
293  free(column[i]);
294  }
295 
296  return result;
297 }
298 
299 Item *SelectProcesses(const char *process_name, const ProcessSelect *a, bool attrselect)
300 {
301  assert(a != NULL);
302  const Item *processes = PROCESSTABLE;
303  Item *result = NULL;
304 
305  if (processes == NULL)
306  {
307  return result;
308  }
309 
310  char *names[CF_PROCCOLS];
311  int start[CF_PROCCOLS];
312  int end[CF_PROCCOLS];
313 
314  GetProcessColumnNames(processes->name, names, start, end);
315 
316  /* TODO: use actual time of ps-run, as time(NULL) may be later. */
317  time_t pstime = time(NULL);
318 
319  for (Item *ip = processes->next; ip != NULL; ip = ip->next)
320  {
321  if (NULL_OR_EMPTY(ip->name))
322  {
323  continue;
324  }
325 
326  if (!SelectProcess(ip->name, pstime, names, start, end, process_name, a, attrselect))
327  {
328  continue;
329  }
330 
331  pid_t pid = ExtractPid(ip->name, names, end);
332 
333  if (pid == -1)
334  {
335  Log(LOG_LEVEL_VERBOSE, "Unable to extract pid while looking for %s", process_name);
336  continue;
337  }
338 
339  PrependItem(&result, ip->name, "");
340  result->counter = (int)pid;
341  }
342 
343  for (int i = 0; i < CF_PROCCOLS; i++)
344  {
345  free(names[i]);
346  }
347 
348  return result;
349 }
350 
351 static bool SelectProcRangeMatch(char *name1, char *name2, int min, int max, char **names, char **line)
352 {
353  int i;
354  long value;
355 
356  if ((min == CF_NOINT) || (max == CF_NOINT))
357  {
358  return false;
359  }
360 
361  if ((i = GetProcColumnIndex(name1, name2, names)) != -1)
362  {
363  value = IntFromString(line[i]);
364 
365  if (value == CF_NOINT)
366  {
367  Log(LOG_LEVEL_INFO, "Failed to extract a valid integer from '%s' => '%s' in process list", names[i],
368  line[i]);
369  return false;
370  }
371 
372  if ((min <= value) && (value <= max))
373  {
374  return true;
375  }
376  else
377  {
378  return false;
379  }
380  }
381 
382  return false;
383 }
384 
385 /***************************************************************************/
386 
387 static long TimeCounter2Int(const char *s)
388 {
389  long days, hours, minutes, seconds;
390 
391  if (s == NULL)
392  {
393  return CF_NOINT;
394  }
395 
396  /* If we match dd-hh:mm[:ss], believe it: */
397  int got = sscanf(s, "%ld-%ld:%ld:%ld", &days, &hours, &minutes, &seconds);
398  if (got > 2)
399  {
400  /* All but perhaps seconds set */
401  }
402  /* Failing that, try matching hh:mm[:ss] */
403  else if (1 < (got = sscanf(s, "%ld:%ld:%ld", &hours, &minutes, &seconds)))
404  {
405  /* All but days and perhaps seconds set */
406  days = 0;
407  got++;
408  }
409  else
410  {
412  "Unable to parse 'ps' time field as [dd-]hh:mm[:ss], got '%s'",
413  s);
414  return CF_NOINT;
415  }
416  assert(got > 2); /* i.e. all but maybe seconds have been set */
417  /* Clear seconds if unset: */
418  if (got < 4)
419  {
420  seconds = 0;
421  }
422 
423  LogDebug(LOG_MOD_PS, "TimeCounter2Int:"
424  " Parsed '%s' as elapsed time '%ld-%02ld:%02ld:%02ld'",
425  s, days, hours, minutes, seconds);
426 
427  /* Convert to seconds: */
428  return ((days * 24 + hours) * 60 + minutes) * 60 + seconds;
429 }
430 
431 static bool SelectProcTimeCounterRangeMatch(char *name1, char *name2, time_t min, time_t max, char **names, char **line)
432 {
433  if ((min == CF_NOINT) || (max == CF_NOINT))
434  {
435  return false;
436  }
437 
438  int i = GetProcColumnIndex(name1, name2, names);
439  if (i != -1)
440  {
441  time_t value = (time_t) TimeCounter2Int(line[i]);
442 
443  if (value == CF_NOINT)
444  {
446  "Failed to extract a valid integer from %s => '%s' in process list",
447  names[i], line[i]);
448  return false;
449  }
450 
451  if ((min <= value) && (value <= max))
452  {
453  Log(LOG_LEVEL_VERBOSE, "Selection filter matched counter range"
454  " '%s/%s' = '%s' in [%jd,%jd] (= %jd secs)",
455  name1, name2, line[i], (intmax_t)min, (intmax_t)max, (intmax_t)value);
456  return true;
457  }
458  else
459  {
460  LogDebug(LOG_MOD_PS, "Selection filter REJECTED counter range"
461  " '%s/%s' = '%s' in [%jd,%jd] (= %jd secs)",
462  name1, name2, line[i],
463  (intmax_t) min, (intmax_t) max, (intmax_t) value);
464  return false;
465  }
466  }
467 
468  return false;
469 }
470 
471 static time_t TimeAbs2Int(const char *s)
472 {
473  if (s == NULL)
474  {
475  return CF_NOINT;
476  }
477 
478  struct tm tm;
479  localtime_r(&CFSTARTTIME, &tm);
480  tm.tm_sec = 0;
481  tm.tm_isdst = -1;
482 
483  /* Try various ways to parse s: */
484  char word[4]; /* Abbreviated month name */
485  long ns[3]; /* Miscellaneous numbers, diverse readings */
486  int got = sscanf(s, "%2ld:%2ld:%2ld", ns, ns + 1, ns + 2);
487  if (1 < got) /* Hr:Min[:Sec] */
488  {
489  tm.tm_hour = ns[0];
490  tm.tm_min = ns[1];
491  if (got == 3)
492  {
493  tm.tm_sec = ns[2];
494  }
495  }
496  /* or MMM dd (the %ld shall ignore any leading space) */
497  else if (sscanf(s, "%3[a-zA-Z]%ld", word, ns) == 2 &&
498  /* Only match if word is a valid month text: */
499  0 < (ns[1] = Month2Int(word)))
500  {
501  int month = ns[1] - 1;
502  if (tm.tm_mon < month)
503  {
504  /* Wrapped around */
505  tm.tm_year--;
506  }
507  tm.tm_mon = month;
508  tm.tm_mday = ns[0];
509  tm.tm_hour = 0;
510  tm.tm_min = 0;
511  }
512  /* or just year, or seconds since 1970 */
513  else if (sscanf(s, "%ld", ns) == 1)
514  {
515  if (ns[0] > 9999)
516  {
517  /* Seconds since 1970.
518  *
519  * This is the amended value SplitProcLine() replaces
520  * start time with if it's imprecise and a better value
521  * can be calculated from elapsed time.
522  */
523  return (time_t)ns[0];
524  }
525  /* else year, at most four digits; either 4-digit CE or
526  * already relative to 1900. */
527 
528  memset(&tm, 0, sizeof(tm));
529  tm.tm_year = ns[0] < 999 ? ns[0] : ns[0] - 1900;
530  tm.tm_isdst = -1;
531  }
532  else
533  {
534  return CF_NOINT;
535  }
536 
537  return mktime(&tm);
538 }
539 
540 static bool SelectProcTimeAbsRangeMatch(char *name1, char *name2, time_t min, time_t max, char **names, char **line)
541 {
542  int i;
543  time_t value;
544 
545  if ((min == CF_NOINT) || (max == CF_NOINT))
546  {
547  return false;
548  }
549 
550  if ((i = GetProcColumnIndex(name1, name2, names)) != -1)
551  {
552  value = TimeAbs2Int(line[i]);
553 
554  if (value == CF_NOINT)
555  {
556  Log(LOG_LEVEL_INFO, "Failed to extract a valid integer from %c => '%s' in process list", name1[i],
557  line[i]);
558  return false;
559  }
560 
561  if ((min <= value) && (value <= max))
562  {
563  Log(LOG_LEVEL_VERBOSE, "Selection filter matched absolute '%s/%s' = '%s(%jd)' in [%jd,%jd]", name1, name2, line[i], (intmax_t)value,
564  (intmax_t)min, (intmax_t)max);
565  return true;
566  }
567  else
568  {
569  return false;
570  }
571  }
572 
573  return false;
574 }
575 
576 /***************************************************************************/
577 
578 static bool SelectProcRegexMatch(const char *name1, const char *name2,
579  const char *regex, bool anchored,
580  char **colNames, char **line)
581 {
582  int i;
583 
584  if (regex == NULL)
585  {
586  return false;
587  }
588 
589  if ((i = GetProcColumnIndex(name1, name2, colNames)) != -1)
590  {
591  if (anchored)
592  {
593  return StringMatchFull(regex, line[i]);
594  }
595  else
596  {
597  int s, e;
598  return StringMatch(regex, line[i], &s, &e);
599  }
600  }
601 
602  return false;
603 }
604 
605 static void PrintStringIndexLine(int prefix_spaces, int len)
606 {
607  char arrow_str[CF_BUFSIZE];
608  arrow_str[0] = '^';
609  arrow_str[1] = '\0';
610  char index_str[CF_BUFSIZE];
611  index_str[0] = '0';
612  index_str[1] = '\0';
613  for (int lineindex = 10; lineindex <= len; lineindex += 10)
614  {
615  char num[PRINTSIZE(lineindex)];
616  xsnprintf(num, sizeof(num), "%10d", lineindex);
617  strlcat(index_str, num, sizeof(index_str));
618  strlcat(arrow_str, " ^", sizeof(arrow_str));
619  }
620 
621  // Prefix the beginning of the indexes with the given number.
622  LogDebug(LOG_MOD_PS, "%*s%s", prefix_spaces, "", arrow_str);
623  LogDebug(LOG_MOD_PS, "%*s%s", prefix_spaces, "Index: ", index_str);
624 }
625 
626 static void MaybeFixStartTime(const char *line,
627  time_t pstime,
628  char **names,
629  char **fields)
630 {
631  /* Since start times can be very imprecise (e.g. just a past day's
632  * date, or a past year), calculate a better value from elapsed
633  * time, if available: */
634  int k = GetProcColumnIndex("ELAPSED", "ELAPSED", names);
635  if (k != -1)
636  {
637  const long elapsed = TimeCounter2Int(fields[k]);
638  if (elapsed != CF_NOINT) /* Only use if parsed successfully ! */
639  {
640  int j = GetProcColumnIndex("STIME", "START", names), ns[3];
641  /* Trust the reported value if it matches hh:mm[:ss], though: */
642  if (sscanf(fields[j], "%d:%d:%d", ns, ns + 1, ns + 2) < 2)
643  {
644  time_t value = pstime - (time_t) elapsed;
645 
647  "processes: Replacing parsed start time %s with %s",
648  fields[j], ctime(&value));
649 
650  free(fields[j]);
651  xasprintf(fields + j, "%ld", value);
652  }
653  }
654  else if (fields[k])
655  {
657  "Parsing problem was in ELAPSED field of '%s'",
658  line);
659  }
660  }
661 }
662 
663 /*******************************************************************/
664 /* fields must be char *fields[CF_PROCCOLS] in fact. */
665 /* pstime should be the time at which ps was run. */
666 
667 static bool SplitProcLine(const char *line,
668  time_t pstime,
669  char **names,
670  int *start,
671  int *end,
672  PsColumnAlgorithm pca,
673  char **fields)
674 {
675  if (line == NULL || line[0] == '\0')
676  {
677  return false;
678  }
679 
680  size_t linelen = strlen(line);
681 
683  {
684  LogDebug(LOG_MOD_PS, "Parsing ps line: '%s'", line);
685  // Makes the entry line up with the line above.
686  PrintStringIndexLine(18, linelen);
687  }
688 
689  /*
690  All platforms have been verified to not produce overlapping fields with
691  currently used ps tools, and hence we can parse based on space separation
692  (with some caveats, see below).
693 
694  Dates may have spaces in them, like "May 4", or not, like "May4". Prefer
695  to match a date without spaces as long as it contains a number, but fall
696  back to parsing letters followed by space(s) and a date.
697 
698  Commands will also have extra spaces, but it is always the last field, so
699  we just include spaces at this point in the parsing.
700 
701  An additional complication is that some platforms (only AIX is known at
702  the time of writing) can have empty columns when a process is a
703  zombie. The plan is to match for this by checking the range between start
704  and end directly below the header (same byte position). If the whole range
705  is whitespace we consider the entry missing. The columns are (presumably)
706  guaranteed to line up correctly for this, since zombie processes do not
707  have memory usage which can produce large, potentially alignment-altering
708  numbers. However, we cannot do this whitespace check in general, because
709  non-zombie processes may shift columns in a way that leaves some columns
710  apparently (but not actually) empty. Zombie processes have state Z and
711  command <defunct> on AIX. Similarly processes marked with command
712  <exiting> also have missing columns and need to be skipped. (AIX only).
713 
714  Take these two examples:
715 
716 AIX:
717  USER PID PPID PGID %CPU %MEM VSZ NI S STIME TIME COMMAND
718  root 1 0 0 0.0 0.0 784 20 A Nov 28 00:00:00 /etc/init
719  root 1835344 1 1835344 0.0 0.0 944 20 A Nov 28 00:00:00 /usr/lib/errdemon
720  root 2097594 1 1638802 0.0 0.0 596 20 A Nov 28 00:00:05 /usr/sbin/syncd 60
721  root 3408328 1 3408328 0.0 0.0 888 20 A Nov 28 00:00:00 /usr/sbin/srcmstr
722  root 4325852 3408328 4325852 0.0 0.0 728 20 A Nov 28 00:00:00 /usr/sbin/syslogd
723  root 4784534 3408328 4784534 0.0 0.0 1212 20 A Nov 28 00:00:00 sendmail: accepting connections
724  root 5898690 1 5898690 0.0 0.0 1040 20 A Nov 28 00:00:00 /usr/sbin/cron
725  6095244 8913268 8913268 20 Z 00:00:00 <defunct>
726  root 6160866 3408328 6160866 0.0 0.0 1612 20 A Nov 28 00:00:00 /opt/rsct/bin/IBM.ServiceRMd
727  6750680 17826152 17826152 20 Z 00:00:00 <defunct>
728  root 7143692 3408328 7143692 0.0 0.0 476 20 A Nov 28 00:00:00 /var/perf/pm/bin/pmperfrec
729  root 7340384 8651136 8651136 0.0 0.0 500 20 A Nov 28 00:00:00 [trspoolm]
730  root 7602560 8978714 7602560 0.0 0.0 636 20 A Nov 28 00:00:00 sshd: u0013628 [priv]
731  7733720 - - - A - <exiting>
732 
733 Solaris 9:
734  USER PID %CPU %MEM SZ RSS TT S STIME TIME COMMAND
735  jenkins 29769 0.0 0.0 810 2976 pts/1 S 07:22:43 0:00 /usr/bin/perl ../../ps.pl
736  jenkins 29835 - - 0 0 ? Z - 0:00 <defunct>
737  jenkins 10026 0.0 0.3 30927 143632 ? S Jan_21 01:18:58 /usr/jdk/jdk1.6.0_45/bin/java -jar slave.jar
738 
739  Due to how the process state 'S' is shifted under the 'S' header in the
740  second example, it is not possible to separate between this and a missing
741  column. Counting spaces is no good, because commands can contain an
742  arbitrary number of spaces, and there is no way to know the byte position
743  where a command begins. Hence the only way is to base this algorithm on
744  platform and only do the "empty column detection" when:
745  * The platform is known to produce empty columns for zombie processes
746  (see PCA_ZombieSkipEmptyColumns)
747  * The platform is known to not shift columns when the process is a
748  zombie.
749  * It is a zombie / exiting / idle process
750  (These states provide almost no useful info in ps output)
751  */
752 
753  bool skip = false;
754 
755  if (pca == PCA_ZombieSkipEmptyColumns)
756  {
757  // Find out if the process is a zombie.
758  for (int field = 0; names[field] && !skip; field++)
759  {
760  if (strcmp(names[field], "S") == 0 ||
761  strcmp(names[field], "ST") == 0)
762  {
763  // Check for zombie state.
764  for (int pos = start[field]; pos <= end[field] && pos < linelen && !skip; pos++)
765  {
766  // 'Z' letter with word boundary on each side.
767  if (isspace(line[pos - 1])
768  && line[pos] == 'Z'
769  && (isspace(line[pos + 1])
770  || line[pos + 1] == '\0'))
771  {
772  LogDebug(LOG_MOD_PS, "Detected zombie process, "
773  "skipping parsing of empty ps fields.");
774  skip = true;
775  }
776  }
777  }
778  else if (strcmp(names[field], "COMMAND") == 0)
779  {
780  // Check for exiting or idle state.
781  for (int pos = start[field]; pos <= end[field] && pos < linelen && !skip; pos++)
782  {
783  if (!isspace(line[pos])) // Skip spaces
784  {
785  if (strncmp(line + pos, "<exiting>", 9) == 0 ||
786  strncmp(line + pos, "<idle>", 6) == 0)
787  {
788  LogDebug(LOG_MOD_PS, "Detected exiting/idle process, "
789  "skipping parsing of empty ps fields.");
790  skip = true;
791  }
792  else
793  {
794  break;
795  }
796  }
797  }
798  }
799  }
800  }
801 
802  int field = 0;
803  int pos = 0;
804  while (names[field])
805  {
806  // Some sanity checks.
807  if (pos >= linelen)
808  {
809  if (pca == PCA_ZombieSkipEmptyColumns && skip)
810  {
811  LogDebug(LOG_MOD_PS, "Assuming '%s' field is empty, "
812  "since ps line '%s' is not long enough to reach under its "
813  "header.", names[field], line);
814  fields[field] = xstrdup("");
815  field++;
816  continue;
817  }
818  else
819  {
820  Log(LOG_LEVEL_ERR, "ps output line '%s' is shorter than its "
821  "associated header.", line);
822  return false;
823  }
824  }
825 
826  bool cmd = (strcmp(names[field], "CMD") == 0 ||
827  strcmp(names[field], "COMMAND") == 0);
828  bool stime = !cmd && (strcmp(names[field], "STIME") == 0);
829 
830  // Equal boolean results, either both must be true, or both must be
831  // false. IOW we must either both be at the last field, and it must be
832  // CMD, or none of those. |
833  // v
834  if ((names[field + 1] != NULL) == cmd)
835  {
836  Log(LOG_LEVEL_ERR, "Last field of ps output '%s' is not "
837  "CMD/COMMAND.", line);
838  return false;
839  }
840 
841  // If zombie/exiting, check if field is empty.
842  if (pca == PCA_ZombieSkipEmptyColumns && skip)
843  {
844  int empty_pos = start[field];
845  bool empty = true;
846  while (empty_pos <= end[field])
847  {
848  if (!isspace(line[empty_pos]))
849  {
850  empty = false;
851  break;
852  }
853  empty_pos++;
854  }
855  if (empty)
856  {
857  LogDebug(LOG_MOD_PS, "Detected empty"
858  " '%s' field between positions %d and %d",
859  names[field], start[field], end[field]);
860  fields[field] = xstrdup("");
861  pos = end[field] + 1;
862  field++;
863  continue;
864  }
865  else
866  {
867  LogDebug(LOG_MOD_PS, "Detected non-empty "
868  "'%s' field between positions %d and %d",
869  names[field], start[field], end[field]);
870  }
871  }
872 
873  // Preceding space.
874  while (isspace(line[pos]))
875  {
876  pos++;
877  }
878 
879  // Field.
880  int last = pos;
881  if (cmd)
882  {
883  // Last field, slurp up the rest, but discard trailing whitespace.
884  last = linelen;
885  while (last > pos && isspace(line[last - 1]))
886  {
887  last--;
888  }
889  }
890  else if (stime)
891  {
892  while (isalpha(line[last]))
893  {
894  last++;
895  }
896  if (isspace(line[last]))
897  {
898  // In this case we expect spaces followed by a number.
899  // It means what we first read was the month, now is the date.
900  do
901  {
902  last++;
903  } while (isspace(line[last]));
904  if (!isdigit(line[last]))
905  {
906  char fmt[200];
907  xsnprintf(fmt, sizeof(fmt), "Unable to parse STIME entry in ps "
908  "output line '%%s': Expected day number after "
909  "'%%.%ds'", (last - 1) - pos);
910  Log(LOG_LEVEL_ERR, fmt, line, line + pos);
911  return false;
912  }
913  }
914  while (line[last] && !isspace(line[last]))
915  {
916  last++;
917  }
918  }
919  else
920  {
921  // Generic fields
922  while (line[last] && !isspace(line[last]))
923  {
924  last++;
925  }
926  }
927 
928  // Make a copy and store in fields.
929  fields[field] = xstrndup(line + pos, last - pos);
930  LogDebug(LOG_MOD_PS, "'%s' field '%s'"
931  " extracted from between positions %d and %d",
932  names[field], fields[field], pos, last - 1);
933 
934  pos = last;
935  field++;
936  }
937 
938  MaybeFixStartTime(line, pstime, names, fields);
939 
940  return true;
941 }
942 
943 /*******************************************************************/
944 
945 static int GetProcColumnIndex(const char *name1, const char *name2, char **names)
946 {
947  for (int i = 0; names[i] != NULL; i++)
948  {
949  if (strcmp(names[i], name1) == 0 ||
950  strcmp(names[i], name2) == 0)
951  {
952  return i;
953  }
954  }
955 
956  LogDebug(LOG_MOD_PS, "Process column %s/%s"
957  " was not supported on this system",
958  name1, name2);
959 
960  return -1;
961 }
962 
963 /**********************************************************************************/
964 
965 bool IsProcessNameRunning(char *procNameRegex)
966 {
967  char *colHeaders[CF_PROCCOLS];
968  int start[CF_PROCCOLS] = { 0 };
969  int end[CF_PROCCOLS] = { 0 };
970  bool matched = false;
971  int i;
972 
973  memset(colHeaders, 0, sizeof(colHeaders));
974 
975  if (PROCESSTABLE == NULL)
976  {
977  Log(LOG_LEVEL_ERR, "IsProcessNameRunning: PROCESSTABLE is empty");
978  return false;
979  }
980  /* TODO: use actual time of ps-run, not time(NULL), which may be later. */
981  time_t pstime = time(NULL);
982 
983  GetProcessColumnNames(PROCESSTABLE->name, colHeaders, start, end);
984 
985  for (const Item *ip = PROCESSTABLE->next; !matched && ip != NULL; ip = ip->next) // iterate over ps lines
986  {
987  char *lineSplit[CF_PROCCOLS];
988  memset(lineSplit, 0, sizeof(lineSplit));
989 
990  if (NULL_OR_EMPTY(ip->name))
991  {
992  continue;
993  }
994 
995  if (!SplitProcLine(ip->name, pstime, colHeaders, start, end,
996  PS_COLUMN_ALGORITHM[VPSHARDCLASS], lineSplit))
997  {
998  Log(LOG_LEVEL_ERR, "IsProcessNameRunning: Could not split process line '%s'", ip->name);
999  goto loop_cleanup;
1000  }
1001 
1002  ApplyPlatformExtraTable(colHeaders, lineSplit);
1003 
1004  if (SelectProcRegexMatch("CMD", "COMMAND", procNameRegex, true, colHeaders, lineSplit))
1005  {
1006  matched = true;
1007  }
1008 
1009  loop_cleanup:
1010  for (i = 0; lineSplit[i] != NULL; i++)
1011  {
1012  free(lineSplit[i]);
1013  }
1014  }
1015 
1016  for (i = 0; colHeaders[i] != NULL; i++)
1017  {
1018  free(colHeaders[i]);
1019  }
1020 
1021  return matched;
1022 }
1023 
1024 
1025 static void GetProcessColumnNames(const char *proc, char **names, int *start, int *end)
1026 {
1027  char title[16];
1028  int col, offset = 0;
1029 
1031  {
1032  LogDebug(LOG_MOD_PS, "Parsing ps line: '%s'", proc);
1033  // Makes the entry line up with the line above.
1034  PrintStringIndexLine(18, strlen(proc));
1035  }
1036 
1037  for (col = 0; col < CF_PROCCOLS; col++)
1038  {
1039  start[col] = end[col] = -1;
1040  names[col] = NULL;
1041  }
1042 
1043  col = 0;
1044 
1045  for (const char *sp = proc; *sp != '\0'; sp++)
1046  {
1047  offset = sp - proc;
1048 
1049  if (isspace((unsigned char) *sp))
1050  {
1051  if (start[col] != -1)
1052  {
1053  LogDebug(LOG_MOD_PS, "End of '%s' is %d", title, offset - 1);
1054  end[col++] = offset - 1;
1055  if (col >= CF_PROCCOLS) /* No space for more columns. */
1056  {
1057  size_t blank = strspn(sp, " \t\r\n\f\v");
1058  if (sp[blank]) /* i.e. that wasn't everything. */
1059  {
1060  /* If this happens, we have more columns in
1061  * our ps output than space to store them.
1062  * Update the #define CF_PROCCOLS (last seen
1063  * in libpromises/cf3.defs.h) to a bigger
1064  * number ! */
1066  "Process table lacks space for last columns: %s",
1067  sp + blank);
1068  }
1069  break;
1070  }
1071  }
1072  }
1073  else if (start[col] == -1)
1074  {
1075  if (col == 0)
1076  {
1077  // The first column always extends all the way to the left.
1078  start[col] = 0;
1079  }
1080  else
1081  {
1082  start[col] = offset;
1083  }
1084 
1085  if (sscanf(sp, "%15s", title) == 1)
1086  {
1087  LogDebug(LOG_MOD_PS, "Start of '%s' is at offset: %d",
1088  title, offset);
1089  LogDebug(LOG_MOD_PS, "Col[%d] = '%s'", col, title);
1090 
1091  names[col] = xstrdup(title);
1092  }
1093  }
1094  }
1095 
1096  if (end[col] == -1)
1097  {
1098  LogDebug(LOG_MOD_PS, "End of '%s' is %d", title, offset);
1099  end[col] = offset;
1100  }
1101 }
1102 
1103 #ifndef _WIN32
1104 static const char *GetProcessOptions(void)
1105 {
1106 
1107 # ifdef __linux__
1108  if (strncmp(VSYSNAME.release, "2.4", 3) == 0)
1109  {
1110  // No threads on 2.4 kernels, so omit nlwp
1111  return "-eo user,pid,ppid,pgid,pcpu,pmem,vsz,ni,rss:9,stime,etime,time,args";
1112  }
1113 # endif
1114 
1115  return VPSOPTS[VPSHARDCLASS];
1116 }
1117 #endif
1118 
1119 static int ExtractPid(char *psentry, char **names, int *end)
1120 {
1121  int offset = 0;
1122 
1123  for (int col = 0; col < CF_PROCCOLS; col++)
1124  {
1125  if (strcmp(names[col], "PID") == 0)
1126  {
1127  if (col > 0)
1128  {
1129  offset = end[col - 1];
1130  }
1131  break;
1132  }
1133  }
1134 
1135  for (const char *sp = psentry + offset; *sp != '\0'; sp++) /* if first field contains alpha, skip */
1136  {
1137  /* If start with alphanum then skip it till the first space */
1138 
1139  if (isalnum((unsigned char) *sp))
1140  {
1141  while (*sp != ' ' && *sp != '\0')
1142  {
1143  sp++;
1144  }
1145  }
1146 
1147  while (*sp == ' ' || *sp == '\t')
1148  {
1149  sp++;
1150  }
1151 
1152  int pid;
1153  if (sscanf(sp, "%d", &pid) == 1 && pid != -1)
1154  {
1155  return pid;
1156  }
1157  }
1158 
1159  return -1;
1160 }
1161 
1162 # ifndef _WIN32
1163 # ifdef HAVE_GETZONEID
1164 /* ListLookup with the following return semantics
1165  * -1 if the first argument is smaller than the second
1166  * 0 if the arguments are equal
1167  * 1 if the first argument is bigger than the second
1168  */
1169 int PidListCompare(const void *pid1, const void *pid2, ARG_UNUSED void *user_data)
1170 {
1171  int p1 = (intptr_t)(void *)pid1;
1172  int p2 = (intptr_t)(void *)pid2;
1173 
1174  if (p1 < p2)
1175  {
1176  return -1;
1177  }
1178  else if (p1 > p2)
1179  {
1180  return 1;
1181  }
1182  return 0;
1183 }
1184 /* Load processes using zone-aware ps
1185  * to obtain solaris list of global
1186  * process ids for root and non-root
1187  * users to lookup later */
1188 int ZLoadProcesstable(Seq *pidlist, Seq *rootpidlist)
1189 {
1190 
1191  char *names[CF_PROCCOLS];
1192  int start[CF_PROCCOLS];
1193  int end[CF_PROCCOLS];
1194 
1195  const char *pscmd = "/usr/bin/ps -Aleo zone,user,pid";
1196 
1197  FILE *psf = cf_popen(pscmd, "r", false);
1198  if (psf == NULL)
1199  {
1200  Log(LOG_LEVEL_ERR, "ZLoadProcesstable: Couldn't open the process list with command %s.", pscmd);
1201  return false;
1202  }
1203 
1204  size_t pbuff_size = CF_BUFSIZE;
1205  char *pbuff = xmalloc(pbuff_size);
1206  bool header = true;
1207 
1208  while (true)
1209  {
1210  ssize_t res = CfReadLine(&pbuff, &pbuff_size, psf);
1211  if (res == -1)
1212  {
1213  if (!feof(psf))
1214  {
1215  Log(LOG_LEVEL_ERR, "IsGlobalProcess(char **, int): Unable to read process list with command '%s'. (fread: %s)", pscmd, GetErrorStr());
1216  cf_pclose(psf);
1217  free(pbuff);
1218  return false;
1219  }
1220  else
1221  {
1222  break;
1223  }
1224  }
1225  Chop(pbuff, pbuff_size);
1226  if (header) /* This line is the header. */
1227  {
1228  GetProcessColumnNames(pbuff, &names[0], start, end);
1229  }
1230  else
1231  {
1232  int pid = ExtractPid(pbuff, &names[0], end);
1233 
1234  size_t zone_offset = strspn(pbuff, " ");
1235  size_t zone_end_offset = strcspn(pbuff + zone_offset, " ") + zone_offset;
1236  size_t user_offset = strspn(pbuff + zone_end_offset, " ") + zone_end_offset;
1237  size_t user_end_offset = strcspn(pbuff + user_offset, " ") + user_offset;
1238  bool is_global = (zone_end_offset - zone_offset == 6
1239  && strncmp(pbuff + zone_offset, "global", 6) == 0);
1240  bool is_root = (user_end_offset - user_offset == 4
1241  && strncmp(pbuff + user_offset, "root", 4) == 0);
1242 
1243  if (is_global && is_root)
1244  {
1245  SeqAppend(rootpidlist, (void*)(intptr_t)pid);
1246  }
1247  else if (is_global && !is_root)
1248  {
1249  SeqAppend(pidlist, (void*)(intptr_t)pid);
1250  }
1251  }
1252 
1253  header = false;
1254  }
1255  cf_pclose(psf);
1256  free(pbuff);
1257  return true;
1258 }
1259 bool PidInSeq(Seq *list, int pid)
1260 {
1261  void *res = SeqLookup(list, (void *)(intptr_t)pid, PidListCompare);
1262  int result = (intptr_t)(void*)res;
1263 
1264  if (result == pid)
1265  {
1266  return true;
1267  }
1268  return false;
1269 }
1270 /* return true if the process with
1271  * pid is in the global zone */
1272 int IsGlobalProcess(int pid, Seq *pidlist, Seq *rootpidlist)
1273 {
1274  if (PidInSeq(pidlist, pid) || PidInSeq(rootpidlist, pid))
1275  {
1276  return true;
1277  }
1278  else
1279  {
1280  return false;
1281  }
1282 }
1283 void ZCopyProcessList(Item **dest, const Item *source, Seq *pidlist, char **names, int *end)
1284 {
1285  int gpid = ExtractPid(source->name, names, end);
1286 
1287  if (PidInSeq(pidlist, gpid))
1288  {
1289  PrependItem(dest, source->name, "");
1290  }
1291 }
1292 # endif /* HAVE_GETZONEID */
1293 
1294 static void CheckPsLineLimitations(void)
1295 {
1296 #ifdef __hpux
1297  FILE *ps_fd;
1298  int ret;
1299  char limit[21];
1300  char *buf = NULL;
1301  size_t bufsize = 0;
1302 
1303  ps_fd = safe_fopen("/etc/default/ps", "r");
1304  if (!ps_fd)
1305  {
1306  Log(LOG_LEVEL_VERBOSE, "Could not open '/etc/default/ps' "
1307  "to check ps line length limitations.");
1308  return;
1309  }
1310 
1311  while (true)
1312  {
1313  ret = CfReadLine(&buf, &bufsize, ps_fd);
1314  if (ret < 0)
1315  {
1316  break;
1317  }
1318 
1319  ret = sscanf(buf, "DEFAULT_CMD_LINE_WIDTH = %20[0-9]", limit);
1320 
1321  if (ret == 1)
1322  {
1323  if (atoi(limit) < 1024)
1324  {
1325  Log(LOG_LEVEL_VERBOSE, "ps line length limit is less than 1024. "
1326  "Consider adjusting the DEFAULT_CMD_LINE_WIDTH setting in /etc/default/ps "
1327  "in order to guarantee correct process matching.");
1328  }
1329  break;
1330  }
1331  }
1332 
1333  free(buf);
1334  fclose(ps_fd);
1335 #endif // __hpux
1336 }
1337 #endif // _WIN32
1338 
1339 const char *GetProcessTableLegend(void)
1340 {
1341  if (PROCESSTABLE)
1342  {
1343  // First entry in the table is legend.
1344  return PROCESSTABLE->name;
1345  }
1346  else
1347  {
1348  return "<Process table not loaded>";
1349  }
1350 }
1351 
1352 #if defined(__sun) || defined(TEST_UNIT_TEST)
1353 static FILE *OpenUcbPsPipe(void)
1354 {
1355  for (int i = 0; UCB_STYLE_PS[i]; i++)
1356  {
1357  struct stat statbuf;
1358  if (stat(UCB_STYLE_PS[i], &statbuf) < 0)
1359  {
1361  "%s not found, cannot be used for extra process information",
1362  UCB_STYLE_PS[i]);
1363  continue;
1364  }
1365  if (!(statbuf.st_mode & 0111))
1366  {
1368  "%s not executable, cannot be used for extra process information",
1369  UCB_STYLE_PS[i]);
1370  continue;
1371  }
1372 
1373  char *ps_cmd;
1374  xasprintf(&ps_cmd, "%s %s", UCB_STYLE_PS[i],
1375  UCB_STYLE_PS_ARGS);
1376 
1377  FILE *cmd = cf_popen(ps_cmd, "rt", false);
1378  if (!cmd)
1379  {
1380  Log(LOG_LEVEL_WARNING, "Could not execute \"%s\", extra process "
1381  "information not available. "
1382  "Process command line length may be limited to 80 characters.",
1383  ps_cmd);
1384  }
1385 
1386  free(ps_cmd);
1387 
1388  return cmd;
1389  }
1390 
1391  Log(LOG_LEVEL_VERBOSE, "No eligible tool for extra process information "
1392  "found. Skipping.");
1393 
1394  return NULL;
1395 }
1396 
1397 static void ReadFromUcbPsPipe(FILE *cmd)
1398 {
1399  char *names[CF_PROCCOLS];
1400  memset(names, 0, sizeof(names));
1401  int start[CF_PROCCOLS];
1402  int end[CF_PROCCOLS];
1403  char *line = NULL;
1404  size_t linesize = 0;
1405  bool header = true;
1406  time_t pstime = time(NULL);
1407  int pidcol = -1;
1408  int cmdcol = -1;
1409  while (CfReadLine(&line, &linesize, cmd) > 0)
1410  {
1411  if (header)
1412  {
1413  GetProcessColumnNames(line, names, start, end);
1414 
1415  for (int i = 0; names[i]; i++)
1416  {
1417  if (strcmp(names[i], "PID") == 0)
1418  {
1419  pidcol = i;
1420  }
1421  else if (strcmp(names[i], "COMMAND") == 0
1422  || strcmp(names[i], "CMD") == 0)
1423  {
1424  cmdcol = i;
1425  }
1426  }
1427 
1428  if (pidcol < 0 || cmdcol < 0)
1429  {
1431  "Could not find PID and/or CMD/COMMAND column in "
1432  "ps output: \"%s\"", line);
1433  break;
1434  }
1435 
1436  header = false;
1437  continue;
1438  }
1439 
1440 
1441  char *columns[CF_PROCCOLS];
1442  memset(columns, 0, sizeof(columns));
1443  if (!SplitProcLine(line, pstime, names, start, end,
1444  UCB_STYLE_PS_COLUMN_ALGORITHM, columns))
1445  {
1447  "Not able to parse ps output: \"%s\"", line);
1448  }
1449 
1450  StringMapInsert(UCB_PS_MAP, columns[pidcol], columns[cmdcol]);
1451  // We avoid strdup'ing these strings by claiming ownership here.
1452  columns[pidcol] = NULL;
1453  columns[cmdcol] = NULL;
1454 
1455  for (int i = 0; i < CF_PROCCOLS; i++)
1456  {
1457  // There may be some null entries here, but since we "steal"
1458  // strings in the section above, we may have set some of them to
1459  // NULL and there may be following non-NULL fields.
1460  free(columns[i]);
1461  }
1462  }
1463 
1464  if (!feof(cmd) && ferror(cmd))
1465  {
1466  Log(LOG_LEVEL_ERR, "Error while reading output from ps: %s",
1467  GetErrorStr());
1468  }
1469 
1470  for (int i = 0; names[i] && i < CF_PROCCOLS; i++)
1471  {
1472  free(names[i]);
1473  }
1474 
1475  free(line);
1476 }
1477 
1478 static void ClearPlatformExtraTable(void)
1479 {
1480  if (UCB_PS_MAP)
1481  {
1482  StringMapDestroy(UCB_PS_MAP);
1483  UCB_PS_MAP = NULL;
1484  }
1485 }
1486 
1487 static void LoadPlatformExtraTable(void)
1488 {
1489  if (UCB_PS_MAP)
1490  {
1491  return;
1492  }
1493 
1494  UCB_PS_MAP = StringMapNew();
1495 
1496  FILE *cmd = OpenUcbPsPipe();
1497  if (!cmd)
1498  {
1499  return;
1500  }
1501  ReadFromUcbPsPipe(cmd);
1502  if (cf_pclose(cmd) != 0)
1503  {
1504  Log(LOG_LEVEL_WARNING, "Command returned non-zero while gathering "
1505  "extra process information.");
1506  // Make an empty map, in this case. The information can't be trusted.
1507  StringMapClear(UCB_PS_MAP);
1508  }
1509 }
1510 
1511 static void ApplyPlatformExtraTable(char **names, char **columns)
1512 {
1513  int pidcol = -1;
1514 
1515  for (int i = 0; names[i] && columns[i]; i++)
1516  {
1517  if (strcmp(names[i], "PID") == 0)
1518  {
1519  pidcol = i;
1520  break;
1521  }
1522  }
1523 
1524  if (pidcol == -1 || !StringMapHasKey(UCB_PS_MAP, columns[pidcol]))
1525  {
1526  return;
1527  }
1528 
1529  for (int i = 0; names[i] && columns[i]; i++)
1530  {
1531  if (strcmp(names[i], "COMMAND") == 0 || strcmp(names[i], "CMD") == 0)
1532  {
1533  free(columns[i]);
1534  columns[i] = xstrdup(StringMapGet(UCB_PS_MAP, columns[pidcol]));
1535  break;
1536  }
1537  }
1538 }
1539 
1540 #else
1541 static inline void LoadPlatformExtraTable(void)
1542 {
1543 }
1544 
1545 static inline void ClearPlatformExtraTable(void)
1546 {
1547 }
1548 
1549 static inline void ApplyPlatformExtraTable(ARG_UNUSED char **names, ARG_UNUSED char **columns)
1550 {
1551 }
1552 #endif
1553 
1554 #ifndef _WIN32
1556 {
1557  FILE *prp;
1558  char pscomm[CF_MAXLINKSIZE];
1559  Item *rootprocs = NULL;
1560  Item *otherprocs = NULL;
1561 
1562 
1563  if (PROCESSTABLE)
1564  {
1565  Log(LOG_LEVEL_VERBOSE, "Reusing cached process table");
1566  return true;
1567  }
1568 
1570 
1572 
1573  const char *psopts = GetProcessOptions();
1574 
1575  snprintf(pscomm, CF_MAXLINKSIZE, "%s %s", VPSCOMM[VPSHARDCLASS], psopts);
1576 
1577  Log(LOG_LEVEL_VERBOSE, "Observe process table with %s", pscomm);
1578 
1579  if ((prp = cf_popen(pscomm, "r", false)) == NULL)
1580  {
1581  Log(LOG_LEVEL_ERR, "Couldn't open the process list with command '%s'. (popen: %s)", pscomm, GetErrorStr());
1582  return false;
1583  }
1584 
1585  size_t vbuff_size = CF_BUFSIZE;
1586  char *vbuff = xmalloc(vbuff_size);
1587 
1588 # ifdef HAVE_GETZONEID
1589 
1590  char *names[CF_PROCCOLS];
1591  int start[CF_PROCCOLS];
1592  int end[CF_PROCCOLS];
1593  Seq *pidlist = SeqNew(1, NULL);
1594  Seq *rootpidlist = SeqNew(1, NULL);
1595  bool global_zone = IsGlobalZone();
1596 
1597  if (global_zone)
1598  {
1599  int res = ZLoadProcesstable(pidlist, rootpidlist);
1600 
1601  if (res == false)
1602  {
1603  Log(LOG_LEVEL_ERR, "Unable to load solaris zone process table.");
1604  return false;
1605  }
1606  }
1607 
1608 # endif
1609 
1610  ARG_UNUSED bool header = true; /* used only if HAVE_GETZONEID */
1611 
1612  for (;;)
1613  {
1614  ssize_t res = CfReadLine(&vbuff, &vbuff_size, prp);
1615  if (res == -1)
1616  {
1617  if (!feof(prp))
1618  {
1619  Log(LOG_LEVEL_ERR, "Unable to read process list with command '%s'. (fread: %s)", pscomm, GetErrorStr());
1620  cf_pclose(prp);
1621  free(vbuff);
1622  return false;
1623  }
1624  else
1625  {
1626  break;
1627  }
1628  }
1629  Chop(vbuff, vbuff_size);
1630 
1631 # ifdef HAVE_GETZONEID
1632 
1633  if (global_zone)
1634  {
1635  if (header)
1636  { /* this is the banner so get the column header names for later use*/
1637  GetProcessColumnNames(vbuff, &names[0], start, end);
1638  }
1639  else
1640  {
1641  int gpid = ExtractPid(vbuff, names, end);
1642 
1643  if (!IsGlobalProcess(gpid, pidlist, rootpidlist))
1644  {
1645  continue;
1646  }
1647  }
1648  }
1649 
1650 # endif
1651  AppendItem(&PROCESSTABLE, vbuff, "");
1652 
1653  header = false;
1654  }
1655 
1656  cf_pclose(prp);
1657 
1658 /* Now save the data */
1659  const char* const statedir = GetStateDir();
1660 
1661  snprintf(vbuff, CF_MAXVARSIZE, "%s%ccf_procs", statedir, FILE_SEPARATOR);
1662 
1664 
1665 # ifdef HAVE_GETZONEID
1666  if (global_zone) /* pidlist and rootpidlist are empty if we're not in the global zone */
1667  {
1668  Item *ip = PROCESSTABLE;
1669  while (ip != NULL)
1670  {
1671  ZCopyProcessList(&rootprocs, ip, rootpidlist, names, end);
1672  ip = ip->next;
1673  }
1674  ReverseItemList(rootprocs);
1675  ip = PROCESSTABLE;
1676  while (ip != NULL)
1677  {
1678  ZCopyProcessList(&otherprocs, ip, pidlist, names, end);
1679  ip = ip->next;
1680  }
1681  ReverseItemList(otherprocs);
1682  }
1683  else
1684 # endif
1685  {
1686  CopyList(&rootprocs, PROCESSTABLE);
1687  CopyList(&otherprocs, PROCESSTABLE);
1688 
1689  while (DeleteItemNotContaining(&rootprocs, "root"))
1690  {
1691  }
1692 
1693  while (DeleteItemContaining(&otherprocs, "root"))
1694  {
1695  }
1696  }
1697  if (otherprocs)
1698  {
1699  PrependItem(&rootprocs, otherprocs->name, NULL);
1700  }
1701 
1702  // TODO: Change safe_fopen() to default to 0600, then remove this.
1703  const mode_t old_umask = SetUmask(0077);
1704 
1705  snprintf(vbuff, CF_MAXVARSIZE, "%s%ccf_rootprocs", statedir, FILE_SEPARATOR);
1706  RawSaveItemList(rootprocs, vbuff, NewLineMode_Unix);
1707  DeleteItemList(rootprocs);
1708 
1709  snprintf(vbuff, CF_MAXVARSIZE, "%s%ccf_otherprocs", statedir, FILE_SEPARATOR);
1710  RawSaveItemList(otherprocs, vbuff, NewLineMode_Unix);
1711  DeleteItemList(otherprocs);
1712 
1713  RestoreUmask(old_umask);
1714 
1715  free(vbuff);
1716  return true;
1717 }
1718 # endif
1719 
1721 {
1723 
1725  PROCESSTABLE = NULL;
1726 }
void * xmalloc(size_t size)
Definition: alloc-mini.c:46
char * xstrdup(const char *str)
Definition: alloc-mini.c:56
int xasprintf(char **strp, const char *fmt,...)
Definition: alloc.c:71
char * xstrndup(const char *str, size_t n)
Definition: alloc.c:61
#define ARG_UNUSED
Definition: cf-net.c:47
#define CF_PROCCOLS
Definition: cf3.defs.h:57
@ RVAL_TYPE_FNCALL
Definition: cf3.defs.h:608
#define CF_MAXLINKSIZE
Definition: cf3.defs.h:56
#define CF_NOINT
Definition: cf3.defs.h:339
time_t CFSTARTTIME
Definition: cf3globals.c:99
struct utsname VSYSNAME
Definition: cf3globals.c:38
void free(void *)
long IntFromString(const char *s)
Definition: conversion.c:390
int Month2Int(const char *string)
Definition: conversion.c:802
#define CF_BUFSIZE
Definition: definitions.h:50
#define CF_MAXVARSIZE
Definition: definitions.h:36
bool EvalProcessResult(const char *process_result, StringSet *proc_attr)
Definition: eval_context.c:620
static void cleanup(void *generic_data)
Definition: fchmodat.c:56
mode_t SetUmask(mode_t new_mask)
Definition: file_lib.c:496
ssize_t CfReadLine(char **buff, size_t *size, FILE *fp)
Works exactly like posix 'getline', EXCEPT it does not include carriage return at the end.
Definition: file_lib.c:1476
void RestoreUmask(mode_t old_mask)
Definition: file_lib.c:502
FILE * safe_fopen(const char *const path, const char *const mode)
Definition: file_lib.c:812
#define FILE_SEPARATOR
Definition: file_lib.h:102
@ NewLineMode_Unix
Definition: file_lib.h:36
#define NULL
Definition: getopt1.c:56
bool DeleteItemNotContaining(Item **list, const char *string)
Definition: item_lib.c:1009
bool RawSaveItemList(const Item *liststart, const char *filename, NewLineMode new_line_mode)
Definition: item_lib.c:1016
void AppendItem(Item **liststart, const char *itemstring, const char *classes)
Definition: item_lib.c:415
bool DeleteItemContaining(Item **list, const char *string)
Definition: item_lib.c:1002
void DeleteItemList(Item *item)
Definition: item_lib.c:808
void CopyList(Item **dest, const Item *source)
Definition: item_lib.c:468
Item * ReverseItemList(Item *list)
Definition: item_lib.c:97
Item * PrependItem(Item **liststart, const char *itemstring, const char *classes)
Definition: item_lib.c:372
const char * GetStateDir(void)
Definition: known_dirs.c:186
struct tm * localtime_r(const time_t *timep, struct tm *result)
@ LogDebug
Definition: log.h:34
LogLevel LogGetGlobalLevel(void)
Definition: logging.c:581
const char * GetErrorStr(void)
Definition: logging.c:275
void Log(LogLevel level, const char *fmt,...)
Definition: logging.c:409
@ LOG_MOD_PS
Definition: logging.h:65
@ LOG_LEVEL_ERR
Definition: logging.h:42
@ LOG_LEVEL_DEBUG
Definition: logging.h:47
@ LOG_LEVEL_WARNING
Definition: logging.h:43
@ LOG_LEVEL_VERBOSE
Definition: logging.h:46
@ LOG_LEVEL_INFO
Definition: logging.h:45
void StringMapDestroy(StringMap *map)
Definition: map.c:359
bool StringMapHasKey(const StringMap *map, const char *key)
Definition: map.c:359
bool StringMapInsert(const StringMap *map, char *key, char *value)
Definition: map.c:359
StringMap * StringMapNew(void)
Definition: map.c:359
char * StringMapGet(const StringMap *map, const char *key)
Definition: map.c:359
void StringMapClear(StringMap *map)
Definition: map.c:359
void xsnprintf(char *str, size_t str_size, const char *format,...)
Definition: misc_lib.c:114
int cf_pclose(FILE *pp)
Definition: pipes_unix.c:812
FILE * cf_popen(const char *command, const char *type, bool capture_stderr)
Definition: pipes_unix.c:332
#define PRINTSIZE(what)
Definition: printsize.h:66
static bool SelectProcess(const char *procentry, time_t pstime, char **names, int *start, int *end, const char *process_regex, const ProcessSelect *a, bool attrselect)
Item * SelectProcesses(const char *process_name, const ProcessSelect *a, bool attrselect)
static bool SelectProcTimeCounterRangeMatch(char *name1, char *name2, time_t min, time_t max, char **names, char **line)
static int GetProcColumnIndex(const char *name1, const char *name2, char **names)
static void ApplyPlatformExtraTable(char **names, char **columns)
static int ExtractPid(char *psentry, char **names, int *end)
static bool SplitProcLine(const char *proc, time_t pstime, char **names, int *start, int *end, PsColumnAlgorithm pca, char **line)
static bool SelectProcRegexMatch(const char *name1, const char *name2, const char *regex, bool anchored, char **colNames, char **line)
static void LoadPlatformExtraTable(void)
static void PrintStringIndexLine(int prefix_spaces, int len)
static bool SelectProcRangeMatch(char *name1, char *name2, int min, int max, char **names, char **line)
static const char * GetProcessOptions(void)
bool IsProcessNameRunning(char *procNameRegex)
static void CheckPsLineLimitations(void)
PsColumnAlgorithm
@ PCA_AllColumnsPresent
@ PCA_ZombieSkipEmptyColumns
bool LoadProcessTable()
static time_t TimeAbs2Int(const char *s)
const char * GetProcessTableLegend(void)
static void MaybeFixStartTime(const char *line, time_t pstime, char **names, char **fields)
static Item * PROCESSTABLE
static void GetProcessColumnNames(const char *proc, char **names, int *start, int *end)
static const PsColumnAlgorithm PS_COLUMN_ALGORITHM[]
static void ClearPlatformExtraTable(void)
void ClearProcessTable(void)
static bool SelectProcTimeAbsRangeMatch(char *name1, char *name2, time_t min, time_t max, char **names, char **line)
#define TABLE_STORAGE
static long TimeCounter2Int(const char *s)
bool StringMatchFull(const char *regex, const char *str)
Definition: regex.c:106
bool StringMatch(const char *regex, const char *str, int *start, int *end)
Definition: regex.c:90
FnCall * RlistFnCallValue(const Rlist *rlist)
Definition: rlist.c:105
char * RlistScalarValue(const Rlist *rlist)
Definition: rlist.c:83
Seq * SeqNew(size_t initialCapacity, void(ItemDestroy)(void *item))
Definition: sequence.c:31
void * SeqLookup(Seq *seq, const void *key, SeqItemComparator Compare)
Linearly searches through the sequence and return the first item considered equal to the specified ke...
Definition: sequence.c:161
void SeqAppend(Seq *seq, void *item)
Append a new item to the Sequence.
Definition: sequence.c:104
StringSetIterator StringSetIteratorInit(StringSet *set)
Definition: set.c:34
size_t StringSetSize(const StringSet *set)
Definition: set.c:34
StringSet * StringSetNew(void)
Definition: set.c:34
void StringSetDestroy(StringSet *set)
Definition: set.c:34
void StringSetAdd(const StringSet *set, char *element)
Definition: set.c:34
char * StringSetIteratorNext(StringSetIterator *iter)
Definition: set.c:34
int Chop(char *str, size_t max_length)
Remove trailing spaces.
Definition: string_lib.c:1174
#define NULL_OR_EMPTY(str)
Definition: string_lib.h:43
size_t strlcat(char *dst, const char *src, size_t siz)
Definition: strlcat.c:36
Definition: item_lib.h:33
Item * next
Definition: item_lib.h:38
int counter
Definition: item_lib.h:36
char * name
Definition: item_lib.h:34
time_t min_stime
Definition: cf3.defs.h:1151
long min_rsize
Definition: cf3.defs.h:1145
time_t max_stime
Definition: cf3.defs.h:1152
Rlist * owner
Definition: cf3.defs.h:1138
char * command
Definition: cf3.defs.h:1158
char * tty
Definition: cf3.defs.h:1159
long max_rsize
Definition: cf3.defs.h:1146
long max_vsize
Definition: cf3.defs.h:1148
char * process_result
Definition: cf3.defs.h:1160
time_t max_ttime
Definition: cf3.defs.h:1150
long min_thread
Definition: cf3.defs.h:1155
char * status
Definition: cf3.defs.h:1157
time_t min_ttime
Definition: cf3.defs.h:1149
long max_thread
Definition: cf3.defs.h:1156
long min_vsize
Definition: cf3.defs.h:1147
Definition: rlist.h:35
Rval val
Definition: rlist.h:36
Rlist * next
Definition: rlist.h:37
RvalType type
Definition: cf3.defs.h:616
Sequence data-structure.
Definition: sequence.h:50
Definition: map.h:212
Definition: set.h:138
Definition: writer.c:45
char release[257]
Definition: platform.h:110
const char *const VPSCOMM[]
Definition: systype.c:67
PlatformContext VPSHARDCLASS
Definition: systype.c:37
const char *const VPSOPTS[]
Definition: systype.c:95
@ PLATFORM_CONTEXT_UNKNOWN
Definition: systype.h:32
@ PLATFORM_CONTEXT_NETBSD
Definition: systype.h:41
@ PLATFORM_CONTEXT_OPENVZ
Definition: systype.h:33
@ PLATFORM_CONTEXT_MINGW
Definition: systype.h:50
@ PLATFORM_CONTEXT_AIX
Definition: systype.h:35
@ PLATFORM_CONTEXT_SOLARIS
Definition: systype.h:38
@ PLATFORM_CONTEXT_DRAGONFLY
Definition: systype.h:49
@ PLATFORM_CONTEXT_VMWARE
Definition: systype.h:51
@ PLATFORM_CONTEXT_FREEBSD
Definition: systype.h:40
@ PLATFORM_CONTEXT_HP
Definition: systype.h:34
@ PLATFORM_CONTEXT_QNX
Definition: systype.h:48
@ PLATFORM_CONTEXT_DARWIN
Definition: systype.h:47
@ PLATFORM_CONTEXT_LINUX
Definition: systype.h:36
@ PLATFORM_CONTEXT_OPENBSD
Definition: systype.h:45
@ PLATFORM_CONTEXT_SYSTEMV
Definition: systype.h:44
@ PLATFORM_CONTEXT_ANDROID
Definition: systype.h:52
@ PLATFORM_CONTEXT_CFSCO
Definition: systype.h:46
@ PLATFORM_CONTEXT_BUSYBOX
Definition: systype.h:37
@ PLATFORM_CONTEXT_SUN_SOLARIS
Definition: systype.h:39
@ PLATFORM_CONTEXT_WINDOWS_NT
Definition: systype.h:43
@ PLATFORM_CONTEXT_CRAYOS
Definition: systype.h:42
size_t WriterWrite(Writer *writer, const char *str)
Definition: writer.c:193
const char * StringWriterData(const Writer *writer)
Definition: writer.c:229
size_t WriterWriteChar(Writer *writer, char c)
Definition: writer.c:200
void WriterClose(Writer *writer)
Definition: writer.c:242
Writer * StringWriter(void)
Definition: writer.c:67
bool IsGlobalZone()
Definition: zones.c:34