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:
parent
089b93cb11
commit
cbbe31f11e
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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_ */
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue