Subversion Repositories mdb

Rev

Rev 59 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * Copyright (C) 2015 by Andreas Theofilu <andreas@theosys.at>
 *
 * All rights reserved. No warranty, explicit or implicit, provided.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Andreas Theofilu and his suppliers, if any.
 * The intellectual and technical concepts contained
 * herein are proprietary to Andreas Theofilu and its suppliers and
 * may be covered by European and Foreign Patents, patents in process,
 * and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Andreas Theofilu.
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <libgen.h>
#include <math.h>
#include <time.h>
#include <signal.h>
#ifndef __APPLE__
#include <wait.h>
#endif
#include <syslog.h>
#include <errno.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h>
#include <dirent.h>
#include <netinet/in.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <pwd.h>
#include <grp.h>
#include "config.h"
#include "helplib.h"
#include "mdb.h"
#include "play.h"

#ifndef SIGCLD
#   define SIGCLD SIGCHLD
#endif

static pthread_t pthr_pars, pthr_cmds;
struct SOCKETS soc;

/* Prototypes */
void daemon_start(int ignsigcld);
void sig_child (int x);
void changeToUser(char *, char *);
void *pthr_parser(void *pV_data);
void *thread_wait(void *pV_data);

int main(int argc, char **argv)
{
        readConf();
        memset (&playCurrent, 0, sizeof(ST_PLAYING));
        /* Now daemonize this application */
        daemon_start(1);
        changeToUser(&configs.User[0], &configs.Grp[0]);
        handleInit();

        /* Prepare the thread attributes */
        if (pthread_attr_init(&pattr) != 0)
        {
                syslog(LOG_DAEMON,"Error getting thread attributes.");
                return FALSE;
        }
        
        if (pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED) != 0)
        {
                syslog(LOG_DAEMON,"Error setting thread attributes.");
                return FALSE;
        }

        /* Now start our Thread */
        if (pthread_create(&pthr_pars, &pattr, pthr_parser, (void *)0) != 0)
        {
                syslog (LOG_DAEMON, "Create of thread \"pthr_parser\" failed!");
                return 1;
        }

        while (1)
                sleep(3600);

        return 0;
}

/*
 * Detach application from console and make it a daemon.
 */
