I'm looking for bugs, ways to make it more portable or standardized, improvements of any kind. Seems to do what it is supposed to on my Ubuntu 12.04 PC.
/* VLC-Watchdog v2
*
* A fix for stopped VLC Media Player inhibiting the power management
* daemon and preventing screen saver and/or monitor power off.
*
* Takes start | stop as command line argument.
*
* Checks if VLC Media Player is running using pgrep. If VLC is found
* to be running, queries the playback status through the DBus low level
* API. If the playback status is found to be stopped, calls the Quit()
* method via DBus. If VLC is found not to be running VLC-Watchdog sleeps
* for 10 seconds then repeats all of the above. When VLC-Watchdog receives
* the stop signal ("VLC-Watchdog stop" from the command line) it exits.
*/
#include <stdio.h>
#include <stdlib.h>
#include <dbus/dbus.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
static void skeleton_daemon();
int vlc_running();
void kill_vlc_if_stopped(int pid);
void usage(char *pname);
void sig_handler(int signo);
extern int running = 1;
int main(int argc, char *argv[]) {
FILE *fp;
int pid;
//check command line arguments for validity
if ((argc != 2) || (
(strcmp(argv[1], "start") != 0) &&
(strcmp(argv[1], "stop") != 0)))
usage(argv[0]);
//start / run actions
if (strcmp(argv[1], "start") == 0) {
skeleton_daemon(); //daemonize
//write the PID to a file for future reference
fp = fopen("/tmp/vlcwatchdog.pid", "w");
fprintf(fp, "%d\n", getpid());
fclose(fp);
//register signal handler
if (signal(SIGUSR1, sig_handler) == SIG_ERR) {
fprintf(stderr, "error while setting signal handler\n");
exit(1);
}
/* when first started and every 10 seconds thereafter, check if
* VLC is running, check if it is stopped, ask it to Quit() if it
* is stopped. Stop and exit when SIGUSR1 is received */
while (running) {
if ((pid = vlc_running())) {
kill_vlc_if_stopped(pid);
}
sleep(10);
}
//remove temporary file
if (remove("/tmp/vlcwatchdog.pid") == -1) {
fprintf(stderr, "error removing temporary file \
/tmp/vlcwatchdog.pid");
exit(1);
}
}
//stop actions
if (strcmp(argv[1], "stop") == 0) {
//retrieve PID from file
fp = fopen("/tmp/vlcwatchdog.pid", "r");
if (fscanf(fp, "%d", &pid) != 1)
fprintf(stderr, "fscanf failed\n");
fclose(fp);
//issue stop signal to VLC-Watchdog daemon
kill(pid, SIGUSR1);
}
exit(0);
}
/* deamonize the process */
static void skeleton_daemon() {
pid_t pid;
/* Fork off the parent process */
pid = fork();
/* An error occurred */
if (pid < 0)
exit(EXIT_FAILURE);
/* Success: Let the parent terminate */
if (pid > 0)
exit(EXIT_SUCCESS);
/* On success: The child process becomes session leader */
if (setsid() < 0)
exit(EXIT_FAILURE);
/* Fork off for the second time*/
pid = fork();
/* An error occurred */
if (pid < 0)
exit(EXIT_FAILURE);
/* Success: Let the parent terminate */
if (pid > 0)
exit(EXIT_SUCCESS);
/* Set new file permissions */
umask(0);
/* Change the working directory to the root directory */
/* or another appropriated directory */
if (chdir("/") != 0) {
fprintf(stderr, "chdir failed\n");
exit(1);
}
/* Close all open file descriptors */
int x;
for (x = sysconf(_SC_OPEN_MAX); x>0; x--)
{
close (x);
}
}
/* check if it is stopped, if so, tell it to Quit() */
void kill_vlc_if_stopped(int pid) {
DBusConnection *conn;
DBusError err;
DBusMessage *method_call, *reply;
DBusMessageIter iter, sub_iter;
int arg_type = 0;
const char *prop_iface = "org.mpris.MediaPlayer2.Player";
const char *prop_iface_method_name = "PlaybackStatus";
const char *pb_stat;
char name[80], buf[80];
//append the pid to the name of the destination
strcpy (name, "org.mpris.MediaPlayer2.vlc-");
sprintf (buf, "%d", pid);
strcat (name, buf);
dbus_error_init(&err); // init error
conn = dbus_bus_get(DBUS_BUS_SESSION, &err); //connect to session bus
//create the method call to be used to get playback status of VLC
method_call = dbus_message_new_method_call(name,
"/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties",
"Get");
//put the method arguments on the message
dbus_message_append_args(method_call, DBUS_TYPE_STRING, &prop_iface,
DBUS_TYPE_STRING, &prop_iface_method_name, DBUS_TYPE_INVALID);
//call the method, get the reply, check for error
reply = dbus_connection_send_with_reply_and_block (conn,
method_call, -1, &err);
if (reply == NULL) {
fprintf(stderr, "Null Reply \n %s \n", err.message);
dbus_error_free(&err);
exit(1);
}
//initialize an iterator pointing to arguments in reply message
dbus_message_iter_init (reply, &iter);
//check the type of the first argument (should be variant)
arg_type = dbus_message_iter_get_arg_type(&iter);
if (arg_type != DBUS_TYPE_VARIANT) {
fprintf(stderr, "Expected variant, got %d\n", arg_type);
exit(1);
}
//recurse into the variant container to get an iter to the string
dbus_message_iter_recurse(&iter, &sub_iter);
//check the type of the value in the variant (should be string)
arg_type = dbus_message_iter_get_arg_type(&sub_iter);
if (arg_type != DBUS_TYPE_STRING) {
fprintf(stderr, "Expected string, got %d\n", arg_type);
exit(1);
}
//extract the value of the string from sub_iter
dbus_message_iter_get_basic(&sub_iter, &pb_stat);
//if VLC is stopped, tell it to close by calling Quit()
if (strcmp("Stopped", pb_stat) == 0) {
//initialize the Quit() method call message
method_call = dbus_message_new_method_call(name,
"/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2", "Quit");
//send the Quit() message, report error if any
if (!dbus_connection_send(conn, method_call, NULL)) {
fprintf (stderr, "Send of Quit() call message failed\n");
exit(1);
}
}
return;
}
/* return PID of VLC if it is running, 0 otherwise */
int vlc_running() {
FILE *fp;
char s[64];
int pid;
fp = popen("pgrep vlc", "r");
if (fp == NULL) {
printf("NULL fp error\n");
fclose(fp);
exit(1);
}
if (fgets(s, 9, fp) != NULL) {
sscanf(s, "%d", &pid);
fclose(fp);
return pid;
}
else {
fclose(fp);
return 0;
}
}
/* Print the usage help then exit */
void usage(char *pname) {
fprintf(stderr, "usage: %s start | stop\n", pname);
exit(1);
}
/* Signal handler function */
void sig_handler(int signo) {
if (signo == SIGUSR1)
running = 0;
return;
}
This is the makefile
#-----------------------------------------------#
#
# VLC-Wathcdog Makefile
#
# using GCC C compiler
#
#-----------------------------------------------#
# Variable Definitions ( *** denotes a required entry )
EXECUTABLE=VLC-Watchdog # *** name of finished program executable
SOURCES=vlcwd.c # *** .c files separated by a space
CC=gcc # which compiler to use
CFLAGS= -g -Wall -O3 --std=gnu1x# compiler options
LIBS=dbus-1 # libraries that need pkg-config
OBJECTS=$(SOURCES:.c=.o)
# Compiler flags:
# -g -- Enable debugging
# -Wall -- Turn on all warnings
# -O3 -- make the maximum effort to optimize the code
ifeq ($(strip $(LIBS)),) # or ifneq (...
LIBFLAGS=#command-if-LIBS-empty
else #command-if-LIBS-not-empty
CFLAGS += `pkg-config --cflags $(LIBS)`
LIBFLAGS= `pkg-config --libs $(LIBS)`
endif
all: $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(CC) $(OBJECTS) -o $@ $(LIBFLAGS)
.o:
$(CC) $(CFLAGS) $< -o $@
clean:
rm $(OBJECTS) $(EXECUTABLE)