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 <aws_iot_log.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <libgen.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <unistd.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
|
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 int check_stop(void);
|
||||||
static void add_sigkill_signal(void);
|
static void add_sigkill_signal(void);
|
||||||
static void graceful_shutdown(void);
|
static void graceful_shutdown(void);
|
||||||
static void sigint_handler(int signum);
|
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
|
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
|
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 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;
|
int result = EXIT_SUCCESS;
|
||||||
|
|
||||||
|
|
@ -97,3 +176,13 @@ static void sigint_handler(int signum)
|
||||||
IOT_DEBUG("Received signal %d to close Cloud connection.", signum);
|
IOT_DEBUG("Received signal %d to close Cloud connection.", signum);
|
||||||
exit(0);
|
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