Print this page
10130 smatch fixes for usr/src/cmd/fm
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/fm/notify/smtp-notify/common/smtp-notify.c
+++ new/usr/src/cmd/fm/notify/smtp-notify/common/smtp-notify.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
↓ open down ↓ |
14 lines elided |
↑ open up ↑ |
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 24 */
25 +
26 +/*
27 + * Copyright (c) 2018, Joyent, Inc.
28 + */
29 +
25 30 #include <stdio.h>
26 31 #include <stdlib.h>
27 32 #include <string.h>
28 33 #include <alloca.h>
29 34 #include <errno.h>
30 35 #include <fcntl.h>
31 36 #include <libscf.h>
32 37 #include <priv_utils.h>
33 38 #include <netdb.h>
34 39 #include <signal.h>
35 40 #include <strings.h>
36 41 #include <time.h>
37 42 #include <unistd.h>
38 43 #include <zone.h>
39 44 #include <sys/types.h>
40 45 #include <sys/stat.h>
41 46 #include <fm/fmd_msg.h>
42 47 #include <fm/libfmevent.h>
43 48 #include "libfmnotify.h"
44 49
45 50 #define SENDMAIL "/usr/sbin/sendmail"
46 51 #define SVCNAME "system/fm/smtp-notify"
47 52
48 53 #define XHDR_HOSTNAME "X-FMEV-HOSTNAME"
49 54 #define XHDR_CLASS "X-FMEV-CLASS"
50 55 #define XHDR_UUID "X-FMEV-UUID"
51 56 #define XHDR_MSGID "X-FMEV-CODE"
52 57 #define XHDR_SEVERITY "X-FMEV-SEVERITY"
53 58 #define XHDR_FMRI "X-FMEV-FMRI"
54 59 #define XHDR_FROM_STATE "X-FMEV-FROM-STATE"
55 60 #define XHDR_TO_STATE "X-FMEV-TO-STATE"
56 61
57 62 /*
58 63 * Debug messages can be enabled by setting the debug property to true
59 64 *
60 65 * # svccfg -s svc:/system/fm/smtp-notify setprop config/debug=true
61 66 *
62 67 * Debug messages will be spooled to the service log at:
63 68 * <root>/var/svc/log/system-fm-smtp-notify:default.log
64 69 */
65 70 #define PP_SCRIPT "usr/lib/fm/notify/process_msg_template.sh"
66 71
67 72 typedef struct email_pref
68 73 {
69 74 int ep_num_recips;
70 75 char **ep_recips;
71 76 char *ep_reply_to;
72 77 char *ep_template_path;
73 78 char *ep_template;
74 79 } email_pref_t;
75 80
76 81 static nd_hdl_t *nhdl;
77 82 static char hostname[MAXHOSTNAMELEN + 1];
78 83 static const char optstr[] = "dfR:";
79 84 static const char DEF_SUBJ_TEMPLATE[] = "smtp-notify-subject-template";
80 85 static const char SMF_SUBJ_TEMPLATE[] = "smtp-notify-smf-subject-template";
81 86 static const char FM_SUBJ_TEMPLATE[] = "smtp-notify-fm-subject-template";
82 87 static const char IREPORT_MSG_TEMPLATE[] = "ireport-msg-template";
83 88 static const char SMF_MSG_TEMPLATE[] = "ireport.os.smf-msg-template";
84 89
85 90 static int
86 91 usage(const char *pname)
87 92 {
88 93 (void) fprintf(stderr, "Usage: %s [-df] [-R <altroot>]\n", pname);
89 94
90 95 (void) fprintf(stderr,
91 96 "\t-d enable debug mode\n"
92 97 "\t-f stay in foreground\n"
93 98 "\t-R specify alternate root\n");
94 99
95 100 return (1);
96 101 }
97 102
98 103 /*
99 104 * This function simply reads the file specified by "template" into a buffer
100 105 * and returns a pointer to that buffer (or NULL on failure). The caller is
101 106 * responsible for free'ing the returned buffer.
102 107 */
103 108 static char *
104 109 read_template(const char *template)
105 110 {
106 111 int fd;
107 112 struct stat statb;
108 113 char *buf;
109 114
110 115 if (stat(template, &statb) != 0) {
111 116 nd_error(nhdl, "Failed to stat %s (%s)", template,
112 117 strerror(errno));
113 118 return (NULL);
114 119 }
115 120 if ((fd = open(template, O_RDONLY)) < 0) {
116 121 nd_error(nhdl, "Failed to open %s (%s)", template,
117 122 strerror(errno));
118 123 return (NULL);
119 124 }
120 125 if ((buf = malloc(statb.st_size + 1)) == NULL) {
121 126 nd_error(nhdl, "Failed to allocate %d bytes", statb.st_size);
122 127 (void) close(fd);
123 128 return (NULL);
124 129 }
125 130 if (read(fd, buf, statb.st_size) < 0) {
126 131 nd_error(nhdl, "Failed to read in template (%s)",
127 132 strerror(errno));
128 133 free(buf);
129 134 (void) close(fd);
130 135 return (NULL);
131 136 }
132 137 buf[statb.st_size] = '\0';
133 138 (void) close(fd);
134 139 return (buf);
135 140 }
136 141
137 142 /*
138 143 * This function runs a user-supplied message body template through a script
139 144 * which replaces the "committed" expansion macros with actual libfmd_msg
140 145 * expansion macros.
141 146 */
142 147 static int
143 148 process_template(nd_ev_info_t *ev_info, email_pref_t *eprefs)
144 149 {
145 150 char pp_script[PATH_MAX], tmpfile[PATH_MAX], pp_cli[PATH_MAX];
146 151 int ret = -1;
147 152
148 153 (void) snprintf(pp_script, sizeof (pp_script), "%s%s",
149 154 nhdl->nh_rootdir, PP_SCRIPT);
150 155 (void) snprintf(tmpfile, sizeof (tmpfile), "%s%s",
151 156 nhdl->nh_rootdir, tmpnam(NULL));
152 157
153 158 /*
154 159 * If it's an SMF event, then the diagcode and severity won't be part
155 160 * of the event payload and so libfmd_msg won't be able to expand them.
156 161 * Therefore we pass the code and severity into the script and let the
157 162 * script do the expansion.
158 163 */
159 164 /* LINTED: E_SEC_SPRINTF_UNBOUNDED_COPY */
160 165 (void) sprintf(pp_cli, "%s %s %s %s %s", pp_script,
161 166 eprefs->ep_template_path, tmpfile, ev_info->ei_diagcode,
162 167 ev_info->ei_severity);
163 168
164 169 nd_debug(nhdl, "Executing %s", pp_cli);
165 170 if (system(pp_cli) != -1)
166 171 if ((eprefs->ep_template = read_template(tmpfile)) != NULL)
167 172 ret = 0;
168 173
169 174 (void) unlink(tmpfile);
170 175 return (ret);
171 176 }
172 177
173 178 /*
174 179 * If someone does an "svcadm refresh" on us, then this function gets called,
175 180 * which rereads our service configuration.
176 181 */
177 182 static void
178 183 get_svc_config()
179 184 {
180 185 int s = 0;
181 186 uint8_t val;
182 187
183 188 s = nd_get_boolean_prop(nhdl, SVCNAME, "config", "debug", &val);
184 189 nhdl->nh_debug = val;
185 190
186 191 s += nd_get_astring_prop(nhdl, SVCNAME, "config", "rootdir",
187 192 &(nhdl->nh_rootdir));
188 193
189 194 if (s != 0)
190 195 nd_error(nhdl, "Failed to read retrieve service "
191 196 "properties\n");
192 197 }
193 198
194 199 static void
195 200 nd_sighandler(int sig)
196 201 {
197 202 if (sig == SIGHUP)
198 203 get_svc_config();
199 204 else
200 205 nd_cleanup(nhdl);
201 206 }
202 207
203 208 /*
204 209 * This function constructs all the email headers and puts them into the
205 210 * "headers" buffer handle. The caller is responsible for free'ing this
206 211 * buffer.
207 212 */
208 213 static int
209 214 build_headers(nd_hdl_t *nhdl, nd_ev_info_t *ev_info, email_pref_t *eprefs,
210 215 char **headers)
211 216 {
212 217 const char *subj_key;
213 218 char *subj_fmt, *subj = NULL;
214 219 size_t len;
215 220 boolean_t is_smf_event = B_FALSE, is_fm_event = B_FALSE;
216 221
217 222 /*
218 223 * Fetch and format the email subject.
219 224 */
220 225 if (strncmp(ev_info->ei_class, "list.", 5) == 0) {
221 226 is_fm_event = B_TRUE;
222 227 subj_key = FM_SUBJ_TEMPLATE;
223 228 } else if (strncmp(ev_info->ei_class, "ireport.os.smf", 14) == 0) {
224 229 is_smf_event = B_TRUE;
225 230 subj_key = SMF_SUBJ_TEMPLATE;
226 231 } else {
227 232 subj_key = DEF_SUBJ_TEMPLATE;
228 233 }
229 234
230 235 if ((subj_fmt = fmd_msg_gettext_key(nhdl->nh_msghdl, NULL,
231 236 FMNOTIFY_MSG_DOMAIN, subj_key)) == NULL) {
232 237 nd_error(nhdl, "Failed to contruct subject format");
233 238 return (-1); /* libfmd_msg error */
234 239 }
235 240
236 241 if (is_fm_event) {
237 242 /* LINTED: E_SEC_PRINTF_VAR_FMT */
238 243 len = snprintf(NULL, 0, subj_fmt, hostname,
239 244 ev_info->ei_diagcode);
240 245 subj = alloca(len + 1);
241 246 /* LINTED: E_SEC_PRINTF_VAR_FMT */
242 247 (void) snprintf(subj, len + 1, subj_fmt, hostname,
243 248 ev_info->ei_diagcode);
244 249 } else if (is_smf_event) {
245 250 /* LINTED: E_SEC_PRINTF_VAR_FMT */
246 251 len = snprintf(NULL, 0, subj_fmt, hostname, ev_info->ei_fmri,
247 252 ev_info->ei_from_state, ev_info->ei_to_state);
248 253 subj = alloca(len + 1);
249 254 /* LINTED: E_SEC_PRINTF_VAR_FMT */
250 255 (void) snprintf(subj, len + 1, subj_fmt, hostname,
251 256 ev_info->ei_fmri, ev_info->ei_from_state,
252 257 ev_info->ei_to_state);
253 258 } else {
254 259 /* LINTED: E_SEC_PRINTF_VAR_FMT */
255 260 len = snprintf(NULL, 0, subj_fmt, hostname);
256 261 subj = alloca(len + 1);
257 262 /* LINTED: E_SEC_PRINTF_VAR_FMT */
258 263 (void) snprintf(subj, len + 1, subj_fmt, hostname);
259 264 }
260 265
261 266 /*
262 267 * Here we add some X-headers to our mail message for use by mail
263 268 * filtering agents. We add headers for the following bits of event
264 269 * data for all events
265 270 *
266 271 * hostname
267 272 * msg id (diagcode)
268 273 * event class
269 274 * event severity
270 275 * event uuid
271 276 *
272 277 * For SMF transition events, we'll have the following add'l X-headers
273 278 *
274 279 * from-state
275 280 * to-state
276 281 * service fmri
277 282 *
278 283 * We follow the X-headers with standard Reply-To and Subject headers.
279 284 */
280 285 if (is_fm_event) {
281 286 len = snprintf(NULL, 0, "%s: %s\n%s: %s\n%s: %s\n%s: %s\n"
282 287 "%s: %s\nReply-To: %s\nSubject: %s\n\n", XHDR_HOSTNAME,
283 288 hostname, XHDR_CLASS, ev_info->ei_class, XHDR_UUID,
284 289 ev_info->ei_uuid, XHDR_MSGID, ev_info->ei_diagcode,
285 290 XHDR_SEVERITY, ev_info->ei_severity, eprefs->ep_reply_to,
286 291 subj);
287 292
288 293 *headers = calloc(len + 1, sizeof (char));
289 294
290 295 (void) snprintf(*headers, len + 1, "%s: %s\n%s: %s\n%s: %s\n"
291 296 "%s: %s\n%s: %s\nReply-To: %s\nSubject: %s\n\n",
292 297 XHDR_HOSTNAME, hostname, XHDR_CLASS, ev_info->ei_class,
293 298 XHDR_UUID, ev_info->ei_uuid, XHDR_MSGID,
294 299 ev_info->ei_diagcode, XHDR_SEVERITY, ev_info->ei_severity,
295 300 eprefs->ep_reply_to, subj);
296 301 } else if (is_smf_event) {
297 302 len = snprintf(NULL, 0, "%s: %s\n%s: %s\n%s: %s\n%s: %s\n"
298 303 "%s: %s\n%s: %s\n%s: %s\nReply-To: %s\n"
299 304 "Subject: %s\n\n", XHDR_HOSTNAME, hostname, XHDR_CLASS,
300 305 ev_info->ei_class, XHDR_MSGID, ev_info->ei_diagcode,
301 306 XHDR_SEVERITY, ev_info->ei_severity, XHDR_FMRI,
302 307 ev_info->ei_fmri, XHDR_FROM_STATE, ev_info->ei_from_state,
303 308 XHDR_TO_STATE, ev_info->ei_to_state, eprefs->ep_reply_to,
304 309 subj);
305 310
306 311 *headers = calloc(len + 1, sizeof (char));
307 312
308 313 (void) snprintf(*headers, len + 1, "%s: %s\n%s: %s\n%s: %s\n"
309 314 "%s: %s\n%s: %s\n%s: %s\n%s: %s\nReply-To: %s\n"
310 315 "Subject: %s\n\n", XHDR_HOSTNAME, hostname, XHDR_CLASS,
311 316 ev_info->ei_class, XHDR_MSGID, ev_info->ei_diagcode,
312 317 XHDR_SEVERITY, ev_info->ei_severity, XHDR_FMRI,
313 318 ev_info->ei_fmri, XHDR_FROM_STATE, ev_info->ei_from_state,
314 319 XHDR_TO_STATE, ev_info->ei_to_state, eprefs->ep_reply_to,
315 320 subj);
316 321 } else {
317 322 len = snprintf(NULL, 0, "%s: %s\n%s: %s\n%s: %s\n%s: %s\n"
318 323 "Reply-To: %s\nSubject: %s\n\n", XHDR_HOSTNAME,
319 324 hostname, XHDR_CLASS, ev_info->ei_class, XHDR_MSGID,
320 325 ev_info->ei_diagcode, XHDR_SEVERITY, ev_info->ei_severity,
321 326 eprefs->ep_reply_to, subj);
322 327
323 328 *headers = calloc(len + 1, sizeof (char));
324 329
325 330 (void) snprintf(*headers, len + 1, "%s: %s\n%s: %s\n%s: %s\n"
326 331 "%s: %s\nReply-To: %s\nSubject: %s\n\n",
327 332 XHDR_HOSTNAME, hostname, XHDR_CLASS, ev_info->ei_class,
328 333 XHDR_MSGID, ev_info->ei_diagcode, XHDR_SEVERITY,
329 334 ev_info->ei_severity, eprefs->ep_reply_to, subj);
330 335 }
331 336 return (0);
332 337 }
333 338
334 339 static void
335 340 send_email(nd_hdl_t *nhdl, const char *headers, const char *body,
336 341 const char *recip)
337 342 {
338 343 FILE *mp;
339 344 char sm_cli[PATH_MAX];
340 345
341 346 /*
342 347 * Open a pipe to sendmail and pump out the email message
343 348 */
344 349 (void) snprintf(sm_cli, PATH_MAX, "%s -t %s", SENDMAIL, recip);
345 350
346 351 nd_debug(nhdl, "Sending email notification to %s", recip);
347 352 if ((mp = popen(sm_cli, "w")) == NULL) {
348 353 nd_error(nhdl, "Failed to open pipe to %s (%s)", SENDMAIL,
349 354 strerror(errno));
350 355 return;
351 356 }
352 357 if (fprintf(mp, "%s", headers) < 0)
353 358 nd_error(nhdl, "Failed to write to pipe (%s)", strerror(errno));
354 359
355 360 if (fprintf(mp, "%s\n.\n", body) < 0)
356 361 nd_error(nhdl, "Failed to write to pipe (%s)",
357 362 strerror(errno));
358 363
359 364 (void) pclose(mp);
360 365 }
361 366
362 367 static void
363 368 send_email_template(nd_hdl_t *nhdl, nd_ev_info_t *ev_info, email_pref_t *eprefs)
364 369 {
365 370 char *msg, *headers;
366 371
367 372 if (build_headers(nhdl, ev_info, eprefs, &headers) != 0)
368 373 return;
369 374
370 375 /*
371 376 * If the user specified a message body template, then we pass it
372 377 * through a private interface in libfmd_msg, which will return a string
373 378 * with any expansion tokens decoded.
374 379 */
375 380 if ((msg = fmd_msg_decode_tokens(ev_info->ei_payload,
376 381 eprefs->ep_template, ev_info->ei_url)) == NULL) {
377 382 nd_error(nhdl, "Failed to parse msg template");
378 383 free(headers);
379 384 return;
380 385 }
381 386 for (int i = 0; i < eprefs->ep_num_recips; i++)
382 387 send_email(nhdl, headers, msg, eprefs->ep_recips[i]);
383 388
384 389 free(msg);
385 390 free(headers);
386 391 }
387 392
388 393 static int
389 394 get_email_prefs(nd_hdl_t *nhdl, fmev_t ev, email_pref_t **eprefs)
390 395 {
391 396 nvlist_t **p_nvl = NULL;
392 397 email_pref_t *ep;
393 398 uint_t npref, tn1 = 0, tn2 = 0;
394 399 char **tmparr1, **tmparr2;
395 400 int r, ret = -1;
396 401
397 402 r = nd_get_notify_prefs(nhdl, "smtp", ev, &p_nvl, &npref);
398 403 if (r == SCF_ERROR_NOT_FOUND) {
399 404 /*
400 405 * No email notification preferences specified for this type of
401 406 * event, so we're done
402 407 */
403 408 return (-1);
404 409 } else if (r != 0) {
405 410 nd_error(nhdl, "Failed to retrieve notification preferences "
406 411 "for this event");
407 412 return (-1);
408 413 }
409 414
410 415 if ((ep = malloc(sizeof (email_pref_t))) == NULL) {
411 416 nd_error(nhdl, "Failed to allocate space for email preferences "
412 417 "(%s)", strerror(errno));
413 418 goto eprefs_done;
414 419 }
415 420 (void) memset(ep, 0, sizeof (email_pref_t));
416 421
417 422 /*
418 423 * For SMF state transition events, pref_nvl may contain two sets of
419 424 * preferences, which will have to be merged.
420 425 *
421 426 * The "smtp" nvlist can contain up to four members:
422 427 *
423 428 * "active" - boolean - used to toggle notfications
424 429 * "to" - a string array of email recipients
425 430 * "reply-to" - a string array containing the reply-to addresses
426 431 * - this is optional and defaults to root@localhost
427 432 * "msg_template" - the pathname of a user-supplied message body
428 433 * template
429 434 *
430 435 * In the case that we have two sets of preferences, we will merge them
431 436 * using the following rules:
432 437 *
433 438 * "active" will be set to true, if it is true in either set
434 439 *
435 440 * The "reply-to" and "to" lists will be merged, with duplicate email
436 441 * addresses removed.
437 442 */
438 443 if (npref == 2) {
439 444 boolean_t *act1, *act2;
440 445 char **arr1, **arr2, **strarr, **reparr1, **reparr2;
441 446 uint_t n1, n2, arrsz, repsz;
442 447
443 448 r = nvlist_lookup_boolean_array(p_nvl[0], "active", &act1, &n1);
444 449 r += nvlist_lookup_boolean_array(p_nvl[1], "active", &act2,
445 450 &n2);
446 451 r += nvlist_lookup_string_array(p_nvl[0], "to", &arr1, &n1);
447 452 r += nvlist_lookup_string_array(p_nvl[1], "to", &arr2, &n2);
448 453
449 454 if (r != 0) {
450 455 nd_error(nhdl, "Malformed email notification "
451 456 "preferences");
452 457 nd_dump_nvlist(nhdl, p_nvl[0]);
453 458 nd_dump_nvlist(nhdl, p_nvl[1]);
454 459 goto eprefs_done;
455 460 } else if (!act1[0] && !act2[0]) {
456 461 nd_debug(nhdl, "Email notification is disabled");
457 462 goto eprefs_done;
458 463 }
459 464
460 465 if (nd_split_list(nhdl, arr1[0], ",", &tmparr1, &tn1) != 0 ||
461 466 nd_split_list(nhdl, arr2[0], ",", &tmparr2, &tn2) != 0) {
462 467 nd_error(nhdl, "Error parsing \"to\" lists");
463 468 nd_dump_nvlist(nhdl, p_nvl[0]);
464 469 nd_dump_nvlist(nhdl, p_nvl[1]);
465 470 goto eprefs_done;
466 471 }
467 472
468 473 if ((ep->ep_num_recips = nd_merge_strarray(nhdl, tmparr1, tn1,
469 474 tmparr2, tn2, &ep->ep_recips)) < 0) {
470 475 nd_error(nhdl, "Error merging email recipient lists");
471 476 goto eprefs_done;
472 477 }
473 478
474 479 r = nvlist_lookup_string_array(p_nvl[0], "reply-to", &arr1,
475 480 &n1);
476 481 r += nvlist_lookup_string_array(p_nvl[1], "reply-to", &arr2,
477 482 &n2);
478 483 repsz = n1 = n2 = 0;
479 484 if (!r &&
480 485 nd_split_list(nhdl, arr1[0], ",", &reparr1, &n1) != 0 ||
481 486 nd_split_list(nhdl, arr2[0], ",", &reparr2, &n2) != 0 ||
482 487 (repsz = nd_merge_strarray(nhdl, tmparr1, n1, tmparr2, n2,
483 488 &strarr)) != 0 ||
484 489 nd_join_strarray(nhdl, strarr, repsz, &ep->ep_reply_to)
485 490 != 0) {
486 491
487 492 ep->ep_reply_to = strdup("root@localhost");
488 493 }
489 494 if (n1)
490 495 nd_free_strarray(reparr1, n1);
491 496 if (n2)
492 497 nd_free_strarray(reparr2, n2);
493 498 if (repsz > 0)
494 499 nd_free_strarray(strarr, repsz);
495 500
496 501 if (nvlist_lookup_string_array(p_nvl[0], "msg_template",
497 502 &strarr, &arrsz) == 0)
498 503 ep->ep_template_path = strdup(strarr[0]);
499 504 } else {
500 505 char **strarr, **tmparr;
501 506 uint_t arrsz;
502 507 boolean_t *active;
503 508
504 509 /*
505 510 * Both the "active" and "to" notification preferences are
506 511 * required, so if we have trouble looking either of these up
507 512 * we return an error. We will also return an error if "active"
508 513 * is set to false. Returning an error will cause us to not
509 514 * send a notification for this event.
510 515 */
511 516 r = nvlist_lookup_boolean_array(p_nvl[0], "active", &active,
512 517 &arrsz);
513 518 r += nvlist_lookup_string_array(p_nvl[0], "to", &strarr,
514 519 &arrsz);
515 520
516 521 if (r != 0) {
517 522 nd_error(nhdl, "Malformed email notification "
518 523 "preferences");
519 524 nd_dump_nvlist(nhdl, p_nvl[0]);
520 525 goto eprefs_done;
521 526 } else if (!active[0]) {
522 527 nd_debug(nhdl, "Email notification is disabled");
523 528 goto eprefs_done;
524 529 }
525 530
526 531 if (nd_split_list(nhdl, strarr[0], ",", &tmparr, &arrsz)
527 532 != 0) {
528 533 nd_error(nhdl, "Error parsing \"to\" list");
529 534 goto eprefs_done;
530 535 }
531 536 ep->ep_num_recips = arrsz;
532 537 ep->ep_recips = tmparr;
533 538
534 539 if (nvlist_lookup_string_array(p_nvl[0], "msg_template",
535 540 &strarr, &arrsz) == 0)
536 541 ep->ep_template_path = strdup(strarr[0]);
537 542
538 543 if (nvlist_lookup_string_array(p_nvl[0], "reply-to", &strarr,
539 544 &arrsz) == 0)
540 545 ep->ep_reply_to = strdup(strarr[0]);
541 546 else
542 547 ep->ep_reply_to = strdup("root@localhost");
543 548 }
544 549 ret = 0;
545 550 *eprefs = ep;
546 551 eprefs_done:
547 552 if (ret != 0) {
548 553 if (ep->ep_recips)
549 554 nd_free_strarray(ep->ep_recips, ep->ep_num_recips);
550 555 if (ep->ep_reply_to)
551 556 free(ep->ep_reply_to);
552 557 free(ep);
553 558 }
554 559 if (tn1)
555 560 nd_free_strarray(tmparr1, tn1);
556 561 if (tn2)
557 562 nd_free_strarray(tmparr2, tn2);
558 563 nd_free_nvlarray(p_nvl, npref);
559 564
560 565 return (ret);
561 566 }
562 567
563 568 /*ARGSUSED*/
564 569 static void
565 570 irpt_cbfunc(fmev_t ev, const char *class, nvlist_t *nvl, void *arg)
566 571 {
567 572 char *body_fmt, *headers = NULL, *body = NULL, tstamp[32];
568 573 struct tm ts;
569 574 size_t len;
570 575 nd_ev_info_t *ev_info = NULL;
571 576 email_pref_t *eprefs;
572 577
573 578 nd_debug(nhdl, "Received event of class %s", class);
574 579
575 580 if (get_email_prefs(nhdl, ev, &eprefs) < 0)
576 581 return;
577 582
578 583 if (nd_get_event_info(nhdl, class, ev, &ev_info) != 0)
579 584 goto irpt_done;
580 585
581 586 /*
582 587 * If the user specified a template, then we pass it through a script,
583 588 * which post-processes any expansion macros. Then we attempt to read
584 589 * it in and then send the message. Otherwise we carry on with the rest
585 590 * of this function which will contruct the message body from one of the
586 591 * default templates.
587 592 */
588 593 if (eprefs->ep_template != NULL)
589 594 free(eprefs->ep_template);
590 595
591 596 if (eprefs->ep_template_path != NULL &&
592 597 process_template(ev_info, eprefs) == 0) {
593 598 send_email_template(nhdl, ev_info, eprefs);
594 599 goto irpt_done;
595 600 }
596 601
597 602 /*
598 603 * Fetch and format the event timestamp
599 604 */
600 605 if (fmev_localtime(ev, &ts) == NULL) {
601 606 nd_error(nhdl, "Malformed event: failed to retrieve "
602 607 "timestamp");
603 608 goto irpt_done;
604 609 }
605 610 (void) strftime(tstamp, sizeof (tstamp), NULL, &ts);
606 611
607 612 /*
608 613 * We have two message body templates to choose from. One for SMF
609 614 * service transition events and a generic one for any other
610 615 * uncommitted ireport.
611 616 */
612 617 if (strncmp(class, "ireport.os.smf", 14) == 0) {
613 618 /*
614 619 * For SMF state transition events we have a standard message
615 620 * template that we fill in based on the payload of the event.
616 621 */
617 622 if ((body_fmt = fmd_msg_gettext_key(nhdl->nh_msghdl, NULL,
618 623 FMNOTIFY_MSG_DOMAIN, SMF_MSG_TEMPLATE)) == NULL) {
619 624 nd_error(nhdl, "Failed to format message body");
620 625 goto irpt_done;
621 626 }
622 627
623 628 /* LINTED: E_SEC_PRINTF_VAR_FMT */
624 629 len = snprintf(NULL, 0, body_fmt, hostname, tstamp,
625 630 ev_info->ei_fmri, ev_info->ei_from_state,
626 631 ev_info->ei_to_state, ev_info->ei_descr,
627 632 ev_info->ei_reason);
628 633 body = calloc(len, sizeof (char));
629 634 /* LINTED: E_SEC_PRINTF_VAR_FMT */
630 635 (void) snprintf(body, len, body_fmt, hostname, tstamp,
631 636 ev_info->ei_fmri, ev_info->ei_from_state,
632 637 ev_info->ei_to_state, ev_info->ei_descr,
633 638 ev_info->ei_reason);
634 639 } else {
635 640 if ((body_fmt = fmd_msg_gettext_key(nhdl->nh_msghdl, NULL,
636 641 FMNOTIFY_MSG_DOMAIN, IREPORT_MSG_TEMPLATE)) == NULL) {
637 642 nd_error(nhdl, "Failed to format message body");
638 643 goto irpt_done;
639 644 }
640 645 /* LINTED: E_SEC_PRINTF_VAR_FMT */
641 646 len = snprintf(NULL, 0, body_fmt, hostname, tstamp, class);
642 647 body = calloc(len, sizeof (char));
643 648 /* LINTED: E_SEC_PRINTF_VAR_FMT */
644 649 (void) snprintf(body, len, body_fmt, hostname, tstamp, class);
645 650 }
646 651
647 652 if (build_headers(nhdl, ev_info, eprefs, &headers) != 0)
648 653 goto irpt_done;
649 654
650 655 /*
651 656 * Everything is ready, so now we just iterate through the list of
652 657 * recipents, sending an email notification to each one.
653 658 */
654 659 for (int i = 0; i < eprefs->ep_num_recips; i++)
655 660 send_email(nhdl, headers, body, eprefs->ep_recips[i]);
656 661
657 662 irpt_done:
658 663 free(headers);
659 664 free(body);
660 665 if (ev_info)
661 666 nd_free_event_info(ev_info);
662 667 if (eprefs->ep_recips)
663 668 nd_free_strarray(eprefs->ep_recips, eprefs->ep_num_recips);
664 669 if (eprefs->ep_reply_to)
665 670 free(eprefs->ep_reply_to);
666 671 free(eprefs);
667 672 }
668 673
669 674 /*
670 675 * There is a lack of uniformity in how the various entries in our diagnosis
671 676 * are terminated. Some end with one newline, others with two. This makes the
672 677 * output look a bit ugly. Therefore we postprocess the message before sending
673 678 * it, removing consecutive occurences of newlines.
674 679 */
675 680 static void
676 681 postprocess_msg(char *msg)
677 682 {
678 683 int i = 0, j = 0;
679 684 char *buf;
680 685
681 686 if ((buf = malloc(strlen(msg) + 1)) == NULL)
682 687 return;
683 688
684 689 buf[j++] = msg[i++];
685 690 for (i = 1; i < strlen(msg); i++) {
686 691 if (!(msg[i] == '\n' && msg[i - 1] == '\n'))
687 692 buf[j++] = msg[i];
688 693 }
689 694 buf[j] = '\0';
690 695 (void) strncpy(msg, buf, j+1);
691 696 free(buf);
692 697 }
693 698
694 699 /*ARGSUSED*/
695 700 static void
696 701 listev_cb(fmev_t ev, const char *class, nvlist_t *nvl, void *arg)
697 702 {
698 703 char *body = NULL, *headers = NULL;
699 704 nd_ev_info_t *ev_info = NULL;
700 705 boolean_t domsg;
701 706 email_pref_t *eprefs;
702 707
703 708 nd_debug(nhdl, "Received event of class %s", class);
704 709
705 710 if (get_email_prefs(nhdl, ev, &eprefs) < 0)
706 711 return;
707 712
708 713 if (nd_get_event_info(nhdl, class, ev, &ev_info) != 0)
709 714 goto listcb_done;
710 715
711 716 /*
712 717 * If the message payload member is set to 0, then it's an event we
713 718 * typically suppress messaging on, so we won't send an email for it.
714 719 */
715 720 if (nvlist_lookup_boolean_value(ev_info->ei_payload, FM_SUSPECT_MESSAGE,
716 721 &domsg) == 0 && !domsg) {
717 722 nd_debug(nhdl, "Messaging suppressed for this event");
718 723 goto listcb_done;
719 724 }
720 725
721 726 /*
722 727 * If the user specified a template, then we pass it through a script,
723 728 * which post-processes any expansion macros. Then we attempt to read
724 729 * it in and then send the message. Otherwise we carry on with the rest
725 730 * of this function which will contruct the message body from one of the
726 731 * default templates.
727 732 */
728 733 if (eprefs->ep_template != NULL)
729 734 free(eprefs->ep_template);
730 735
731 736 if (eprefs->ep_template_path != NULL &&
732 737 process_template(ev_info, eprefs) == 0) {
733 738 send_email_template(nhdl, ev_info, eprefs);
734 739 goto listcb_done;
735 740 }
736 741
737 742 /*
738 743 * Format the message body
739 744 *
740 745 * For FMA list.* events we use the same message that the
741 746 * syslog-msgs agent would emit as the message body
742 747 *
743 748 */
744 749 if ((body = fmd_msg_gettext_nv(nhdl->nh_msghdl, NULL,
745 750 ev_info->ei_payload)) == NULL) {
746 751 nd_error(nhdl, "Failed to format message body");
747 752 nd_dump_nvlist(nhdl, ev_info->ei_payload);
748 753 goto listcb_done;
749 754 }
750 755 postprocess_msg(body);
751 756
752 757 if (build_headers(nhdl, ev_info, eprefs, &headers) != 0)
753 758 goto listcb_done;
754 759
755 760 /*
756 761 * Everything is ready, so now we just iterate through the list of
757 762 * recipents, sending an email notification to each one.
758 763 */
759 764 for (int i = 0; i < eprefs->ep_num_recips; i++)
760 765 send_email(nhdl, headers, body, eprefs->ep_recips[i]);
761 766
762 767 listcb_done:
763 768 free(headers);
764 769 free(body);
765 770 if (ev_info)
766 771 nd_free_event_info(ev_info);
767 772 if (eprefs->ep_recips)
768 773 nd_free_strarray(eprefs->ep_recips, eprefs->ep_num_recips);
769 774 if (eprefs->ep_reply_to)
770 775 free(eprefs->ep_reply_to);
771 776 free(eprefs);
772 777 }
773 778
774 779 int
775 780 main(int argc, char *argv[])
776 781 {
777 782 struct rlimit rlim;
778 783 struct sigaction act;
779 784 sigset_t set;
780 785 char c;
781 786 boolean_t run_fg = B_FALSE;
782 787
783 788 if ((nhdl = malloc(sizeof (nd_hdl_t))) == NULL) {
784 789 (void) fprintf(stderr, "Failed to allocate space for notifyd "
785 790 "handle (%s)", strerror(errno));
786 791 return (1);
787 792 }
788 793 (void) memset(nhdl, 0, sizeof (nd_hdl_t));
789 794
790 795 nhdl->nh_keep_running = B_TRUE;
791 796 nhdl->nh_log_fd = stderr;
792 797 nhdl->nh_pname = argv[0];
793 798
794 799 get_svc_config();
795 800
796 801 /*
797 802 * In the case where we get started outside of SMF, args passed on the
798 803 * command line override SMF property setting
799 804 */
800 805 while (optind < argc) {
801 806 while ((c = getopt(argc, argv, optstr)) != -1) {
802 807 switch (c) {
803 808 case 'd':
↓ open down ↓ |
769 lines elided |
↑ open up ↑ |
804 809 nhdl->nh_debug = B_TRUE;
805 810 break;
806 811 case 'f':
807 812 run_fg = B_TRUE;
808 813 break;
809 814 case 'R':
810 815 nhdl->nh_rootdir = strdup(optarg);
811 816 break;
812 817 default:
813 818 free(nhdl);
814 - return (usage(nhdl->nh_pname));
819 + return (usage(argv[0]));
815 820 }
816 821 }
817 822 }
818 823
819 824 /*
820 825 * Set up a signal handler for SIGTERM (and SIGINT if we'll
821 826 * be running in the foreground) to ensure sure we get a chance to exit
822 827 * in an orderly fashion. We also catch SIGHUP, which will be sent to
823 828 * us by SMF if the service is refreshed.
824 829 */
825 830 (void) sigfillset(&set);
826 831 (void) sigfillset(&act.sa_mask);
827 832 act.sa_handler = nd_sighandler;
828 833 act.sa_flags = 0;
829 834
830 835 (void) sigaction(SIGTERM, &act, NULL);
831 836 (void) sigdelset(&set, SIGTERM);
832 837 (void) sigaction(SIGHUP, &act, NULL);
833 838 (void) sigdelset(&set, SIGHUP);
834 839
835 840 if (run_fg) {
836 841 (void) sigaction(SIGINT, &act, NULL);
837 842 (void) sigdelset(&set, SIGINT);
838 843 } else
839 844 nd_daemonize(nhdl);
840 845
841 846 rlim.rlim_cur = RLIM_INFINITY;
842 847 rlim.rlim_max = RLIM_INFINITY;
843 848 (void) setrlimit(RLIMIT_CORE, &rlim);
844 849
845 850 /*
846 851 * We need to be root to initialize our libfmevent handle (because that
847 852 * involves reading/writing to /dev/sysevent), so we do this before
848 853 * calling __init_daemon_priv.
849 854 */
850 855 nhdl->nh_evhdl = fmev_shdl_init(LIBFMEVENT_VERSION_2, NULL, NULL, NULL);
851 856 if (nhdl->nh_evhdl == NULL) {
852 857 (void) sleep(5);
853 858 nd_abort(nhdl, "failed to initialize libfmevent: %s",
854 859 fmev_strerror(fmev_errno));
855 860 }
856 861
857 862 /*
858 863 * If we're in the global zone, reset all of our privilege sets to
859 864 * the minimum set of required privileges. Since we've already
860 865 * initialized our libmevent handle, we no no longer need to run as
861 866 * root, so we change our uid/gid to noaccess (60002).
862 867 *
863 868 * __init_daemon_priv will also set the process core path for us
864 869 *
865 870 */
866 871 if (getzoneid() == GLOBAL_ZONEID)
867 872 if (__init_daemon_priv(
868 873 PU_RESETGROUPS | PU_LIMITPRIVS | PU_INHERITPRIVS,
869 874 60002, 60002, PRIV_PROC_SETID, NULL) != 0)
870 875 nd_abort(nhdl, "additional privileges required to run");
871 876
872 877 nhdl->nh_msghdl = fmd_msg_init(nhdl->nh_rootdir, FMD_MSG_VERSION);
873 878 if (nhdl->nh_msghdl == NULL)
874 879 nd_abort(nhdl, "failed to initialize libfmd_msg");
875 880
876 881 (void) gethostname(hostname, MAXHOSTNAMELEN + 1);
877 882 /*
878 883 * Set up our event subscriptions. We subscribe to everything and then
879 884 * consult libscf when we receive an event to determine whether to send
880 885 * an email notification.
881 886 */
882 887 nd_debug(nhdl, "Subscribing to ireport.* events");
883 888 if (fmev_shdl_subscribe(nhdl->nh_evhdl, "ireport.*", irpt_cbfunc,
884 889 NULL) != FMEV_SUCCESS) {
885 890 nd_abort(nhdl, "fmev_shdl_subscribe failed: %s",
886 891 fmev_strerror(fmev_errno));
887 892 }
888 893
889 894 nd_debug(nhdl, "Subscribing to list.* events");
890 895 if (fmev_shdl_subscribe(nhdl->nh_evhdl, "list.*", listev_cb,
891 896 NULL) != FMEV_SUCCESS) {
892 897 nd_abort(nhdl, "fmev_shdl_subscribe failed: %s",
893 898 fmev_strerror(fmev_errno));
894 899 }
895 900
896 901 /*
897 902 * We run until someone kills us
898 903 */
899 904 while (nhdl->nh_keep_running)
900 905 (void) sigsuspend(&set);
901 906
902 907 free(nhdl->nh_rootdir);
903 908 free(nhdl);
904 909
905 910 return (0);
906 911 }
↓ open down ↓ |
82 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX