1 /****************************************************************************
2 * fs/driver/fs_devsyslog.c
3 *
4 * Copyright (C) 2012 Gregory Nutt. All rights reserved.
5 * Author: Gregory Nutt <gnutt@nuttx.org>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name NuttX nor the names of its contributors may be
18 * used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 ****************************************************************************/
35
36 /****************************************************************************
37 * Included Files
38 ****************************************************************************/
39
40 #include "los_task.h"
41 #include "vfs_config.h"
42 #include "sys/types.h"
43 #include "stdint.h"
44 #include "stdio.h"
45 #include "unistd.h"
46 #include "fcntl.h"
47 #include "semaphore.h"
48 #include "assert.h"
49 #include "fs/driver.h"
50 #include "inode/inode.h"
51
52 #if defined(CONFIG_SYSLOG) && defined(CONFIG_SYSLOG_CHAR)
53
54 /****************************************************************************
55 * Pre-processor Definitions
56 ****************************************************************************/
57
58 /* Open the device/file write-only, try to create (file) it if it doesn't
59 * exist, if the file that already exists, then append the new log data to
60 * end of the file.
61 */
62
63 #define SYSLOG_OFLAGS (O_WRONLY | O_CREAT | O_APPEND)
64
65 /* An invalid thread ID */
66
67 #define NO_HOLDER ((pid_t)-1)
68
69 /****************************************************************************
70 * Private Types
71 ****************************************************************************/
72
73 /* This enumeration represents the state of the SYSLOG device interface */
74
75 enum syslog_state_e
76 {
77 SYSLOG_UNINITIALIZED = 0, /* SYSLOG has not been initialized */
78 SYSLOG_INITIALIZING, /* SYSLOG is being initialized */
79 SYSLOG_REOPEN, /* SYSLOG open failed... try again later */
80 SYSLOG_FAILURE, /* SYSLOG open failed... don't try again */
81 SYSLOG_OPENED, /* SYSLOG device is open and ready to use */
82 };
83
84 /* This structure contains all SYSLOGing state information */
85
86 struct syslog_dev_s
87 {
88 uint8_t sl_state; /* See enum syslog_state_e */
89 sem_t sl_sem; /* Enforces mutually exclusive access */
90 pid_t sl_holder; /* PID of the thread that holds the semaphore */
91 struct file sl_file; /* The syslog file structure */
92 };
93
94 /****************************************************************************
95 * Private Function Prototypes
96 ****************************************************************************/
97
98 /****************************************************************************
99 * Private Data
100 ****************************************************************************/
101
102 /* This is the device structure for the console or syslogging function. */
103
104 static struct syslog_dev_s g_sysdev;
105 static const uint8_t g_syscrlf[2] =
106 {
107 '\r', '\n'
108 };
109
110 /****************************************************************************
111 * Private Functions
112 ****************************************************************************/
113
114 /****************************************************************************
115 * Name: syslog_takesem
116 *
117 * Description:
118 * Write to the syslog device
119 *
120 ****************************************************************************/
121
syslog_takesem(void)122 static inline int syslog_takesem(void)
123 {
124 pid_t me = getpid();
125 int ret;
126
127 /* Does this thread already hold the semaphore? That could happen if
128 * we wer called recursively, i.e., if the logic kicked off by
129 * syslog_write() where to generate more debug output. Return an error
130 * in that case.
131 */
132
133 if (g_sysdev.sl_holder == me)
134 {
135 /* Return an error (instead of deadlocking) */
136
137 return -EWOULDBLOCK;
138 }
139
140 /* Either the semaphore is available or is currently held by another
141 * thread. Wait for it to become available.
142 */
143
144 ret = sem_wait(&g_sysdev.sl_sem);
145 if (ret < 0)
146 {
147 return -get_errno();
148 }
149
150 /* We hold the semaphore. We can safely mark ourself as the holder
151 * of the semaphore.
152 */
153
154 g_sysdev.sl_holder = me;
155 return OK;
156 }
157
158 /****************************************************************************
159 * Name: syslog_givesem
160 *
161 * Description:
162 * Write to the syslog device
163 *
164 ****************************************************************************/
165
syslog_givesem(void)166 static inline void syslog_givesem(void)
167 {
168 #ifdef CONFIG_DEBUG
169 pid_t me = getpid();
170 DEBUGASSERT(g_sysdev.sl_holder == me);
171 #endif
172
173 /* Relinquish the semaphore */
174
175 g_sysdev.sl_holder = NO_HOLDER;
176 (void)sem_post(&g_sysdev.sl_sem);
177 }
178
179 /****************************************************************************
180 * Name: syslog_write
181 *
182 * Description:
183 * Write to the syslog device
184 *
185 ****************************************************************************/
186
syslog_write(const void * buf,size_t nbytes)187 static inline ssize_t syslog_write(const void *buf, size_t nbytes)
188 {
189 struct inode *inode_ptr;
190
191 /* Let the driver perform the write */
192
193 inode_ptr = g_sysdev.sl_file.f_inode;
194 return inode_ptr->u.i_ops->write(&g_sysdev.sl_file, (const char *)buf, nbytes);
195 }
196
197 /****************************************************************************
198 * Name: syslog_flush
199 *
200 * Description:
201 * Flush any buffer data in the file system to media.
202 *
203 ****************************************************************************/
204
205 #ifndef CONFIG_DISABLE_MOUNTPOINT
syslog_flush(void)206 static inline void syslog_flush(void)
207 {
208 struct inode *inode_ptr = g_sysdev.sl_file.f_inode;
209
210 /* Is this a mountpoint? Does it support the sync method? */
211
212 if (INODE_IS_MOUNTPT(inode_ptr) && inode_ptr->u.i_mops->sync)
213 {
214 /* Yes... synchronize to the stream */
215
216 (void)inode_ptr->u.i_mops->sync(&g_sysdev.sl_file);
217 }
218 }
219 #endif
220
221 /****************************************************************************
222 * Public Functions
223 ****************************************************************************/
224
225 /****************************************************************************
226 * Name: syslog_initialize
227 *
228 * Description:
229 * Initialize to use the character device (or file) at
230 * CONFIG_SYSLOG_DEVPATH as the SYSLOG sink.
231 *
232 * NOTE that this implementation excludes using a network connection as
233 * SYSLOG device. That would be a good extension.
234 *
235 ****************************************************************************/
236
syslog_initialize(void)237 int syslog_initialize(void)
238 {
239 struct inode *inode_ptr;
240 const char *relpath = NULL;
241 int ret;
242 struct inode_search_s desc;
243
244 /* At this point, the only expected states are SYSLOG_UNINITIALIZED or
245 * SYSLOG_REOPEN.. Not SYSLOG_INITIALIZING, SYSLOG_FAILURE, SYSLOG_OPENED.
246 */
247
248 DEBUGASSERT(g_sysdev.sl_state == SYSLOG_UNINITIALIZED ||
249 g_sysdev.sl_state == SYSLOG_REOPEN);
250
251 g_sysdev.sl_state = SYSLOG_INITIALIZING;
252
253 /* Try to open the device.
254 *
255 * Note that we cannot just call open. The syslog device must work on all
256 * threads. Open returns a file descriptor that is valid only for the
257 * task that opened the device (and its pthread children). Instead, we
258 * essentially re-implement the guts of open() here so that we can get to
259 * the thread-independent structures of the inode.
260 */
261
262 /* Get an inode for this file/device */
263
264 SETUP_SEARCH(&desc, CONFIG_SYSLOG_DEVPATH, false);
265 ret = inode_find(&desc);
266
267 /* Get the search results */
268
269 if (ret < 0)
270 {
271 /* The inode was not found. In this case, we will attempt to re-open
272 * the device repeatedly. The assumption is that the device path is
273 * valid but that the driver has not yet been registered.
274 */
275
276 g_sysdev.sl_state = SYSLOG_REOPEN;
277 return -EACCES;
278 }
279 inode_ptr = desc.node;
280 relpath = desc.relpath;
281
282 /* Verify that the inode is valid and either a character driver or a
283 * mountpoint.
284 */
285
286 #ifndef CONFIG_DISABLE_MOUNTPOINT
287 if ((!INODE_IS_DRIVER(inode_ptr) && !INODE_IS_MOUNTPT(inode_ptr)))
288 #else
289 if (!INODE_IS_DRIVER(inode_ptr))
290 #endif
291 {
292 ret = -ENXIO;
293 goto errout_with_inode;
294 }
295
296 /* Make sure that the "entity" at this inode supports write access */
297
298 if (!inode_ptr->u.i_ops || !inode_ptr->u.i_ops->write)
299 {
300 ret = -EACCES;
301 goto errout_with_inode;
302 }
303
304 /* Initialize the file structure */
305
306 g_sysdev.sl_file.f_oflags = SYSLOG_OFLAGS;
307 g_sysdev.sl_file.f_pos = 0;
308 g_sysdev.sl_file.f_inode = inode_ptr;
309
310 /* Perform the low-level open operation. */
311
312 ret = OK;
313 if (inode_ptr->u.i_ops->open)
314 {
315 /* Is the inode a mountpoint? */
316
317 #ifndef CONFIG_DISABLE_MOUNTPOINT
318 if (INODE_IS_MOUNTPT(inode_ptr))
319 {
320 /* Yes. Open the device write-only, try to create it if it
321 * doesn't exist, if the file that already exists, then append the
322 * new log data to end of the file.
323 */
324
325 ret = inode_ptr->u.i_mops->open(&g_sysdev.sl_file, relpath,
326 SYSLOG_OFLAGS, 0666);
327 }
328
329 /* No... then it must be a character driver in the NuttX pseudo-
330 * file system.
331 */
332
333 else
334 #endif
335 {
336 ret = inode_ptr->u.i_ops->open(&g_sysdev.sl_file);
337 }
338 }
339
340 /* Was the file/device successfully opened? */
341
342 if (ret < 0)
343 {
344 ret = -ret;
345 goto errout_with_inode;
346 }
347
348 /* The SYSLOG device is open and ready for writing. */
349
350 (void)sem_init(&g_sysdev.sl_sem, 0, 1);
351 g_sysdev.sl_holder = NO_HOLDER;
352 g_sysdev.sl_state = SYSLOG_OPENED;
353 return OK;
354
355 errout_with_inode:
356 inode_release(inode_ptr);
357 g_sysdev.sl_state = SYSLOG_FAILURE;
358 return ret;
359 }
360
361 /****************************************************************************
362 * Name: syslog_putc
363 *
364 * Description:
365 * This is the low-level system logging interface. The debugging/syslogging
366 * interfaces are syslog() and lowsyslog(). The difference is is that
367 * the syslog() function writes to syslogging device (usually fd=1, stdout)
368 * whereas lowsyslog() uses a lower level interface that works from
369 * interrupt handlers. This function is a a low-level interface used to
370 * implement lowsyslog().
371 *
372 ****************************************************************************/
373
syslog_putc(int ch)374 int syslog_putc(int ch)
375 {
376 ssize_t nbytes;
377 uint8_t uch;
378 int errcode;
379 int ret;
380
381 /* Ignore any output:
382 *
383 * (1) Before the SYSLOG device has been initialized. This could happen
384 * from debug output that occurs early in the boot sequence before
385 * syslog_initialize() is called (SYSLOG_UNINITIALIZED).
386 * (2) While the device is being initialized. The case could happen if
387 * debug output is generated while syslog_initialize() executes
388 * (SYSLOG_INITIALIZING).
389 * (3) While we are generating SYSLOG output. The case could happen if
390 * debug output is generated while syslog_putc() executes
391 * (This case is actually handled inside of syslog_semtake()).
392 * (4) Any debug output generated from interrupt handlers. A disadvantage
393 * of using the generic character device for the SYSLOG is that it
394 * cannot handle debug output generated from interrupt level handlers.
395 * (5) Any debug output generated from the IDLE loop. The character
396 * driver interface is blocking and the IDLE thread is not permitted
397 * to block.
398 * (6) If an irrecoverable failure occurred during initialization. In
399 * this case, we won't ever bother to try again (ever).
400 *
401 * NOTE: That the third case is different. It applies only to the thread
402 * that currently holds the sl_sem sempaphore. Other threads should wait.
403 * that is why that case is handled in syslog_semtake().
404 */
405
406 /* Cases (4) and (5) */
407
408 if (OS_INT_ACTIVE || getpid() == 0)
409 {
410 errcode = ENOSYS;
411 goto errout_with_errcode;
412 }
413
414 /* We can save checks in the usual case: That after the SYSLOG device
415 * has been successfully opened.
416 */
417
418 if (g_sysdev.sl_state != SYSLOG_OPENED)
419 {
420 /* Case (1) and (2) */
421
422 if (g_sysdev.sl_state == SYSLOG_UNINITIALIZED ||
423 g_sysdev.sl_state == SYSLOG_INITIALIZING)
424 {
425 errcode = EAGAIN; /* Can't access the SYSLOG now... maybe next time? */
426 goto errout_with_errcode;
427 }
428
429 /* Case (6) */
430
431 if (g_sysdev.sl_state == SYSLOG_FAILURE)
432 {
433 errcode = ENXIO; /* There is no SYSLOG device */
434 goto errout_with_errcode;
435 }
436
437 /* syslog_initialize() is called as soon as enough of the operating
438 * system is in place to support the open operation... but it is
439 * possible that the SYSLOG device is not yet registered at that time.
440 * In this case, we know that the system is sufficiently initialized
441 * to support an attempt to re-open the SYSLOG device.
442 *
443 * NOTE that the scheduler is locked. That is because we do not have
444 * fully initialized semaphore capability until the SYSLOG device is
445 * successfully initialized
446 */
447
448 LOS_TaskLock();
449 if (g_sysdev.sl_state == SYSLOG_REOPEN)
450 {
451 /* Try again to initialize the device. We may do this repeatedly
452 * because the log device might be something that was not ready
453 * the first time that syslog_initializee() was called (such as a
454 * USB serial device that has not yet been connected or a file in
455 * an NFS mounted file system that has not yet been mounted).
456 */
457
458 ret = syslog_initialize();
459 if (ret < 0)
460 {
461 LOS_TaskUnlock();
462 errcode = -ret;
463 goto errout_with_errcode;
464 }
465 }
466
467 LOS_TaskUnlock();
468 DEBUGASSERT(g_sysdev.sl_state == SYSLOG_OPENED);
469 }
470
471 /* Ignore carriage returns */
472
473 if (ch == '\r')
474 {
475 return ch;
476 }
477
478 /* The syslog device is ready for writing and we have something of
479 * value to write.
480 */
481
482 ret = syslog_takesem();
483 if (ret < 0)
484 {
485 /* We probably already hold the semaphore and were probably
486 * re-entered by the logic kicked off by syslog_write().
487 * We might also have been interrupted by a signal. Either
488 * way, we are outta here.
489 */
490
491 errcode = -ret;
492 goto errout_with_errcode;
493 }
494
495 /* Pre-pend a newline with a carriage return. */
496
497 if (ch == '\n')
498 {
499 /* Write the CR-LF sequence */
500
501 nbytes = syslog_write(g_syscrlf, 2);
502
503 /* Synchronize the file when each CR-LF is encountered (i.e.,
504 * implements line buffering always).
505 */
506
507 #ifndef CONFIG_DISABLE_MOUNTPOINT
508 if (nbytes > 0)
509 {
510 syslog_flush();
511 }
512 #endif
513 }
514 else
515 {
516 /* Write the non-newline character (and don't flush) */
517
518 uch = (uint8_t)ch;
519 nbytes = syslog_write(&uch, 1);
520 }
521
522 syslog_givesem();
523
524 /* Check if the write was successful. If not, nbytes will be
525 * a negated errno value.
526 */
527
528 if (nbytes < 0)
529 {
530 errcode = -ret;
531 goto errout_with_errcode;
532 }
533
534 return ch;
535
536 errout_with_errcode:
537 if (errcode != 0)
538 {
539 set_errno(errcode);
540 }
541 return EOF;
542 }
543
544 #endif /* CONFIG_SYSLOG && CONFIG_SYSLOG_CHAR */
545