4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
26 #include <sys/fm/protocol.h>
27 #include <sys/strlog.h>
31 #include <fm/fmd_api.h>
32 #include <fm/fmd_msg.h>
50 { "bad_vers", FMD_TYPE_UINT64
, "event version is missing or invalid" },
51 { "bad_code", FMD_TYPE_UINT64
, "event code has no dictionary name" },
52 { "log_err", FMD_TYPE_UINT64
, "failed to log message to log(7D)" },
53 { "msg_err", FMD_TYPE_UINT64
, "failed to log message to sysmsg(7D)" },
54 { "no_msg", FMD_TYPE_UINT64
, "message logging suppressed" }
57 static const struct facility
{
61 { "LOG_DAEMON", LOG_DAEMON
},
62 { "LOG_LOCAL0", LOG_LOCAL0
},
63 { "LOG_LOCAL1", LOG_LOCAL1
},
64 { "LOG_LOCAL2", LOG_LOCAL2
},
65 { "LOG_LOCAL3", LOG_LOCAL3
},
66 { "LOG_LOCAL4", LOG_LOCAL4
},
67 { "LOG_LOCAL5", LOG_LOCAL5
},
68 { "LOG_LOCAL6", LOG_LOCAL6
},
69 { "LOG_LOCAL7", LOG_LOCAL7
},
73 static fmd_msg_hdl_t
*syslog_msghdl
; /* handle for libfmd_msg calls */
74 static int syslog_msgall
; /* set to message all faults */
75 static log_ctl_t syslog_ctl
; /* log(7D) meta-data for each msg */
76 static int syslog_logfd
= -1; /* log(7D) file descriptor */
77 static int syslog_msgfd
= -1; /* sysmsg(7D) file descriptor */
78 static int syslog_file
; /* log to syslog_logfd */
79 static int syslog_cons
; /* log to syslog_msgfd */
80 static const char SYSLOG_POINTER
[] = "syslog-msgs-pointer";
83 * Ideally we would just use syslog(3C) for outputting our messages, but our
84 * messaging standard defines a nice multi-line format and syslogd(1M) is very
85 * inflexible and stupid when it comes to multi-line messages. It pulls data
86 * out of log(7D) and splits it up by \n, printing each line to the console
87 * with its usual prefix of date and sender; it uses the same behavior for the
88 * messages file as well. Further, syslog(3C) provides no CE_CONT equivalent
89 * for userland callers (which at least works around repeated file prefixing).
90 * So with a multi-line message format, your file and console end up like this:
92 * Dec 02 18:08:40 hostname this is my nicely formatted
93 * Dec 02 18:08:40 hostname message designed for 80 cols
96 * To resolve these issues, we use our own syslog_emit() wrapper to emit
97 * messages and some knowledge of how the Solaris log drivers work. We first
98 * construct an enlarged format string containing the appropriate msgid(1).
99 * We then format the caller's message using the provided format and buffer.
100 * We send this message to log(7D) using putmsg() with SL_CONSOLE | SL_LOGONLY
101 * set in the log_ctl_t. The log driver allows us to set SL_LOGONLY when we
102 * construct messages ourself, indicating that syslogd should only emit the
103 * message to /var/adm/messages and any remote hosts, and skip the console.
104 * Then we emit the message a second time, without the special prefix, to the
105 * sysmsg(7D) device, which handles console redirection and also permits us
106 * to output any characters we like to the console, including \n and \r.
109 syslog_emit(fmd_hdl_t
*hdl
, const char *msg
)
111 struct strbuf ctl
, dat
;
117 const char *format
= "fmd: [ID %u FACILITY_AND_PRIORITY] %s";
118 STRLOG_MAKE_MSGID(format
, msgid
);
120 buflen
= snprintf(NULL
, 0, format
, msgid
, msg
);
121 buf
= alloca(buflen
+ 1);
122 (void) snprintf(buf
, buflen
+ 1, format
, msgid
, msg
);
124 ctl
.buf
= (void *)&syslog_ctl
;
125 ctl
.len
= sizeof (syslog_ctl
);
128 dat
.len
= buflen
+ 1;
131 * The underlying log driver won't accept messages longer than
132 * LOG_MAXPS bytes. Therefore, messages which exceed this limit will
133 * be truncated and appended with a pointer to the full message.
135 if (dat
.len
> LOG_MAXPS
) {
136 char *syslog_pointer
, *p
;
139 if ((syslog_pointer
= fmd_msg_gettext_id(syslog_msghdl
, NULL
,
140 SYSLOG_POINTER
)) == NULL
) {
142 * This shouldn't happen, but if it does we'll just
143 * truncate the message.
145 buf
[LOG_MAXPS
- 1] = '\0';
148 plen
= strlen(syslog_pointer
) + 1;
149 buf
[LOG_MAXPS
- plen
] = '\0';
151 * If possible, the pointer is appended after a newline
153 if ((p
= strrchr(buf
, '\n')) == NULL
)
154 p
= &buf
[LOG_MAXPS
- plen
];
156 (void) strcpy(p
, syslog_pointer
);
157 free(syslog_pointer
);
158 dat
.len
= strlen(buf
) + 1;
161 if (syslog_file
&& putmsg(syslog_logfd
, &ctl
, &dat
, 0) != 0) {
162 fmd_hdl_debug(hdl
, "putmsg failed: %s\n", strerror(errno
));
163 syslog_stats
.log_err
.fmds_value
.ui64
++;
166 dat
.buf
= strchr(buf
, ']');
167 dat
.len
-= (size_t)(dat
.buf
- buf
);
169 dat
.buf
[0] = '\r'; /* overwrite ']' with carriage return */
170 dat
.buf
[1] = '\n'; /* overwrite ' ' with newline */
172 if (syslog_cons
&& write(syslog_msgfd
, dat
.buf
, dat
.len
) != dat
.len
) {
173 fmd_hdl_debug(hdl
, "write failed: %s\n", strerror(errno
));
174 syslog_stats
.msg_err
.fmds_value
.ui64
++;
179 free_notify_prefs(fmd_hdl_t
*hdl
, nvlist_t
**prefs
, uint_t nprefs
)
183 for (i
= 0; i
< nprefs
; i
++) {
184 nvlist_free(prefs
[i
]);
187 fmd_hdl_free(hdl
, prefs
, sizeof (nvlist_t
*) * nprefs
);
191 get_notify_prefs(fmd_hdl_t
*hdl
, nvlist_t
*ev_nvl
, nvlist_t
***pref_nvl
,
194 nvlist_t
*top_nvl
, **np_nvlarr
, *mech_nvl
;
197 uint_t nelem
, nslelem
;
199 if ((ret
= smf_notify_get_params(&top_nvl
, ev_nvl
)) != SCF_SUCCESS
) {
201 if (ret
!= SCF_ERROR_NOT_FOUND
) {
202 fmd_hdl_debug(hdl
, "Error looking up notification "
203 "preferences (%s)", scf_strerror(ret
));
209 if (nvlist_lookup_nvlist_array(top_nvl
, SCF_NOTIFY_PARAMS
, &np_nvlarr
,
211 fmd_hdl_debug(hdl
, "Malformed preference nvlist\n");
212 ret
= SCF_ERROR_INVALID_ARGUMENT
;
216 tmparr
= fmd_hdl_alloc(hdl
, nelem
* sizeof (nvlist_t
*), FMD_SLEEP
);
219 for (i
= 0; i
< nelem
; i
++) {
220 if (nvlist_lookup_nvlist(np_nvlarr
[i
], "syslog", &mech_nvl
)
222 tmparr
[nslelem
++] = fmd_nvl_dup(hdl
, mech_nvl
,
227 size_t sz
= nslelem
* sizeof (nvlist_t
*);
229 *pref_nvl
= fmd_hdl_zalloc(hdl
, sz
, FMD_SLEEP
);
231 bcopy(tmparr
, *pref_nvl
, sz
);
236 ret
= SCF_ERROR_NOT_FOUND
;
239 fmd_hdl_free(hdl
, tmparr
, nelem
* sizeof (nvlist_t
*));
241 nvlist_free(top_nvl
);
247 syslog_recv(fmd_hdl_t
*hdl
, fmd_event_t
*ep
, nvlist_t
*nvl
, const char *class)
250 boolean_t domsg
, *active
;
253 uint_t nprefs
, nelems
;
256 if (nvlist_lookup_uint8(nvl
, FM_VERSION
, &version
) != 0 ||
257 version
> FM_SUSPECT_VERSION
) {
258 fmd_hdl_debug(hdl
, "invalid event version: %u\n", version
);
259 syslog_stats
.bad_vers
.fmds_value
.ui64
++;
260 return; /* invalid event version */
263 if (!syslog_msgall
&& nvlist_lookup_boolean_value(nvl
,
264 FM_SUSPECT_MESSAGE
, &domsg
) == 0 && !domsg
) {
265 fmd_hdl_debug(hdl
, "%s requested no message\n", class);
266 syslog_stats
.no_msg
.fmds_value
.ui64
++;
267 return; /* event is not to be messaged */
270 ret
= get_notify_prefs(hdl
, nvl
, &prefs
, &nprefs
);
271 if (ret
== SCF_ERROR_NOT_FOUND
) {
273 * No syslog notification preferences specified for this type of
274 * event, so we're done
276 fmd_hdl_debug(hdl
, "No syslog notification preferences "
277 "configured for class %s\n", class);
278 syslog_stats
.no_msg
.fmds_value
.ui64
++;
280 } else if (ret
!= 0 || nvlist_lookup_boolean_array(prefs
[0], "active",
282 fmd_hdl_debug(hdl
, "Failed to retrieve notification "
283 "preferences for class %s\n", class);
285 free_notify_prefs(hdl
, prefs
, nprefs
);
287 } else if (!active
[0]) {
288 fmd_hdl_debug(hdl
, "Syslog notifications disabled for "
289 "class %s\n", class);
290 syslog_stats
.no_msg
.fmds_value
.ui64
++;
291 free_notify_prefs(hdl
, prefs
, nprefs
);
294 free_notify_prefs(hdl
, prefs
, nprefs
);
296 if ((msg
= fmd_msg_gettext_nv(syslog_msghdl
, NULL
, nvl
)) == NULL
) {
297 fmd_hdl_debug(hdl
, "failed to format message");
298 syslog_stats
.bad_code
.fmds_value
.ui64
++;
299 return; /* libfmd_msg error */
302 syslog_ctl
.pri
&= LOG_FACMASK
;
303 if (strcmp(class, FM_LIST_ISOLATED_CLASS
) == 0 ||
304 strcmp(class, FM_LIST_RESOLVED_CLASS
) == 0 ||
305 strcmp(class, FM_LIST_REPAIRED_CLASS
) == 0 ||
306 strcmp(class, FM_LIST_UPDATED_CLASS
) == 0)
307 syslog_ctl
.pri
|= LOG_NOTICE
;
309 syslog_ctl
.pri
|= LOG_ERR
;
311 syslog_emit(hdl
, msg
);
315 static const fmd_prop_t fmd_props
[] = {
316 { "console", FMD_TYPE_BOOL
, "true" },
317 { "facility", FMD_TYPE_STRING
, "LOG_DAEMON" },
318 { "gmt", FMD_TYPE_BOOL
, "false" },
319 { "syslogd", FMD_TYPE_BOOL
, "true" },
320 { "url", FMD_TYPE_STRING
, "http://illumos.org/msg/" },
321 { "message_all", FMD_TYPE_BOOL
, "false" },
325 static const fmd_hdl_ops_t fmd_ops
= {
326 syslog_recv
, /* fmdo_recv */
327 NULL
, /* fmdo_timeout */
328 NULL
, /* fmdo_close */
329 NULL
, /* fmdo_stats */
333 static const fmd_hdl_info_t fmd_info
= {
334 "Syslog Messaging Agent", "1.1", &fmd_ops
, fmd_props
338 _fmd_init(fmd_hdl_t
*hdl
)
340 const struct facility
*fp
;
341 char *facname
, *tz
, *rootdir
, *urlbase
;
343 if (fmd_hdl_register(hdl
, FMD_API_VERSION
, &fmd_info
) != 0)
344 return; /* invalid data in configuration file */
346 (void) fmd_stat_create(hdl
, FMD_STAT_NOALLOC
, sizeof (syslog_stats
) /
347 sizeof (fmd_stat_t
), (fmd_stat_t
*)&syslog_stats
);
349 if ((syslog_logfd
= open("/dev/conslog", O_WRONLY
| O_NOCTTY
)) == -1)
350 fmd_hdl_abort(hdl
, "syslog-msgs failed to open /dev/conslog");
352 if ((syslog_msgfd
= open("/dev/sysmsg", O_WRONLY
| O_NOCTTY
)) == -1)
353 fmd_hdl_abort(hdl
, "syslog-msgs failed to open /dev/sysmsg");
356 * If the "gmt" property is set to true, force our EVENT-TIME to be
357 * reported in GMT time; otherwise we use localtime. tzset() affects
358 * the results of subsequent calls to strftime(3C) above.
360 if (fmd_prop_get_int32(hdl
, "gmt") == FMD_B_TRUE
&&
361 ((tz
= getenv("TZ")) == NULL
|| strcmp(tz
, "GMT") != 0)) {
362 (void) putenv(fmd_hdl_strdup(hdl
, "TZ=GMT", FMD_SLEEP
));
363 tzset(); /* reload env */
367 * Look up the value of the "facility" property and use it to determine
368 * what syslog LOG_* facility value we use to fill in our log_ctl_t.
369 * The details of our logging method are described above syslog_emit().
371 facname
= fmd_prop_get_string(hdl
, "facility");
373 for (fp
= syslog_facs
; fp
->fac_name
!= NULL
; fp
++) {
374 if (strcmp(fp
->fac_name
, facname
) == 0)
378 if (fp
->fac_name
== NULL
)
379 fmd_hdl_abort(hdl
, "invalid 'facility' setting: %s\n", facname
);
381 fmd_prop_free_string(hdl
, facname
);
382 syslog_ctl
.pri
= fp
->fac_value
;
383 syslog_ctl
.flags
= SL_CONSOLE
| SL_LOGONLY
;
386 * Cache any properties we use every time we receive an event and
387 * subscribe to list.suspect events regardless of the .conf file.
389 syslog_file
= fmd_prop_get_int32(hdl
, "syslogd");
390 syslog_cons
= fmd_prop_get_int32(hdl
, "console");
391 syslog_msgall
= fmd_prop_get_int32(hdl
, "message_all");
393 rootdir
= fmd_prop_get_string(hdl
, "fmd.rootdir");
394 syslog_msghdl
= fmd_msg_init(rootdir
, FMD_MSG_VERSION
);
395 fmd_prop_free_string(hdl
, rootdir
);
397 if (syslog_msghdl
== NULL
)
398 fmd_hdl_abort(hdl
, "failed to initialize libfmd_msg");
400 urlbase
= fmd_prop_get_string(hdl
, "url");
401 (void) fmd_msg_url_set(syslog_msghdl
, urlbase
);
402 fmd_prop_free_string(hdl
, urlbase
);
405 * We subscribe to all FM events and then consult the notification
406 * preferences in the serice configuration repo to determine whether
407 * or not to emit a console message.
409 fmd_hdl_subscribe(hdl
, FM_LIST_SUSPECT_CLASS
);
410 fmd_hdl_subscribe(hdl
, FM_LIST_REPAIRED_CLASS
);
411 fmd_hdl_subscribe(hdl
, FM_LIST_RESOLVED_CLASS
);
412 fmd_hdl_subscribe(hdl
, FM_LIST_ISOLATED_CLASS
);
413 fmd_hdl_subscribe(hdl
, FM_LIST_UPDATED_CLASS
);
418 _fmd_fini(fmd_hdl_t
*hdl
)
420 fmd_msg_fini(syslog_msghdl
);
421 (void) close(syslog_logfd
);
422 (void) close(syslog_msgfd
);