aws iot sample: add program options for application usage and daemonization

https://jira.digi.com/browse/DEL-4149

Signed-off-by: Tatiana Leon <tatiana.leon@digi.com>
This commit is contained in:
Tatiana Leon 2017-05-10 21:00:08 +02:00
parent 089b93cb11
commit cbbe31f11e
3 changed files with 397 additions and 0 deletions

View File

@ -0,0 +1,280 @@
/*
* Copyright (c) 2017 Digi International Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
* =======================================================================
*
* From http://www.itp.uzh.ch/~dpotter/howto/daemonize
*
*/
#include <aws_iot_log.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <unistd.h>
/*------------------------------------------------------------------------------
F U N C T I O N D E C L A R A T I O N S
------------------------------------------------------------------------------*/
static void daemonize(char const *const daemon_name);
static void signal_handler(int signum);
static int get_lock(char const *const file_name);
static void release_lock(int const fd);
/*------------------------------------------------------------------------------
G L O B A L V A R I A B L E S
------------------------------------------------------------------------------*/
static volatile sig_atomic_t signal_from_child = 0;
/*------------------------------------------------------------------------------
F U N C T I O N D E F I N I T I O N S
------------------------------------------------------------------------------*/
/**
* start_daemon() - Start a new daemon.
*
* @name: Daemon name.
*
* Return: 0 on success, 1 otherwise.
*/
int start_daemon(const char *name)
{
int result = EXIT_SUCCESS;
int lock_fd = -1;
/* Daemonize if requested. */
lock_fd = get_lock(name);
if (lock_fd < 0) {
IOT_ERROR("Unable to start %s. It may be currently running", name);
result = EXIT_FAILURE;
goto done;
}
daemonize(name);
done:
/* Clean up. */
IOT_INFO("Daemon terminated");
release_lock(lock_fd);
return result;
}
/**
* daemonize() - Daemonize the current process using the given name.
*
* @name: Daemon name.
*/
static void daemonize(char const *const name)
{
pid_t pid, sid, parent;
FILE *pid_fp = NULL;
char pid_filename[PATH_MAX];
/* Already a daemon. */
if (getppid() == 1) {
return;
}
IOT_DEBUG("Start daemon %s", name);
/* Open .pid file while root. We can write it once we know child PID. */
umask(0022);
snprintf(pid_filename, sizeof(pid_filename), "/var/run/%s.pid", name);
pid_fp = fopen(pid_filename, "w+");
if (pid_fp == NULL) {
IOT_ERROR("Unable to open pid file (errno: %d)", errno);
exit(EXIT_FAILURE);
}
/* Trap signals that we expect to receive. */
signal(SIGCHLD, signal_handler);
signal(SIGUSR1, signal_handler);
signal(SIGALRM, signal_handler);
/* Fork off the parent process. */
pid = fork();
if (pid < 0) {
IOT_ERROR("Unable to fork daemon (errno: %d, %s)", errno, strerror(errno));
exit(EXIT_FAILURE);
}
/* If we got a good PID, then we can exit the parent process. */
if (pid > 0) {
int fork_succeeded;
int exit_code;
/* Write the PID of the newly created child process into the file. */
if (fprintf(pid_fp, "%d\n", pid) <= 0) {
IOT_ERROR("Unable to write pid file (errno: %d)", errno);
fclose(pid_fp);
/* Do we want to do anything about the child that has just started
* at this point. */
exit(EXIT_FAILURE);
}
fclose(pid_fp);
fork_succeeded = 0;
/* Wait for confirmation from the child via SIGTERM or SIGCHLD, or
* for two seconds to elapse (SIGALRM). pause() should return after two
* seconds at most. */
alarm(2);
if (signal_from_child)
fork_succeeded = 1;
pause();
if (signal_from_child)
fork_succeeded = 1;
exit_code = fork_succeeded ? EXIT_SUCCESS : EXIT_FAILURE;
exit(exit_code);
}
/* At this point we are executing as the child process. */
parent = getppid();
/* Cancel certain signals. */
signal(SIGCHLD, SIG_DFL); /* A child process dies */
signal(SIGTSTP, SIG_IGN); /* Various TTY signals */
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGHUP, SIG_IGN); /* Ignore hangup signal */
signal(SIGTERM, SIG_DFL); /* Die on SIGTERM */
/* Change the file mode mask. */
umask(0);
/* Create a new SID for the child process. */
sid = setsid();
if (sid < 0) {
IOT_ERROR("Unable to create a new session (errno %d, %s)",
errno, strerror(errno));
exit(EXIT_FAILURE);
}
/* Change the current working directory. This prevents the current
directory from being locked; hence not being able to remove it. */
if (chdir("/") < 0) {
IOT_ERROR("Unable to change directory to '%s', (errno %d, %s)", "/",
errno, strerror(errno));
exit(EXIT_FAILURE);
}
/* Redirect standard files to /dev/null.
* Log an error, but otherwise ignore it. */
if (freopen("/dev/null", "r", stdin) == NULL)
IOT_ERROR("Failed to redirect stdin to /dev/null");
if (freopen("/dev/null", "w", stdout) == NULL)
IOT_ERROR("Failed to redirect stdout to /dev/null");
if (freopen("/dev/null", "w", stderr) == NULL)
IOT_ERROR("Failed to redirect stderr to /dev/null");
/* Tell the parent process that we are A-okay. */
kill(parent, SIGUSR1);
}
/**
* signal_handler() - Manage signal received.
*
* @signum: Received signal.
*/
static void signal_handler(int signum)
{
switch (signum) {
case SIGALRM:
/* Ignore this signal.*/
break;
case SIGUSR1:
signal_from_child = 1;
break;
case SIGCHLD:
exit(EXIT_FAILURE);
break;
default:
break;
}
}
/**
* get_lock() - Try to get lock
*
* @file_name: Name of the file used as lock inside '/var/lock'.
*
* Return: File descriptor of lock file, or -1 on error.
*/
static int get_lock(char const *const file_name)
{
static char const path[] = "/var/lock/";
char full_path[sizeof(path) + strlen(file_name)];
int fd;
snprintf(full_path, sizeof(full_path), "%s%s", path, file_name);
fd = open(full_path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1) {
IOT_ERROR("Could not open PID file '%s'", full_path);
goto error;
}
if (flock(fd, LOCK_EX | LOCK_NB) != 0) {
IOT_ERROR("Could not open lock PID file '%s'", full_path);
goto error;
}
if (ftruncate(fd, 0) != 0) {
IOT_ERROR("Could not truncate PID file '%s'", full_path);
goto error;
}
{
char buf[50];
int len = snprintf(buf, sizeof(buf), "%ld", (long) getpid());
if (write(fd, buf, len) != len) {
IOT_ERROR("Error writing to PID file '%s'", full_path);
goto error;
}
}
goto done;
error:
release_lock(fd);
fd = -1;
done:
return fd;
}
/**
* release_lock() - Release the lock obtained with 'get_lock(file_name)'
*
* @fd: File descriptor of lock file.
*/
static void release_lock(int const fd)
{
if (fd < 0)
return;
if (ftruncate(fd, 0) == -1)
IOT_ERROR("Could not truncate PID file");
if (lockf(fd, F_ULOCK, 0) == -1)
IOT_ERROR("Unable to unlock");
close(fd);
}

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2017 Digi International Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
* =======================================================================
*/
#ifndef DAEMONIZE_H_
#define DAEMONIZE_H_
/*------------------------------------------------------------------------------
F U N C T I O N D E C L A R A T I O N S
------------------------------------------------------------------------------*/
int start_daemon(const char *name);
#endif /* DAEMONIZE_H_ */

