• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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