bmsd/bms.c

Sat, 25 Sep 2021 10:42:54 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sat, 25 Sep 2021 10:42:54 +0200
changeset 778
e64fd38c469c
parent 747
b6fbe6821468
permissions
-rw-r--r--

If during styles import the CATEGORY_NUMBER is empty, insert 0 in the database instead.

/*****************************************************************************
 * Copyright (C) 2017-2021
 *   
 * Michiel Broek <mbroek at mbse dot eu>
 *
 * This file is part of the bms (Brewery Management System)
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * bms is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with ThermFerm; see the file COPYING.  If not, write to the Free
 * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *****************************************************************************/

#include "bms.h"
#include "xutil.h"
#include "futil.h"
#include "rdconfig.h"
#include "lock.h"
#include "mqtt.h"
#include "mysql.h"
#include "nodes.h"
#include "websocket.h"


int			my_shutdown = false;
int			debug = false;
static pid_t		mypid;
char			*Private_Path = NULL;	/* Users data path		*/
pthread_t		ws_thread;

extern sys_config       Config;



void help(void)
{
    fprintf(stdout, "bmsd v%s starting\n\n", VERSION);
    fprintf(stdout, "Usage: bmsd [-d] [-h]\n");
    fprintf(stdout, "  -d --debug              Debug and run in foreground\n");
    fprintf(stdout, "  -h --help               Display this help\n");
}



void die(int onsig)
{
    switch (onsig) {
	case SIGHUP:    syslog(LOG_NOTICE, "Got SIGHUP, shutting down");
			break;
	case SIGINT:    syslog(LOG_NOTICE, "Keyboard interrupt, shutting down");
			break;
	case SIGTERM:   syslog(LOG_NOTICE, "Got SIGTERM, shutting down");
			break;
	case SIGSEGV:   syslog(LOG_NOTICE, "Got SIGSEGV, shutting down");
			my_shutdown = true;
			exit(SIGSEGV);
			break;
	default:        syslog(LOG_NOTICE, "die() on signal %d", onsig);
    }
    my_shutdown = true;
}



/**
 * @brief Drop privileges in a safe way.
 * @return 0 on success and -1 on failure.
 */
int drop_root_privileges(uid_t pw_uid, gid_t pw_gid, char *pw_dir)
{
    // no need to "drop" the privileges that you don't have in the first place!
    if (getuid() == pw_uid && getgid() == pw_gid) {
	syslog(LOG_NOTICE, "No need to drop privileges");
    } else {
	if (setgid(pw_gid) != 0) {
	    syslog(LOG_NOTICE, "setgid: %s", strerror(errno));
	    return -1;
	}
	if (setuid(pw_uid) != 0) {
	    syslog(LOG_NOTICE, "setgid: %s", strerror(errno));
	    return -1;
	}
    }

    /* Change to the home directory */
    if (chdir(pw_dir) != 0) {
	syslog(LOG_NOTICE, "chdir(%s): %s", pw_dir, strerror(errno));
	return -1;
    }

    /* check if we successfully dropped the root privileges */
    if (setuid(0) == 0 || seteuid(0) == 0) {
        syslog(LOG_NOTICE, "could not drop root privileges!");
        return -1;
    }

    syslog(LOG_NOTICE, "Privileges dropped to %d:%d", pw_uid, pw_gid);
    return 0;
}



