• 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