1
0
mirror of https://gitlab.xiph.org/xiph/ezstream.git synced 2025-02-02 15:07:45 -05:00

Add support for writing PID files

This commit is contained in:
Moritz Grimm 2017-07-10 11:39:00 +02:00
parent 781cfc12c7
commit 43e26549f9
11 changed files with 161 additions and 19 deletions

2
NEWS
View File

@ -12,6 +12,8 @@ Changes in 1.0.0, released on XXXX-XX-XX:
* The configuration file structure has changed. * The configuration file structure has changed.
* TagLib (its C wrapper library) is now a mandatory dependency * TagLib (its C wrapper library) is now a mandatory dependency
* Support the new '@b@' placeholder for separate album metadata. * Support the new '@b@' placeholder for separate album metadata.
* The command line option -p has been added, causing ezstream to write a
locked PID file to a given location
Changes in 0.6.0, released on 2015-01-18: Changes in 0.6.0, released on 2015-01-18:

View File

@ -20,6 +20,7 @@
.Bk -words .Bk -words
.Op Fl hqrVv .Op Fl hqrVv
.Fl c Ar configfile .Fl c Ar configfile
.Op Fl p Ar pidfile
.Ek .Ek
.Nm .Nm
.Bk -words .Bk -words
@ -45,6 +46,20 @@ Use the XML configuration in
.It Fl h .It Fl h
Print a summary of available command line arguments with short descriptions Print a summary of available command line arguments with short descriptions
and exit. and exit.
.It Fl p Ar pidfile
Write the
.Nm
process ID
.Pq a single number
to
.Ar pidfile .
The file will be written even when it already exists.
A file lock is maintained until the main
.Nm
process terminates.
If the file cannot be written for any reason,
.Nm
will log this, but not consider it a fatal error.
.It Fl q .It Fl q
Be more quiet. Be more quiet.
Suppress the output that external programs send to standard error. Suppress the output that external programs send to standard error.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015 Moritz Grimm <mgrimm@mrsserver.net> * Copyright (c) 2015, 2017 Moritz Grimm <mgrimm@mrsserver.net>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -277,6 +277,13 @@ cfg_set_program_config_file(const char *file, const char **errstrp)
return (0); return (0);
} }
int
cfg_set_program_pid_file(const char *file, const char **errstrp)
{
SET_STRLCPY(cfg.program.pid_file, file, errstrp);
return (0);
}
int int
cfg_set_program_quiet_stderr(int quiet_stderr, const char **not_used) cfg_set_program_quiet_stderr(int quiet_stderr, const char **not_used)
{ {
@ -642,6 +649,12 @@ cfg_get_program_config_file(void)
return (cfg.program.config_file[0] ? cfg.program.config_file : NULL); return (cfg.program.config_file[0] ? cfg.program.config_file : NULL);
} }
const char *
cfg_get_program_pid_file(void)
{
return (cfg.program.pid_file[0] ? cfg.program.pid_file : NULL);
}
int int
cfg_get_program_quiet_stderr(void) cfg_get_program_quiet_stderr(void)
{ {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015 Moritz Grimm <mgrimm@mrsserver.net> * Copyright (c) 2015, 2017 Moritz Grimm <mgrimm@mrsserver.net>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -91,6 +91,7 @@ int cfg_file_check(const char *);
int cfg_set_program_name(const char *, const char **); int cfg_set_program_name(const char *, const char **);
int cfg_set_program_config_type(enum cfg_config_type, const char **); int cfg_set_program_config_type(enum cfg_config_type, const char **);
int cfg_set_program_config_file(const char *, const char **); int cfg_set_program_config_file(const char *, const char **);
int cfg_set_program_pid_file(const char *, const char **);
int cfg_set_program_quiet_stderr(int, const char **); int cfg_set_program_quiet_stderr(int, const char **);
int cfg_set_program_rtstatus_output(int, const char **); int cfg_set_program_rtstatus_output(int, const char **);
int cfg_set_program_verbosity(unsigned int, const char **); int cfg_set_program_verbosity(unsigned int, const char **);
@ -137,6 +138,8 @@ enum cfg_config_type
cfg_get_program_config_type(void); cfg_get_program_config_type(void);
const char * const char *
cfg_get_program_config_file(void); cfg_get_program_config_file(void);
const char *
cfg_get_program_pid_file(void);
int cfg_get_program_quiet_stderr(void); int cfg_get_program_quiet_stderr(void);
int cfg_get_program_rtstatus_output(void); int cfg_get_program_rtstatus_output(void);
unsigned int unsigned int

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015 Moritz Grimm <mgrimm@mrsserver.net> * Copyright (c) 2015, 2017 Moritz Grimm <mgrimm@mrsserver.net>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -37,6 +37,7 @@ struct cfg {
char name[PATH_MAX]; char name[PATH_MAX];
enum cfg_config_type config_type; enum cfg_config_type config_type;
char config_file[PATH_MAX]; char config_file[PATH_MAX];
char pid_file[PATH_MAX];
int quiet_stderr; int quiet_stderr;
int rtstatus_output; int rtstatus_output;
unsigned int verbosity; unsigned int verbosity;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015 Moritz Grimm <mgrimm@mrsserver.net> * Copyright (c) 2015, 2017 Moritz Grimm <mgrimm@mrsserver.net>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -32,10 +32,11 @@
#include "cmdline.h" #include "cmdline.h"
#include "playlist.h" #include "playlist.h"
#define OPTSTRING "c:hqrs:Vv" #define OPTSTRING "c:hp:qrs:Vv"
enum opt_vals { enum opt_vals {
OPT_CONFIGFILE = 'c', OPT_CONFIGFILE = 'c',
OPT_HELP = 'h', OPT_HELP = 'h',
OPT_PIDFILE = 'p',
OPT_QUIETSTDERR = 'q', OPT_QUIETSTDERR = 'q',
OPT_RTSTATUS = 'r', OPT_RTSTATUS = 'r',
OPT_SHUFFLEFILE = 's', OPT_SHUFFLEFILE = 's',
@ -51,7 +52,7 @@ static void _set_program_name(const char *);
static void static void
_usage(void) _usage(void)
{ {
fprintf(stderr, "usage: %s [-hqrVv] -c cfgfile\n", fprintf(stderr, "usage: %s [-hqrVv] -c cfgfile [-p pidfile]\n",
cfg_get_program_name()); cfg_get_program_name());
fprintf(stderr, " %s -s file\n", fprintf(stderr, " %s -s file\n",
cfg_get_program_name()); cfg_get_program_name());
@ -63,6 +64,7 @@ _usage_help(void)
fprintf(stderr, "\n"); fprintf(stderr, "\n");
fprintf(stderr, " -c cfgfile use XML configuration in cfgfile\n"); fprintf(stderr, " -c cfgfile use XML configuration in cfgfile\n");
fprintf(stderr, " -h print this help and exit\n"); fprintf(stderr, " -h print this help and exit\n");
fprintf(stderr, " -p pidfile write PID to pidfile\n");
fprintf(stderr, " -q suppress STDERR output from external en-/decoders\n"); fprintf(stderr, " -q suppress STDERR output from external en-/decoders\n");
fprintf(stderr, " -r show real-time stream information on stdout\n"); fprintf(stderr, " -r show real-time stream information on stdout\n");
fprintf(stderr, " -s file read lines from file, shuffle, print to STDOUT, then exit\n"); fprintf(stderr, " -s file read lines from file, shuffle, print to STDOUT, then exit\n");
@ -125,6 +127,15 @@ cmdline_parse(int argc, char *argv[], int *ret_p)
_usage_help(); _usage_help();
*ret_p = 0; *ret_p = 0;
return (-1); return (-1);
case OPT_PIDFILE:
if (0 > cfg_set_program_pid_file(optarg, &err_str)) {
fprintf(stderr, "-%c: argument %s\n",
OPT_PIDFILE, err_str);
_usage();
*ret_p = 2;
return (-1);
}
break;
case OPT_RTSTATUS: case OPT_RTSTATUS:
cfg_set_program_rtstatus_output(1, NULL); cfg_set_program_rtstatus_output(1, NULL);
/* FALLTHROUGH */ /* FALLTHROUGH */

View File

@ -1,7 +1,7 @@
/* /*
* ezstream - source client for Icecast with external en-/decoder support * ezstream - source client for Icecast with external en-/decoder support
* Copyright (C) 2003, 2004, 2005, 2006 Ed Zaleski <oddsock@oddsock.org> * Copyright (C) 2003, 2004, 2005, 2006 Ed Zaleski <oddsock@oddsock.org>
* Copyright (C) 2007, 2009, 2015 Moritz Grimm <mgrimm@mrsserver.net> * Copyright (C) 2007, 2009, 2015, 2017 Moritz Grimm <mgrimm@mrsserver.net>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
@ -801,6 +801,9 @@ main(int argc, char *argv[])
return (ez_shutdown(1)); return (ez_shutdown(1));
} }
if (0 > writePidfile(cfg_get_program_pid_file()))
log_syserr(WARNING, errno, cfg_get_program_pid_file());
if (0 > stream_connect(stream)) { if (0 > stream_connect(stream)) {
log_error("initial server connection failed"); log_error("initial server connection failed");
return (ez_shutdown(1)); return (ez_shutdown(1));

View File

@ -1,7 +1,7 @@
/* /*
* ezstream - source client for Icecast with external en-/decoder support * ezstream - source client for Icecast with external en-/decoder support
* Copyright (C) 2003, 2004, 2005, 2006 Ed Zaleski <oddsock@oddsock.org> * Copyright (C) 2003, 2004, 2005, 2006 Ed Zaleski <oddsock@oddsock.org>
* Copyright (C) 2007, 2009 Moritz Grimm <mgrimm@mrsserver.net> * Copyright (C) 2007, 2009, 2017 Moritz Grimm <mgrimm@mrsserver.net>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
@ -24,6 +24,9 @@
#include "compat.h" #include "compat.h"
#include <sys/types.h>
#include <sys/file.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <langinfo.h> #include <langinfo.h>
@ -31,12 +34,12 @@
#include <locale.h> #include <locale.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#ifdef HAVE_ICONV #ifdef HAVE_ICONV
# include <iconv.h> # include <iconv.h>
#endif #endif
#include "cfg.h"
#include "log.h" #include "log.h"
#include "util.h" #include "util.h"
#include "xalloc.h" #include "xalloc.h"
@ -45,7 +48,13 @@
# define BUFSIZ 1024 # define BUFSIZ 1024
#endif #endif
char * iconvert(const char *, const char *, const char *, int); static char *pidfile_path;
static FILE *pidfile_file;
static pid_t pidfile_pid;
static unsigned int pidfile_numlocks;
static char * iconvert(const char *, const char *, const char *, int);
static void cleanupPidfile(void);
int int
strrcmp(const char *s, const char *sub) strrcmp(const char *s, const char *sub)
@ -105,7 +114,7 @@ UTF8toCHAR(const char *in_str, int mode)
return (iconvert(in_str, "UTF-8", codeset, mode)); return (iconvert(in_str, "UTF-8", codeset, mode));
} }
char * static char *
iconvert(const char *in_str, const char *from, const char *to, int mode) iconvert(const char *in_str, const char *from, const char *to, int mode)
{ {
#ifdef HAVE_ICONV #ifdef HAVE_ICONV
@ -205,6 +214,64 @@ iconvert(const char *in_str, const char *from, const char *to, int mode)
#endif /* HAVE_ICONV */ #endif /* HAVE_ICONV */
} }
static void
cleanupPidfile(void)
{
if (NULL != pidfile_path && getpid() == pidfile_pid) {
(void)unlink(pidfile_path);
(void)fclose(pidfile_file);
}
}
int
writePidfile(const char *path)
{
int save_errno = 0;
pid_t pid;
if (NULL == path)
return (0);
xfree(pidfile_path);
pidfile_path = xstrdup(path);
if (NULL != pidfile_file)
fclose(pidfile_file);
if (NULL == (pidfile_file = fopen(pidfile_path, "w"))) {
save_errno = errno;
xfree(pidfile_path);
pidfile_path = NULL;
return (-1);
}
pid = getpid();
if (0 >= fprintf(pidfile_file, "%ld\n", (long)pid) ||
0 > fflush(pidfile_file) ||
0 > flock(fileno(pidfile_file), LOCK_EX))
goto error;
if (0 == pidfile_numlocks) {
pidfile_pid = pid;
if (0 != atexit(cleanupPidfile))
goto error;
pidfile_numlocks++;
}
return (0);
error:
save_errno = errno;
(void)unlink(pidfile_path);
xfree(pidfile_path);
pidfile_path = NULL;
(void)fclose(pidfile_file);
pidfile_file = NULL;
pidfile_pid = 0;
errno = save_errno;
return (-1);
}
char * char *
replaceString(const char *source, const char *from, const char *to) replaceString(const char *source, const char *from, const char *to)
{ {

View File

@ -1,7 +1,7 @@
/* /*
* ezstream - source client for Icecast with external en-/decoder support * ezstream - source client for Icecast with external en-/decoder support
* Copyright (C) 2003, 2004, 2005, 2006 Ed Zaleski <oddsock@oddsock.org> * Copyright (C) 2003, 2004, 2005, 2006 Ed Zaleski <oddsock@oddsock.org>
* Copyright (C) 2007 Moritz Grimm <mgrimm@mrsserver.net> * Copyright (C) 2007, 2017 Moritz Grimm <mgrimm@mrsserver.net>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
@ -20,12 +20,13 @@
#define ICONV_TRANSLIT 1 #define ICONV_TRANSLIT 1
#define ICONV_IGNORE 2 #define ICONV_IGNORE 2
int strrcmp(const char *, const char *); int writePidfile(const char *);
int strrcasecmp(const char *, const char *); int strrcmp(const char *, const char *);
char * CHARtoUTF8(const char *, int); int strrcasecmp(const char *, const char *);
char * UTF8toCHAR(const char *, int); char * CHARtoUTF8(const char *, int);
char * replaceString(const char *, const char *, const char *); char * UTF8toCHAR(const char *, int);
char * shellQuote(const char *); char * replaceString(const char *, const char *, const char *);
int urlParse(const char *, char **, unsigned short *, char **); char * shellQuote(const char *);
int urlParse(const char *, char **, unsigned short *, char **);
#endif /* __UTIL_H__ */ #endif /* __UTIL_H__ */

View File

@ -207,6 +207,14 @@ START_TEST(test_program_config_file)
} }
END_TEST END_TEST
START_TEST(test_program_pid_file)
{
ck_assert_ptr_eq(cfg_get_program_pid_file(), NULL);
TEST_STRLCPY(cfg_set_program_pid_file, cfg_get_program_pid_file,
PATH_MAX);
}
END_TEST
START_TEST(test_program_quiet_stderr) START_TEST(test_program_quiet_stderr)
{ {
ck_assert_int_eq(cfg_set_program_quiet_stderr(-1, NULL), 0); ck_assert_int_eq(cfg_set_program_quiet_stderr(-1, NULL), 0);
@ -808,6 +816,7 @@ cfg_suite(void)
tcase_add_test(tc_program, test_program_name); tcase_add_test(tc_program, test_program_name);
tcase_add_test(tc_program, test_program_config_type); tcase_add_test(tc_program, test_program_config_type);
tcase_add_test(tc_program, test_program_config_file); tcase_add_test(tc_program, test_program_config_file);
tcase_add_test(tc_program, test_program_pid_file);
tcase_add_test(tc_program, test_program_quiet_stderr); tcase_add_test(tc_program, test_program_quiet_stderr);
tcase_add_test(tc_program, test_program_rtstatus_output); tcase_add_test(tc_program, test_program_rtstatus_output);
tcase_add_test(tc_program, test_program_verbosity); tcase_add_test(tc_program, test_program_verbosity);

View File

@ -33,6 +33,22 @@ START_TEST(test_help)
} }
END_TEST END_TEST
START_TEST(test_pidfile)
{
char *argv[] =
{
"check_cmdline", "-p", "PIDFILE-TEST" , NULL
};
int argc = (int)(sizeof(argv) / sizeof(argv[0])) - 1;
int ret;
ck_assert_ptr_eq(cfg_get_program_pid_file(), NULL);
ck_assert_int_ne(cmdline_parse(argc, argv, &ret), 0);
ck_assert_int_eq(ret, 2);
ck_assert_str_eq(cfg_get_program_pid_file(), "PIDFILE-TEST");
}
END_TEST
START_TEST(test_quiet_stderr) START_TEST(test_quiet_stderr)
{ {
char *argv[] = { "check_cmdline", "-q", NULL }; char *argv[] = { "check_cmdline", "-q", NULL };
@ -133,6 +149,7 @@ cmdline_suite(void)
tcase_add_checked_fixture(tc_cmdline, setup_checked, teardown_checked); tcase_add_checked_fixture(tc_cmdline, setup_checked, teardown_checked);
tcase_add_test(tc_cmdline, test_configfile); tcase_add_test(tc_cmdline, test_configfile);
tcase_add_test(tc_cmdline, test_help); tcase_add_test(tc_cmdline, test_help);
tcase_add_test(tc_cmdline, test_pidfile);
tcase_add_test(tc_cmdline, test_quiet_stderr); tcase_add_test(tc_cmdline, test_quiet_stderr);
tcase_add_test(tc_cmdline, test_rtstatus_output); tcase_add_test(tc_cmdline, test_rtstatus_output);
tcase_add_test(tc_cmdline, test_shuffle); tcase_add_test(tc_cmdline, test_shuffle);