1
\$\begingroup\$

I created a daemon that creates a fifo file with a default name, then is been blocked waiting for messages on that fifo. When an interactive process writes data to that fifo, the daemon wakes up and writes the received data to a journal file along with the time when it was written.

The question is whether it is considered a daemon if it runs in the background and other commands can be executed in the terminal? Does a daemon have to have a control terminal? What else could I improve or what problems does implementation have?

code:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include "lab.h"

void printTime(int fd)
{
    time_t t = time(NULL);
    struct tm tm = *localtime(&t);
    char s[100];
    snprintf(s, 100, "%d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
    if (write(fd, s, strlen(s)) < 0)
    {
        printf("Writing error\n");
    }
}

int main()
{
    int ff;
    char c;
    int sem = 0;
    int fd;
    if (mkfifo("file_fifo", 0660) < 0)
    {
        printf("Existing pipes\n");
    }

    if (creat("journal.txt", 0777) < 0)
    {
        printf("File exist\n");
    }
    ff = open("file_fifo", O_RDWR);
    while (1)
    {
        if (read(ff, &c, 1) < 0)
        {
            printf("Error reading\n");
        }
        if (sem == 0)
        {
            fd = open("journal.txt", O_WRONLY | O_APPEND, 0777);
            printTime(fd);
            sem = 1;
        }
        if (write(fd, &c, 1) < 0)
        {
            printf("Error reading\n");
        }

        if (c == '\n')
        {
            if (write(fd, &c, 1) < 0)
            {
                printf("Error writing\n");
            }
            close(fd);
            sem = 0;
        }
    }

    unlink("file_fifo");
    exit(0);
}

lab.h content:

/* Our own header, to be included *after* all standard system headers */

#ifndef __ourhdr_h
#define __ourhdr_h

#include    <sys/types.h>   /* required for some of our prototypes */
#include    <stdio.h>       /* for convenience */
#include    <stdlib.h>      /* for convenience */
#include    <string.h>      /* for convenience */
#include    <unistd.h>      /* for convenience */

#define MAXLINE 4096            /* max line length */

#define FILE_MODE   (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
                    /* default file access permissions for new files */
#define DIR_MODE    (FILE_MODE | S_IXUSR | S_IXGRP | S_IXOTH)
                    /* default permissions for new directories */

typedef void    Sigfunc(int);   /* for signal handlers */

                    /* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */
#if defined(SIG_IGN) && !defined(SIG_ERR)
#define SIG_ERR ((Sigfunc *)-1)
#endif

#define min(a,b)    ((a) < (b) ? (a) : (b))
#define max(a,b)    ((a) > (b) ? (a) : (b))

                    /* prototypes for our own functions */
char    *path_alloc(int *);         /* {Prog pathalloc} */
int      open_max(void);            /* {Prog openmax} */
void     clr_fl(int, int);          /* {Prog setfl} */
void     set_fl(int, int);          /* {Prog setfl} */
void     pr_exit(int);              /* {Prog prexit} */
void     pr_mask(const char *);     /* {Prog prmask} */
Sigfunc *signal_intr(int, Sigfunc *);/* {Prog signal_intr_function} */

int      tty_cbreak(int);           /* {Prog raw} */
int      tty_raw(int);              /* {Prog raw} */
int      tty_reset(int);            /* {Prog raw} */
void     tty_atexit(void);          /* {Prog raw} */
#ifdef  ECHO    /* only if <termios.h> has been included */
struct termios  *tty_termios(void); /* {Prog raw} */
#endif

void     sleep_us(unsigned int);    /* {Ex sleepus} */
ssize_t  readn(int, void *, size_t);/* {Prog readn} */
ssize_t  writen(int, const void *, size_t);/* {Prog writen} */
int      daemon_init(void);         /* {Prog daemoninit} */

int      s_pipe(int *);             /* {Progs svr4_spipe bsd_spipe} */
int      recv_fd(int, ssize_t (*func)(int, const void *, size_t));
                                    /* {Progs recvfd_svr4 recvfd_43bsd} */
int      send_fd(int, int);         /* {Progs sendfd_svr4 sendfd_43bsd} */
int      send_err(int, int, const char *);/* {Prog senderr} */
int      serv_listen(const char *); /* {Progs servlisten_svr4 servlisten_44bsd} */
int      serv_accept(int, uid_t *); /* {Progs servaccept_svr4 servaccept_44bsd} */
int      cli_conn(const char *);    /* {Progs cliconn_svr4 cliconn_44bsd} */
int      buf_args(char *, int (*func)(int, char **));
                                    /* {Prog bufargs} */

int      ptym_open(char *);         /* {Progs ptyopen_svr4 ptyopen_44bsd} */
int      ptys_open(int, char *);    /* {Progs ptyopen_svr4 ptyopen_44bsd} */
#ifdef  TIOCGWINSZ
pid_t    pty_fork(int *, char *, const struct termios *,
                  const struct winsize *);  /* {Prog ptyfork} */
#endif

int     lock_reg(int, int, int, off_t, int, off_t);
                                    /* {Prog lockreg} */
#define read_lock(fd, offset, whence, len) \
            lock_reg(fd, F_SETLK, F_RDLCK, offset, whence, len)
#define readw_lock(fd, offset, whence, len) \
            lock_reg(fd, F_SETLKW, F_RDLCK, offset, whence, len)
#define write_lock(fd, offset, whence, len) \
            lock_reg(fd, F_SETLK, F_WRLCK, offset, whence, len)
#define writew_lock(fd, offset, whence, len) \
            lock_reg(fd, F_SETLKW, F_WRLCK, offset, whence, len)
#define un_lock(fd, offset, whence, len) \
            lock_reg(fd, F_SETLK, F_UNLCK, offset, whence, len)

pid_t   lock_test(int, int, off_t, int, off_t);
                                    /* {Prog locktest} */

#define is_readlock(fd, offset, whence, len) \
            lock_test(fd, F_RDLCK, offset, whence, len)
#define is_writelock(fd, offset, whence, len) \
            lock_test(fd, F_WRLCK, offset, whence, len)

void    err_init(const char *);

void    err_dump(const char *, ...);    /* {App misc_source} */
void    err_msg(const char *, ...);
void    err_quit(const char *, ...);
void    err_ret(const char *, ...);
void    err_sys(const char *, ...);

void    log_msg(const char *, ...);     /* {App misc_source} */
void    log_open(const char *, int, int);
void    log_quit(const char *, ...);
void    log_ret(const char *, ...);
void    log_sys(const char *, ...);

void    TELL_WAIT(void);        /* parent/child from {Sec race_conditions} */
void    TELL_PARENT(pid_t);
void    TELL_CHILD(pid_t);
void    WAIT_PARENT(void);
void    WAIT_CHILD(void);

#endif  /* __ourhdr_h */
\$\endgroup\$
4
  • \$\begingroup\$ Usually, we expect a daemon to not have a controlling tty. That's why we close fds and double-fork. These days, we can achieve that externally using helpers such as daemonize rather than having to re-implement in every daemon we write. \$\endgroup\$ Commented Feb 22, 2022 at 14:31
  • 2
    \$\begingroup\$ What does the daemon actually do? That should be the title. I'd edit it myself, but I'm not sure - is it an "echo" server? \$\endgroup\$ Commented Feb 22, 2022 at 14:33
  • 1
    \$\begingroup\$ I just exemplified a pipe to a daemon. No, is not a echo server. I just create a daemon that creates a fifo file. \$\endgroup\$
    – Mark
    Commented Feb 22, 2022 at 19:44
  • \$\begingroup\$ Unfortunately my copy of en.wikipedia.org/wiki/… is in the office I haven't visited for 2 years. If you're serious about doing this sort of thing, you could do worse than to get hold of a copy. All the details of how to write a daemon and why the various steps are needed , together with so much more. And Richard Stevens was an exemplary technical author. \$\endgroup\$ Commented Feb 26, 2022 at 15:53

2 Answers 2

3
\$\begingroup\$

Traditionally, UNIX daemons would not have a controlling terminal, and would detach themselves from one if they had it. This prevents it from receiving signals from that tty, such as SIGHUP.

Daemons would traditionally also change their working directory to /, so as not to prevent the sysadmin from unmounting a filesystem. This is less important for your example, since this daemon manages a FIFO on a mounted filesystem.

\$\endgroup\$
3
\$\begingroup\$

All of your questions to date use the exit(status) function to terminate the program. The general practice in C programming is to return success or failure of the program by using a return statement in the main() function. There is no reason to call the exit() function. The exit() function should be used in functions from called by main() when the program encounters a situation where termination must occur.

The file stdlib.h includes 2 system defined constants, EXIT_SUCCESS and EXIT_FAILURE which will help with writing self documenting code and should be used instead of return 0 or return 1 from or exit(1) from functions that are not main.

int main(int argc, char *argv[])
{
    ...
    if (`ERROR`)
    {
        fprintf(stderr, "Error Message\n");
        return EXIT_FAILURE;
    }
    else
    {
        return EXIT_SUCCESS;
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.