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.
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
);
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
);
51 mpd_connection_free(conn2
);
56 x_sigaction(int signum
, const struct sigaction
*act
)
58 if (sigaction(signum
, act
, NULL
) < 0) {
59 perror("sigaction()");
69 sigemptyset(&sa
.sa_mask
);
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
)
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;
99 item
= strtok_r(&buffer
[0], "|", &saveptr
);
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)
119 else if (strncmp(item
, "DISCONNECTED", strlen("DISCONNECTED")) == 0)
120 pevent
|= DISCONNECTED
;
121 else if (strncmp(item
, "SONGID", strlen("SONGID")) == 0)
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
;
134 fprintf(stderr
, "Unknown event found: %s\n", item
);
137 item
= strtok_r(NULL
, "|",&saveptr
);
138 }while(item
!= NULL
);
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
;
173 struct mpd_status
*status
= NULL
;
174 enum mpd_idle listen_to_events
=0;
177 * Parse the commandline options
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
);
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");
202 fprintf(stderr
, "Unknown parameter: %s\n", argv
[i
]);
208 fp
= fopen(argv
[i
], "r");
210 fprintf(stderr
, "Failed to open file: %s: %s\n", argv
[i
], strerror(errno
));
214 listen_to_events
|= parse_cronfile(fp
);
216 file_specified
= true;
221 * If no file specified try ~/.mpdcron
226 char buffer
[MAX_LINE_LENGTH
];
227 const char *homedir
= getenv("HOME");
228 if(homedir
== NULL
) {
229 fprintf(stderr
, "Homedir not found\n");
232 /* Create path to config file */
233 snprintf(buffer
,MAX_LINE_LENGTH
, "%s/.mpdcron", homedir
);
235 fp
= fopen(buffer
, "r");
237 /* not found is fatal here */
238 fprintf(stderr
, "No mpdcron file found\n");
242 listen_to_events
|= parse_cronfile(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");
252 /* Setup the signal handling */
265 /* Try to connect to mpd. */
267 conn
= mpd_connection_new(hostname
, port
, 5000);
269 fprintf(stderr
, "Failed to allocate memory\n");
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
);
278 SIGNAL_PEVENT(CONNECTED
);
279 /* If connect success break out of while loop */
282 /* sleep and sighandler does not work, fix this */
287 status
= mpd_run_status(conn
);
292 /* Block until mpd send command */
293 enum mpd_idle idle_event
= mpd_run_idle_mask(conn
,listen_to_events
|MPD_IDLE_PLAYER
);
297 if(conn
&& (idle_event
) > 0 ) {
298 struct mpd_status
*st2
= mpd_run_status(conn
);
299 enum private_event pevent
= 0;
302 if(mpd_status_get_song_id(status
) != mpd_status_get_song_id(st2
))
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
);
317 SIGNAL_PEVENT(pevent
);
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
);
331 SIGNAL_PEVENT(DISCONNECTED
);
334 fprintf(stdout
,"reconnect\n");
335 conn
= mpd_connection_new(hostname
, port
, 5000);
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
);
345 /* Retry util success */
348 SIGNAL_PEVENT(CONNECTED
);
350 /* Continue with the while loop */
357 printf("quitting\n");
359 mpd_status_free(status
);
360 /* Close/cleanup connection */
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
]);