Path: blob/main/sys/contrib/openzfs/cmd/zed/zed_log.c
48380 views
// SPDX-License-Identifier: CDDL-1.01/*2* This file is part of the ZFS Event Daemon (ZED).3*4* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).5* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.6* Refer to the OpenZFS git commit log for authoritative copyright attribution.7*8* The contents of this file are subject to the terms of the9* Common Development and Distribution License Version 1.0 (CDDL-1.0).10* You can obtain a copy of the license from the top-level file11* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.12* You may not use this file except in compliance with the license.13*/1415#include <assert.h>16#include <errno.h>17#include <stdarg.h>18#include <stdio.h>19#include <stdlib.h>20#include <string.h>21#include <sys/types.h>22#include <syslog.h>23#include <unistd.h>24#include "zed_log.h"2526#define ZED_LOG_MAX_LOG_LEN 10242728static struct {29unsigned do_stderr:1;30unsigned do_syslog:1;31const char *identity;32int priority;33int pipe_fd[2];34} _ctx;3536/*37* Initialize the logging subsystem.38*/39void40zed_log_init(const char *identity)41{42if (identity) {43const char *p = strrchr(identity, '/');44_ctx.identity = (p != NULL) ? p + 1 : identity;45} else {46_ctx.identity = NULL;47}48_ctx.pipe_fd[0] = -1;49_ctx.pipe_fd[1] = -1;50}5152/*53* Shutdown the logging subsystem.54*/55void56zed_log_fini(void)57{58zed_log_stderr_close();59zed_log_syslog_close();60}6162/*63* Create pipe for communicating daemonization status between the parent and64* child processes across the double-fork().65*/66void67zed_log_pipe_open(void)68{69if ((_ctx.pipe_fd[0] != -1) || (_ctx.pipe_fd[1] != -1))70zed_log_die("Invalid use of zed_log_pipe_open in PID %d",71(int)getpid());7273if (pipe(_ctx.pipe_fd) < 0)74zed_log_die("Failed to create daemonize pipe in PID %d: %s",75(int)getpid(), strerror(errno));76}7778/*79* Close the read-half of the daemonize pipe.80*81* This should be called by the child after fork()ing from the parent since82* the child will never read from this pipe.83*/84void85zed_log_pipe_close_reads(void)86{87if (_ctx.pipe_fd[0] < 0)88zed_log_die(89"Invalid use of zed_log_pipe_close_reads in PID %d",90(int)getpid());9192if (close(_ctx.pipe_fd[0]) < 0)93zed_log_die(94"Failed to close reads on daemonize pipe in PID %d: %s",95(int)getpid(), strerror(errno));9697_ctx.pipe_fd[0] = -1;98}99100/*101* Close the write-half of the daemonize pipe.102*103* This should be called by the parent after fork()ing its child since the104* parent will never write to this pipe.105*106* This should also be called by the child once initialization is complete107* in order to signal the parent that it can safely exit.108*/109void110zed_log_pipe_close_writes(void)111{112if (_ctx.pipe_fd[1] < 0)113zed_log_die(114"Invalid use of zed_log_pipe_close_writes in PID %d",115(int)getpid());116117if (close(_ctx.pipe_fd[1]) < 0)118zed_log_die(119"Failed to close writes on daemonize pipe in PID %d: %s",120(int)getpid(), strerror(errno));121122_ctx.pipe_fd[1] = -1;123}124125/*126* Block on reading from the daemonize pipe until signaled by the child127* (via zed_log_pipe_close_writes()) that initialization is complete.128*129* This should only be called by the parent while waiting to exit after130* fork()ing the child.131*/132void133zed_log_pipe_wait(void)134{135ssize_t n;136char c;137138if (_ctx.pipe_fd[0] < 0)139zed_log_die("Invalid use of zed_log_pipe_wait in PID %d",140(int)getpid());141142for (;;) {143n = read(_ctx.pipe_fd[0], &c, sizeof (c));144if (n < 0) {145if (errno == EINTR)146continue;147zed_log_die(148"Failed to read from daemonize pipe in PID %d: %s",149(int)getpid(), strerror(errno));150}151if (n == 0) {152break;153}154}155}156157/*158* Start logging messages at the syslog [priority] level or higher to stderr.159* Refer to syslog(3) for valid priority values.160*/161void162zed_log_stderr_open(int priority)163{164_ctx.do_stderr = 1;165_ctx.priority = priority;166}167168/*169* Stop logging messages to stderr.170*/171void172zed_log_stderr_close(void)173{174if (_ctx.do_stderr)175_ctx.do_stderr = 0;176}177178/*179* Start logging messages to syslog.180* Refer to syslog(3) for valid option/facility values.181*/182void183zed_log_syslog_open(int facility)184{185_ctx.do_syslog = 1;186openlog(_ctx.identity, LOG_NDELAY | LOG_PID, facility);187}188189/*190* Stop logging messages to syslog.191*/192void193zed_log_syslog_close(void)194{195if (_ctx.do_syslog) {196_ctx.do_syslog = 0;197closelog();198}199}200201/*202* Auxiliary function to log a message to syslog and/or stderr.203*/204static void205_zed_log_aux(int priority, const char *fmt, va_list vargs)206{207char buf[ZED_LOG_MAX_LOG_LEN];208int n;209210if (!fmt)211return;212213n = vsnprintf(buf, sizeof (buf), fmt, vargs);214if ((n < 0) || (n >= sizeof (buf))) {215buf[sizeof (buf) - 2] = '+';216buf[sizeof (buf) - 1] = '\0';217}218219if (_ctx.do_syslog)220syslog(priority, "%s", buf);221222if (_ctx.do_stderr && (priority <= _ctx.priority))223fprintf(stderr, "%s\n", buf);224}225226/*227* Log a message at the given [priority] level specified by the printf-style228* format string [fmt].229*/230void231zed_log_msg(int priority, const char *fmt, ...)232{233va_list vargs;234235if (fmt) {236va_start(vargs, fmt);237_zed_log_aux(priority, fmt, vargs);238va_end(vargs);239}240}241242/*243* Log a fatal error message specified by the printf-style format string [fmt].244*/245void246zed_log_die(const char *fmt, ...)247{248va_list vargs;249250if (fmt) {251va_start(vargs, fmt);252_zed_log_aux(LOG_ERR, fmt, vargs);253va_end(vargs);254}255exit(EXIT_FAILURE);256}257258259