void daemon_start (int ignsigcld)
{
        int childpid, fd;
        char hv0[64];

        if (getpid () == 1)
                goto out;

#ifdef SIGTTOU
        signal (SIGTTOU, SIG_IGN);
#endif
#ifdef SIGTTIN
        signal (SIGTTIN, SIG_IGN);
#endif
#ifdef SIGTSTP
        signal (SIGTSTP, SIG_IGN);
#endif

        if ((childpid = fork ()) < 0)
                fprintf (stderr, "Can't fork this child\n");
        else if (childpid > 0)
                exit (0);            /* Parent */

        if (setpgrp () == -1)
                fprintf (stderr, "Can't change process group\n");

        signal (SIGHUP, SIG_IGN);

        if ((childpid = fork ()) < 0)
                syslog (LOG_DAEMON, "Can't fork second child");
        else if (childpid > 0)
                exit (0);            /* first child */

        /* second child */
out:

        for (fd = 0; fd < NOFILE; fd++)
                close (fd);

        errno = 0;
        chdir ("/");
        umask (0);
#ifdef SIGCLD
        if (ignsigcld)
                signal (SIGCLD, SIG_IGN);
        else
                signal(SIGCLD, &sig_child);
#endif
        /* Create PID file */
        if ((fd = open(configs.Pidfile, O_RDWR | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
        {
                syslog(LOG_WARNING, "Can't create PID file %s: %s", configs.Pidfile, strerror(errno));
                return;
        }

        sprintf(&hv0[0], "%d", getpid());
        write(fd, hv0, strlen(hv0));
        close(fd);
}

void sig_child (int x)
{
#if defined(BSD) && !defined(sinix) && !defined(Linux)
        int pid;
        int status;

        while ((pid = wait4 (0, &status, WNOHANG, (struct rusage *)0)) > 0)
                sleep(1);
#endif
}

void changeToUser(char *usr, char *grp)
{
        gid_t gr_gid;

        if (usr && strlen(usr))
        {
                /* get uid */
                struct passwd *userpwd;
                struct group *usergrp = NULL;

                if ((userpwd = getpwnam(usr)) == NULL)
                {
                        syslog(LOG_DAEMON, "no such user: %s", usr);
                        exit(EXIT_FAILURE);
                }

                if (grp != NULL && strlen(grp) && (usergrp = getgrnam(grp)) == NULL)
                {
                        if (grp && strlen(grp))
                                syslog(LOG_WARNING, "no such group: %s", grp);

                        gr_gid = userpwd->pw_gid;
                }
                else if (usergrp != NULL)
                        gr_gid = usergrp->gr_gid;
                else
                        gr_gid = userpwd->pw_gid;

                if (setgid(gr_gid) == -1)
                {
                        syslog(LOG_DAEMON, "cannot setgid of user %s: %s", usr, strerror(errno));
                        exit(EXIT_FAILURE);
                }

#ifdef _BSD_SOURCE

                /* init suplementary groups
                 * (must be done before we change our uid)
                 */
                if (initgroups(usr, gr_gid) == -1)
                        syslog(LOG_DAEMON, "cannot init suplementary groups of user %s: %s", usr, strerror(errno));

#endif

                /* set uid */
                if (setuid(userpwd->pw_uid) == -1)
                {
                        syslog(LOG_DAEMON, "cannot change to uid of user %s: %s\n", usr, strerror(errno));
                        exit(EXIT_FAILURE);
                }

                if (userpwd->pw_dir)
                        setenv("HOME", userpwd->pw_dir, 1);
        }
}

void *pthr_parser(void *pV_data)
{
        char str[INET_ADDRSTRLEN];
        /* socket structure */
        struct sockaddr_in client1, server1;
        struct servent *serviceInfo;
        int s1, s;
        socklen_t length;
/*      struct SOCKETS soc; */
        
        /* socket server */
        if (configs.port > 0)
                server1.sin_port = htons(configs.port);
        else if ((serviceInfo = getservbyname ("mdb", "tcp")))
                server1.sin_port = serviceInfo->s_port;
        else
        {
                syslog(LOG_DAEMON,"Error: No TCP port defined!");
                pthread_exit(NULL);
                exit(EXIT_FAILURE);
        }
        
        server1.sin_family = AF_INET;
        server1.sin_addr.s_addr = INADDR_ANY;
        s = socket(AF_INET,SOCK_STREAM,0);
        
        if (s < 0)
        {
                syslog (LOG_DAEMON, "Error in socket: %s",strerror(errno));
                pthread_exit(NULL);
                exit(EXIT_FAILURE);
        }
        
        if (bind (s, (struct sockaddr *)&server1, sizeof (server1)) < 0)
        {
                syslog (LOG_DAEMON, "Error in bind: %s", strerror(errno));
                pthread_exit(NULL);
                exit(EXIT_FAILURE);
        }
        
        if (listen (s, 5) < 0)
        {
                syslog (LOG_DAEMON, "Error in listen: %s", strerror(errno));
                pthread_exit(NULL);
                exit(EXIT_FAILURE);
        }
        
        length = sizeof(client1);
        syslog (LOG_DEBUG, "Server ready: %d",s);
        
        while (1)
        {
/*              int childpid, status; */

                if ((s1 = accept (s, (struct sockaddr *)&client1, &length)) < 0)
                {
                        syslog (LOG_DAEMON, "Error in accept: %d: %s", s1, strerror(errno));
                        continue;
                }
                
                inet_ntop(AF_INET, &(client1.sin_addr), str, INET_ADDRSTRLEN);
                syslog (LOG_INFO, "Connected to client %s", str);
                soc.sockfd = s;
                soc.newfd = s1;

                /* start a new thread to parse the commands */
                if (pthread_create(&pthr_cmds, &pattr, processCommands, (void *)&soc) != 0)
                {
                        syslog (LOG_DAEMON,"Creation of thread \"pthr_cmds\" failed!");
                        close(s1);
                        close(s);
                        pthread_exit(NULL);
                        return NULL;
                }

/*              if ((childpid = fork()) < 0)
                        syslog(LOG_DAEMON, "Can't fork a child: %s", strerror(errno));

                if (childpid == 0)
                {
                        / Child process /
                        processCommands((void *)&soc);
                        exit(EXIT_SUCCESS);
                }

#if defined(BSD) && !defined(Linux)
                while (wait3 (&status, WNOHANG, (struct rusage *)0) > 0)
                        sleep(1);
#else
                while (waitpid(-1, &status, WNOHANG) > 0)
                        sleep(1);
#endif
                close(s1); */
        }
        
        close (s);
        pthread_exit(NULL);
        return NULL;
}