source: mainline/uspace/lib/c/generic/io/log.c

Last change on this file was 01900b6, checked in by Martin Decky <martin@…>, 5 years ago

Use an optional output argument instead of errno to propagate the error

The use of errno is troublesome in all other than top-level library
functions since the value in errno might get overwritten by subsequent
inner calls on the error path (e.g. cleanup, deallocation, etc.). The
optional output argument makes it possible to explicitly ignore the
error code if it is not needed, but still to pass it reliably back to
the original caller.

This change affecs async_connect_me_to(),
async_connect_me_to_blocking(), async_connect_kbox(), service_connect(),
service_connect_blocking() and loader_connect().

  • Property mode set to 100644
File size: 6.8 KB
Line 
1/*
2 * Copyright (c) 2011 Vojtech Horky
3 * Copyright (c) 2011 Jiri Svoboda
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup libc
31 * @{
32 */
33
34#include <assert.h>
35#include <errno.h>
36#include <fibril_synch.h>
37#include <stdarg.h>
38#include <stdlib.h>
39#include <stdio.h>
40#include <async.h>
41#include <io/log.h>
42#include <ipc/logger.h>
43#include <str.h>
44#include <ns.h>
45
46/** Id of the first log we create at logger. */
47static sysarg_t default_log_id;
48
49/** Log messages are printed under this name. */
50static const char *log_prog_name;
51
52/** Names of individual log levels. */
53static const char *log_level_names[] = {
54 "fatal",
55 "error",
56 "warn",
57 "note",
58 "debug",
59 "debug2",
60 NULL
61};
62
63/** IPC session with the logger service. */
64static async_sess_t *logger_session;
65
66/** Maximum length of a single log message (in bytes). */
67#define MESSAGE_BUFFER_SIZE 4096
68
69/** Send formatted message to the logger service.
70 *
71 * @param session Initialized IPC session with the logger.
72 * @param log Log to use.
73 * @param level Verbosity level of the message.
74 * @param message The actual message.
75 * @return Error code of the conversion or EOK on success.
76 */
77static errno_t logger_message(async_sess_t *session, log_t log, log_level_t level, char *message)
78{
79 async_exch_t *exchange = async_exchange_begin(session);
80 if (exchange == NULL) {
81 return ENOMEM;
82 }
83 if (log == LOG_DEFAULT)
84 log = default_log_id;
85
86 // FIXME: remove when all USB drivers use libc logging explicitly
87 str_rtrim(message, '\n');
88
89 aid_t reg_msg = async_send_2(exchange, LOGGER_WRITER_MESSAGE,
90 log, level, NULL);
91 errno_t rc = async_data_write_start(exchange, message, str_size(message));
92 errno_t reg_msg_rc;
93 async_wait_for(reg_msg, &reg_msg_rc);
94
95 async_exchange_end(exchange);
96
97 /*
98 * Getting ENAK means no-one wants our message. That is not an
99 * error at all.
100 */
101 if (rc == ENAK)
102 rc = EOK;
103
104 if (rc != EOK) {
105 return rc;
106 }
107
108 return reg_msg_rc;
109}
110
111/** Get name of the log level.
112 *
113 * @param level The log level.
114 * @return String name or "unknown".
115 */
116const char *log_level_str(log_level_t level)
117{
118 if (level >= LVL_LIMIT)
119 return "unknown";
120 else
121 return log_level_names[level];
122}
123
124/** Convert log level name to the enum.
125 *
126 * @param[in] name Log level name or log level number.
127 * @param[out] level_out Where to store the result (set to NULL to ignore).
128 * @return Error code of the conversion or EOK on success.
129 */
130errno_t log_level_from_str(const char *name, log_level_t *level_out)
131{
132 log_level_t level = LVL_FATAL;
133
134 while (log_level_names[level] != NULL) {
135 if (str_cmp(name, log_level_names[level]) == 0) {
136 if (level_out != NULL)
137 *level_out = level;
138 return EOK;
139 }
140 level++;
141 }
142
143 /* Maybe user specified number directly. */
144 char *end_ptr;
145 int level_int = strtol(name, &end_ptr, 0);
146 if ((end_ptr == name) || (str_length(end_ptr) != 0))
147 return EINVAL;
148 if (level_int < 0)
149 return ERANGE;
150 if (level_int >= (int) LVL_LIMIT)
151 return ERANGE;
152
153 if (level_out != NULL)
154 *level_out = (log_level_t) level_int;
155
156 return EOK;
157}
158
159/** Initialize the logging system.
160 *
161 * @param prog_name Program name, will be printed as part of message
162 */
163errno_t log_init(const char *prog_name)
164{
165 log_prog_name = str_dup(prog_name);
166 if (log_prog_name == NULL)
167 return ENOMEM;
168
169 errno_t rc;
170 logger_session = service_connect_blocking(SERVICE_LOGGER,
171 INTERFACE_LOGGER_WRITER, 0, &rc);
172 if (logger_session == NULL)
173 return rc;
174
175 default_log_id = log_create(prog_name, LOG_NO_PARENT);
176
177 return EOK;
178}
179
180/** Create a new (sub-) log.
181 *
182 * This function always returns a valid log_t. In case of errors,
183 * @c parent is returned and errors are silently ignored.
184 *
185 * @param name Log name under which message will be reported (appended to parents name).
186 * @param parent Parent log.
187 * @return Opaque identifier of the newly created log.
188 */
189log_t log_create(const char *name, log_t parent)
190{
191 async_exch_t *exchange = async_exchange_begin(logger_session);
192 if (exchange == NULL)
193 return parent;
194
195 if (parent == LOG_DEFAULT)
196 parent = default_log_id;
197
198 ipc_call_t answer;
199 aid_t reg_msg = async_send_1(exchange, LOGGER_WRITER_CREATE_LOG,
200 parent, &answer);
201 errno_t rc = async_data_write_start(exchange, name, str_size(name));
202 errno_t reg_msg_rc;
203 async_wait_for(reg_msg, &reg_msg_rc);
204
205 async_exchange_end(exchange);
206
207 if ((rc != EOK) || (reg_msg_rc != EOK))
208 return parent;
209
210 return ipc_get_arg1(&answer);
211}
212
213/** Write an entry to the log.
214 *
215 * The message is printed only if the verbosity level is less than or
216 * equal to currently set reporting level of the log.
217 *
218 * @param ctx Log to use (use LOG_DEFAULT if you have no idea what it means).
219 * @param level Severity level of the message.
220 * @param fmt Format string in printf-like format (without trailing newline).
221 */
222void log_msg(log_t ctx, log_level_t level, const char *fmt, ...)
223{
224 va_list args;
225
226 va_start(args, fmt);
227 log_msgv(ctx, level, fmt, args);
228 va_end(args);
229}
230
231/** Write an entry to the log (va_list variant).
232 *
233 * @param ctx Log to use (use LOG_DEFAULT if you have no idea what it means).
234 * @param level Severity level of the message.
235 * @param fmt Format string in printf-like format (without trailing newline).
236 * @param args Arguments.
237 */
238void log_msgv(log_t ctx, log_level_t level, const char *fmt, va_list args)
239{
240 assert(level < LVL_LIMIT);
241
242 char *message_buffer = malloc(MESSAGE_BUFFER_SIZE);
243 if (message_buffer == NULL)
244 return;
245
246 vsnprintf(message_buffer, MESSAGE_BUFFER_SIZE, fmt, args);
247 logger_message(logger_session, ctx, level, message_buffer);
248 free(message_buffer);
249}
250
251/** @}
252 */
Note: See TracBrowser for help on using the repository browser.