View File

@ -18,16 +18,38 @@
*/
#include <aws_iot_log.h>
#include <getopt.h>
#include <libgen.h>
#include <signal.h>
#include <unistd.h>
#include "daemonize.h"
/*------------------------------------------------------------------------------
D E F I N I T I O N S
------------------------------------------------------------------------------*/
#define VERSION "0.1" GIT_REVISION
#define USAGE \
"AWS IoT Device SDK demo.\n" \
"Copyright(c) Digi International Inc.\n" \
"\n" \
"Version: %s\n" \
"\n" \
"Usage: %s [options]\n\n" \
" -d --daemon Daemonize the process\n" \
" -h --help Print help and exit\n" \
"\n"
/*------------------------------------------------------------------------------
F U N C T I O N D E C L A R A T I O N S
------------------------------------------------------------------------------*/
static int start_aws_iot(const char *config_file);
static int check_stop(void);
static void add_sigkill_signal(void);
static void graceful_shutdown(void);
static void sigint_handler(int signum);
static void usage(char const *const name);
/*------------------------------------------------------------------------------
G L O B A L V A R I A B L E S
@ -38,6 +60,63 @@ static volatile int stop = 0;
F U N C T I O N D E F I N I T I O N S
------------------------------------------------------------------------------*/
int main(int argc, char **argv)
{
int result = EXIT_SUCCESS;
char *name = basename(argv[0]);
static int opt, opt_index;
int create_daemon = 0;
char *config_file = NULL;
static const char *short_options = "dh";
static const struct option long_options[] = {
{"daemon", no_argument, NULL, 'd'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
while (1) {
opt = getopt_long(argc, argv, short_options, long_options,
&opt_index);
if (opt == -1)
break;
switch (opt) {
case 'd':
create_daemon = 1;
break;
case 'h':
usage(name);
goto done;
default:
usage(name);
result = EXIT_FAILURE;
goto done;
}
}
/* Daemonize if requested. */
if (create_daemon) {
if (start_daemon(name) != 0) {
result = EXIT_FAILURE;
goto done;
}
}
/* Do the real work. */
start_aws_iot(config_file);
done:
return result;
}
/*
* start_aws_iot() - Start AWS IoT Device SDK
*
* @config_file: Absolute path of the configuration file to use.
* NULL to use the default one (/etc/awsiotsdk.conf).
*
* Return: 0 on success, 1 otherwise.
*/
static int start_aws_iot(const char *config_file)
{
int result = EXIT_SUCCESS;
@ -97,3 +176,13 @@ static void sigint_handler(int signum)
IOT_DEBUG("Received signal %d to close Cloud connection.", signum);
exit(0);
}
/**
* usage() - Print usage information
*
* @name: Application name.
*/
static void usage(char const *const name)
{
printf(USAGE, VERSION, name);
}