int server(void)
{
    struct passwd       *mypwd;
    int			rc = 0;
    char		*tmppath = NULL;

    mypwd = getpwnam("brewery");
    if (mypwd == NULL) {
	fprintf(stderr, "[main] Could not find passwd entry\n");
	return 1;
    }

    Private_Path = xstrcpy(mypwd->pw_dir);
    Private_Path = xstrcat(Private_Path, (char *)"/.bms");

    if (drop_root_privileges(mypwd->pw_uid, mypwd->pw_gid, mypwd->pw_dir) < 0) {
	syslog(LOG_NOTICE, "Can't drop privileges");
	return 1;
    }

    if (lockprog((char *)"bmsd")) {
	syslog(LOG_NOTICE, "Can't lock");
	return 1;
    }

    if (rdconfig()) {
	rc = 2;
	goto endit1;
    }

    if (! Config.web_root) {
	rc = 5;
	syslog(LOG_NOTICE, "Configuration `web_root' is not set"); 
	goto endit1;
    }

    /*
     * Make sure the needed log paths exist.
     */
    tmppath = xstrcpy(Config.web_root);
    tmppath = xstrcat(tmppath, (char *)"/log/fermentation/");
    if (! mkdirs(tmppath, 0755)) {
	rc = 6;
	goto endit1;
    }
    free(tmppath);
    tmppath = xstrcpy(Config.web_root);
    tmppath = xstrcat(tmppath, (char *)"/log/brews/");
    if (! mkdirs(tmppath, 0755)) {
	rc = 7;
	goto endit1;
    }
    free(tmppath);
    tmppath = NULL;

    if (bms_mysql_init()) {
	rc = 3;
	goto endit1;
    }
    if (mqtt_connect()) {
	rc = 4;
	goto endit2;
    }

    rc = pthread_create(&ws_thread, NULL, ws_loop, NULL);
    if (rc) {
        fprintf(stderr, "ws_loop thread didn't start rc=%d\n", rc);
        syslog(LOG_NOTICE, "ws_loop thread didn't start rc=%d", rc);
	rc = 5;
	goto endit3;
    }

    if (debug)
	fprintf(stdout, "[main] Entering main loop\n");

    while (my_shutdown == false) {

	usleep(3000000);
	nodes_check_online();
	usleep(2000000);
	ws_check();
    }
    if (debug)
	fprintf(stdout, "[main] Exit from main loop\n");

endit3:
    /*
     * Remove our topics and close MQTT connection.
     */
    mqtt_disconnect();

endit2:
    bms_mysql_end();

endit1:

    killconfig();

    ulockprog((char *)"bmsd");

    free(Private_Path);
    Private_Path = NULL;

    if (debug)
	fprintf(stdout, "[main] Server shutdown complete\n");
    return rc;
}



int main(int argc, char *argv[]) {
    int		rc, c, i;
    pid_t	frk;

    while (1) {
	int option_index = 0;
	static struct option long_options[] = {
	    {"debug", 0, 0, 'c'},
	    {"help", 0, 0, 'h'},
	    {0, 0, 0, 0}
	};

	c = getopt_long(argc, argv, "dh", long_options, &option_index);
	if (c == -1)
	    break;
	switch (c) {
	    case 'd':   debug = true;
			break;
	    case 'h':   help();
			return 1;
	}
    }

    openlog("bmsd", LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_USER);
    syslog(LOG_NOTICE, "bmsd v%s starting", VERSION);
    if (debug)
	fprintf(stdout, "bmsd v%s starting\n", VERSION);


    /*
     *  Catch all the signals we can, and ignore the rest. Note that SIGKILL can't be ignored
     *  but that's live. This daemon should only be stopped by SIGTERM.
     *  Don't catch SIGCHLD.
     */
    for (i = 0; i < NSIG; i++) {
	if ((i != SIGCHLD) && (i != SIGKILL) && (i != SIGSTOP))
	    signal(i, (void (*))die);
    }

    if (debug) {
	/*
	 * For debugging run in foreground.
	 */
	rc = server();
    } else {
	/*
	 * Server initialization is complete. Now we can fork the 
	 * daemon and return to the user.
	 */
	frk = fork();
	switch (frk) {
	    case -1:    
			syslog(LOG_NOTICE, "Daemon fork failed: %s", strerror(errno));
			exit(1);
	    case 0:     /*
			 * Run the daemon
			 */
			fclose(stdin);
			if (open("/dev/null", O_RDONLY) != 0) {
			    syslog(LOG_NOTICE, "Reopen of stdin to /dev/null failed");
			    _exit(2);
			}
			fclose(stdout);
			if (open("/dev/null", O_WRONLY | O_APPEND | O_CREAT,0600) != 1) {
			    syslog(LOG_NOTICE, "Reopen of stdout to /dev/null failed");
			    _exit(2);
			}
			fclose(stderr);
			if (open("/dev/null", O_WRONLY | O_APPEND | O_CREAT,0600) != 2) {
			    syslog(LOG_NOTICE, "Reopen of stderr to /dev/null failed");
			    _exit(2);
			}
			mypid = getpid();
			rc = server();
			break;
			/* Not reached */
	    default:
			/*
			 * Here we detach this process and let the child
			 * run the deamon process.
			 */
			syslog(LOG_NOTICE, "Starting daemon with pid %d", frk);
			exit(0);
	}
    }

    syslog(LOG_NOTICE, "Finished, rc=%d", rc);
    return rc;
}

mercurial