8
\$\begingroup\$

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)
\$\endgroup\$
1
  • \$\begingroup\$ I'm adding error logging and testing if the .pid file exists before trying to open it during a stop. \$\endgroup\$
    – JB0x2D1
    Jan 26, 2014 at 23:47

1 Answer 1

5
\$\begingroup\$

Just a few simple things:

  1. This:

    strcpy (name, "org.mpris.MediaPlayer2.vlc-");
    sprintf (buf, "%d", pid);
    strcat (name, buf);
    

    can be written as

    sprintf(name, "%s%d", "org.mpris.MediaPlayer2.vlc-", pid);
    

    removes the need for buf and makes it a bit cleaner.

  2. In general your error logging is a bit adventurous. Sometimes you obtain the file pointer to the error log in which case you usually close it (but it's kinda ugly having to remember this) and sometimes you just log a string in which case you don't close the file pointer. You should be able to just do it with one function:

    void log_error(const char* format, ...)
    {
        va_list args;
        FILE *errlog;
    
        errlog = fopen("/tmp/vlcwderr.log", "a");
    
        va_start(args, format);
        vfprintf(errlog, format, args);
        va_end(args);
    
        fclose(errlog);
        return;
    }
    
\$\endgroup\$
1
  • \$\begingroup\$ Thank you. That va_ family is pretty handy. Never seen that before but then I'm self taught so that doesn't mean much lol \$\endgroup\$
    – JB0x2D1
    Jan 27, 2014 at 15:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.