Add following events: PSTATE, REPEAT, RANDOM, CONSUME, SINGLE, SONGID
[mpdcron.git] / main.c
blobea44a153a0d61f50478f2d95a80c759e620fef08
1 /* MPDCron
2 * Copyright (C) 2009-2009 Qball Cow <qball@sarine.nl>
3 * Project homepage: http://mpd.wikia.com/
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <stdbool.h>
24 #include <signal.h>
25 #include <errno.h>
26 #include <config.h>
27 #include "mpdcron.h"
29 #define MAX_LINE_LENGTH 1024*4
31 static void event_entry_free(struct event_entry *entry)
33 if(entry == NULL) return;
34 if(entry->command) free(entry->command);
35 free(entry);
38 unsigned int num_entries = 0;
39 struct event_entry **entries = NULL;
40 struct mpd_connection *conn = NULL;
42 /* Code borrowed from mpdscribble */
43 static bool quit = false;
44 static void signal_handler(int signum)
46 struct mpd_connection *conn2 = conn;
47 printf("Got signal: %i\n", signum);
48 conn = NULL;
49 quit = true;
50 if(conn2)
51 mpd_connection_free(conn2);
52 return;
55 static void
56 x_sigaction(int signum, const struct sigaction *act)
58 if (sigaction(signum, act, NULL) < 0) {
59 perror("sigaction()");
60 exit(EXIT_FAILURE);
64 static void
65 setup_signals(void)
67 struct sigaction sa;
69 sigemptyset(&sa.sa_mask);
70 sa.sa_flags = 0;
71 sa.sa_handler = signal_handler;
72 x_sigaction(SIGINT, &sa);
73 x_sigaction(SIGTERM, &sa);
74 x_sigaction(SIGHUP, &sa);
75 sa.sa_handler = SIG_IGN;
76 x_sigaction(SIGPIPE, &sa);
79 /* Parse the config file, return all the events to watch */
80 static enum mpd_idle parse_cronfile(FILE *fp)
82 enum mpd_idle values_events = 0;
83 char buffer[MAX_LINE_LENGTH*4];
85 while(fgets(buffer,MAX_LINE_LENGTH*4, fp) != NULL)
87 unsigned int i = 0;
88 /* comment line */
89 if(buffer[0] == '#') continue;
90 /* Get the first space/tab, where the line is split. */
91 for(i =0; buffer[i] != '\0' && buffer[i] != ' ' && buffer[i] != '\t'; i++);
92 if((buffer[i] == ' ' || buffer[i] == '\t') && (i+2) < strlen(buffer))
94 enum mpd_idle event=0;
95 enum private_event pevent =0;
96 char *item;
97 char *saveptr;
98 buffer[i] = '\0';
99 item = strtok_r(&buffer[0], "|", &saveptr);
100 if(item)do{
101 if(strncmp(item, "PLAYER",strlen("PLAYER")) == 0)
102 event |= MPD_IDLE_PLAYER;
103 else if(strncmp(item, "DATABASE",strlen("DATABASE")) == 0)
104 event |= MPD_IDLE_DATABASE;
105 else if(strncmp(item, "STORED_PLAYLIST",strlen("STORED_PLAYLIST")) == 0)
106 event |= MPD_IDLE_STORED_PLAYLIST;
107 else if(strncmp(item, "QUEUE",strlen("QUEUE")) == 0)
108 event |= MPD_IDLE_QUEUE;
109 else if(strncmp(item, "MIXER",strlen("MIXER")) == 0)
110 event |= MPD_IDLE_MIXER;
111 else if(strncmp(item, "OUTPUT",strlen("OUTPUT")) == 0)
112 event |= MPD_IDLE_OUTPUT;
113 else if(strncmp(item, "OPTIONS",strlen("OPTIONS")) == 0)
114 event |= MPD_IDLE_OPTIONS;
115 else if(strncmp(item, "UPDATE",strlen("UPDATE")) == 0)
116 event |= MPD_IDLE_UPDATE;
117 else if (strncmp(item, "CONNECTED", strlen("CONNECTED")) == 0)
118 pevent |= CONNECTED;
119 else if (strncmp(item, "DISCONNECTED", strlen("DISCONNECTED")) == 0)
120 pevent |= DISCONNECTED;
121 else if (strncmp(item, "SONGID", strlen("SONGID")) == 0)
122 pevent |= SONG_ID;
123 else if (strncmp(item, "PSTATE", strlen("PSTATE")) == 0)
124 pevent |= PLAYER_STATE;
125 else if (strncmp(item, "REPEAT", strlen("REPEAT")) == 0)
126 pevent |= OPTIONS_REPEAT;
127 else if (strncmp(item, "RANDOM", strlen("RANDOM")) == 0)
128 pevent |= OPTIONS_RANDOM;
129 else if (strncmp(item, "CONSUME", strlen("CONSUME")) == 0)
130 pevent |= OPTIONS_CONSUME;
131 else if (strncmp(item, "SINGLE", strlen("SINGLE")) == 0)
132 pevent |= OPTIONS_SINGLE;
133 else {
134 fprintf(stderr, "Unknown event found: %s\n", item);
135 exit(EXIT_FAILURE);
137 item = strtok_r(NULL, "|",&saveptr);
138 }while(item != NULL);
139 num_entries++;
140 entries = realloc(entries, (size_t)(num_entries+1)*sizeof(*entries));
141 entries[num_entries-1] = malloc(sizeof(struct event_entry));
142 entries[num_entries-1]->event = event;
143 entries[num_entries-1]->pevent = pevent;
144 entries[num_entries-1]->command = strdup(&buffer[i+1]);
145 entries[num_entries] = NULL;
147 if((pevent&(SONG_ID|PLAYER_STATE))>0) event |= MPD_IDLE_PLAYER;
148 if((pevent&(OPTIONS_SINGLE|OPTIONS_REPEAT|OPTIONS_RANDOM|OPTIONS_CONSUME))>0) event |= MPD_IDLE_OPTIONS;
149 values_events |= event;
152 return values_events;
154 #define SIGNAL_PEVENT(a) for(unsigned int i=0; i< num_entries; i++){\
155 if((a&(entries[i]->pevent)) != 0) {\
156 do_command(entries[i]);\
160 #define SIGNAL_EVENT(a) for(unsigned int i=0; i< num_entries; i++){\
161 if((a&(entries[i]->event)) != 0) {\
162 do_command(entries[i]);\
166 int main (int argc, char **argv)
168 bool daemonize = true;
169 bool file_specified = false;
170 const char *hostname = NULL;
171 int port = 0;
173 struct mpd_status *status = NULL;
174 enum mpd_idle listen_to_events=0;
177 * Parse the commandline options
179 if(argc >= 2)
181 for(unsigned int i=1;i<(unsigned int)argc;i++)
183 if(argv[i][0] == '-'){
184 if(strcmp(argv[i], "-f") ==0) daemonize = false;
185 else if (strncmp(argv[i], "--hostname=", 11) == 0){
186 hostname = &(argv[i][11]);
187 } else if (strncmp(argv[i], "--port=", 7) == 0){
188 port = atoi(&(argv[i][7]));
189 } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
190 printf("%s\n", PACKAGE_STRING);
191 exit(EXIT_SUCCESS);
192 } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
193 printf("%s <OPTIONS> <CONFIG FILE> ...\n\n", PACKAGE_STRING);
194 printf("-f Do not daemonize\n");
195 printf("--hostname=<password>@<name> Set hostname\n");
196 printf("--port=port Set port\n");
197 printf("--help -h Help message\n");
198 printf("--version -v Print version\n");
199 printf("\n");
200 exit(EXIT_SUCCESS);
201 } else {
202 fprintf(stderr, "Unknown parameter: %s\n", argv[i]);
203 exit (EXIT_FAILURE);
205 } else {
206 FILE *fp = NULL;
207 /* Open the file */
208 fp = fopen(argv[i], "r");
209 if(!fp) {
210 fprintf(stderr, "Failed to open file: %s: %s\n", argv[i], strerror(errno));
211 exit(EXIT_FAILURE);
213 /* Parse file */
214 listen_to_events |= parse_cronfile(fp);
215 fclose(fp);
216 file_specified = true;
221 * If no file specified try ~/.mpdcron
223 if(!file_specified)
225 FILE *fp = NULL;
226 char buffer[MAX_LINE_LENGTH];
227 const char *homedir = getenv("HOME");
228 if(homedir == NULL) {
229 fprintf(stderr, "Homedir not found\n");
230 exit(EXIT_FAILURE);
232 /* Create path to config file */
233 snprintf(buffer,MAX_LINE_LENGTH, "%s/.mpdcron", homedir);
234 /* Open the file */
235 fp = fopen(buffer, "r");
236 if(!fp) {
237 /* not found is fatal here */
238 fprintf(stderr, "No mpdcron file found\n");
239 exit(EXIT_FAILURE);
241 /* Parse file */
242 listen_to_events |= parse_cronfile(fp);
243 fclose(fp);
246 /* Check if there is atleast one event configured */
247 if(num_entries == 0){
248 fprintf(stderr, "No entry specified. Nothing to do.\nQuitting");
249 return EXIT_FAILURE;
252 /* Setup the signal handling */
253 setup_signals();
254 /* Daemonize */
255 if(daemonize) {
256 switch (fork()){
257 case 0:
258 break;
259 case -1:
260 default:
261 exit(EXIT_SUCCESS);
265 /* Try to connect to mpd. */
267 conn = mpd_connection_new(hostname, port, 5000);
268 if(conn == NULL) {
269 fprintf(stderr, "Failed to allocate memory\n");
270 return EXIT_FAILURE;
273 if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) {
274 fprintf(stderr,"%s\n", mpd_connection_get_error_message(conn));
275 mpd_connection_free(conn);
276 conn = NULL;
277 }else{
278 SIGNAL_PEVENT(CONNECTED);
279 /* If connect success break out of while loop */
280 break;
282 /* sleep and sighandler does not work, fix this */
283 sleep(5);
284 }while(!quit);
286 if(!quit){
287 status = mpd_run_status(conn);
289 /* loop */
290 while(!quit)
292 /* Block until mpd send command */
293 enum mpd_idle idle_event = mpd_run_idle_mask(conn,listen_to_events|MPD_IDLE_PLAYER);
294 /* Called to quit */
295 if(quit) break;
297 if(conn && (idle_event) > 0 ) {
298 struct mpd_status *st2 = mpd_run_status(conn);
299 enum private_event pevent = 0;
300 if(status)
302 if(mpd_status_get_song_id(status) != mpd_status_get_song_id(st2))
303 pevent |= SONG_ID;
304 if(mpd_status_get_state(status) != mpd_status_get_state(st2))
305 pevent |= PLAYER_STATE;
306 if(mpd_status_get_repeat(status) != mpd_status_get_repeat(st2))
307 pevent |= OPTIONS_REPEAT;
308 if(mpd_status_get_random(status) != mpd_status_get_random(st2))
309 pevent |= OPTIONS_RANDOM;
310 if(mpd_status_get_consume(status) != mpd_status_get_consume(st2))
311 pevent |= OPTIONS_CONSUME;
312 if(mpd_status_get_single(status) != mpd_status_get_single(st2))
313 pevent |= OPTIONS_SINGLE;
314 mpd_status_free(status);
316 if(pevent) {
317 SIGNAL_PEVENT(pevent);
319 status = st2;
321 SIGNAL_EVENT(idle_event);
323 /* Idle event was 0, this mean something went wrong.. */
324 if(idle_event == 0) {
325 enum mpd_error error = mpd_connection_get_error(conn);
326 /* If the connection gave a timeout or an explicit close, try to reconnect */
327 if(conn && (error&(MPD_ERROR_TIMEOUT|MPD_ERROR_CLOSED))) {
328 /* We got disconnected somehow. Lets try to reconnect */
329 mpd_connection_free(conn);
330 conn = NULL;
331 SIGNAL_PEVENT(DISCONNECTED);
333 /* Reconnect */
334 fprintf(stdout,"reconnect\n");
335 conn = mpd_connection_new(hostname, port, 5000);
336 /* Check success */
337 if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) {
338 fprintf(stderr,"%s\n", mpd_connection_get_error_message(conn));
339 mpd_connection_free(conn);
340 conn = NULL;
341 sleep(5);
343 else
344 break;
345 /* Retry util success */
346 }while(!quit);
348 SIGNAL_PEVENT(CONNECTED);
350 /* Continue with the while loop */
351 continue;
353 /* Else stop loop */
354 break;
357 printf("quitting\n");
358 if(status)
359 mpd_status_free(status);
360 /* Close/cleanup connection */
361 if(conn)
362 mpd_connection_free(conn);
363 /* Free the entries */
364 for(unsigned int i=0;i<num_entries;i++)
365 event_entry_free(entries[i]);
366 free(entries);