2 * Copyright 2005-2006 Timo Hirvonen
12 #include <sys/types.h>
13 #include <sys/socket.h>
15 #include <netinet/in.h>
16 #include <arpa/inet.h>
24 static int raw_args
= 0;
27 static void gethostbyname_failed(void)
29 const char *error
= "Unknown error.";
34 error
= "Host not found.";
37 error
= "A non-recoverable name server error.";
40 error
= "A temporary error occurred on an authoritative name server.";
43 die("gethostbyname: %s\n", error
);
46 static void read_answer(void)
53 int rc
= read(sock
, buf
, sizeof(buf
));
60 die("unexpected EOF\n");
64 // Last line should be empty (i.e. read "\n" or "...\n\n").
65 // Write everything but the last \n to stdout.
66 if (got_nl
&& buf
[0] == '\n')
68 if (len
== 1 && buf
[0] == '\n')
70 if (rc
> 1 && buf
[rc
- 1] == '\n' && buf
[rc
- 2] == '\n') {
71 write_all(1, buf
, rc
- 1);
74 got_nl
= buf
[rc
- 1] == '\n';
75 write_all(1, buf
, rc
);
79 static void write_line(const char *line
)
81 if (write_all(sock
, line
, strlen(line
)) == -1)
87 static void send_cmd(const char *format
, ...)
93 vsnprintf(buf
, sizeof(buf
), format
, ap
);
99 static void remote_connect(const char *address
)
103 struct sockaddr_un un
;
104 struct sockaddr_in in
;
108 if (strchr(address
, '/')) {
110 warn("password ignored for unix connections\n");
113 addr
.sa
.sa_family
= AF_UNIX
;
114 strncpy(addr
.un
.sun_path
, address
, sizeof(addr
.un
.sun_path
) - 1);
116 addrlen
= sizeof(addr
.un
);
118 char *s
= strchr(address
, ':');
119 int port
= DEFAULT_PORT
;
120 struct hostent
*hent
;
123 die("password required for tcp/ip connection\n");
129 hent
= gethostbyname(address
);
131 gethostbyname_failed();
133 addr
.sa
.sa_family
= hent
->h_addrtype
;
134 switch (addr
.sa
.sa_family
) {
136 memcpy(&addr
.in
.sin_addr
, hent
->h_addr_list
[0], hent
->h_length
);
137 addr
.in
.sin_port
= htons(port
);
139 addrlen
= sizeof(addr
.in
);
142 die("unsupported address type\n");
146 sock
= socket(addr
.sa
.sa_family
, SOCK_STREAM
, 0);
150 if (connect(sock
, &addr
.sa
, addrlen
)) {
151 if (errno
== ENOENT
|| errno
== ECONNREFUSED
) {
152 /* "cmus-remote -C" can be used to check if cmus is running */
154 warn("cmus is not running\n");
157 die_errno("connect");
161 send_cmd("%s\n", passwd
);
164 static char *file_url_absolute(const char *str
)
168 if (strncmp(str
, "http://", 7) == 0)
171 absolute
= path_absolute(str
);
172 if (absolute
== NULL
)
173 die_errno("get_current_dir_name");
201 #define NR_FLAGS (FLAG_RAW + 1)
204 static struct option options
[NR_FLAGS
+ 1] = {
216 { 'R', "repeat", 0 },
217 { 'S', "shuffle", 0 },
218 { 'v', "volume", 1 },
222 { 'l', "library", 0 },
223 { 'P', "playlist", 0 },
231 static int flags
[NR_FLAGS
] = { 0, };
233 static const char *usage
=
234 "Usage: %s [OPTION]... [FILE|DIR|PLAYLIST]...\n"
235 " or: %s -C COMMAND...\n"
237 "Control cmus through socket.\n"
239 " --server ADDR connect using ADDR instead of ~/.cmus/socket\n"
240 " ADDR is either a UNIX socket or host[:port]\n"
241 " WARNING: using TCP/IP is insecure!\n"
242 " --passwd PASSWD password to use for TCP/IP connection\n"
243 " --help display this help and exit\n"
244 " --version " VERSION
"\n"
247 " -p, --play player-play\n"
248 " -u, --pause player-pause\n"
249 " -s, --stop player-stop\n"
250 " -n, --next player-next\n"
251 " -r, --prev player-prev\n"
252 " -f, --file player-play FILE\n"
253 " -R, --repeat toggle repeat\n"
254 " -S, --shuffle toggle shuffle\n"
255 " -v, --volume VOL vol VOL\n"
256 " -k, --seek SEEK seek SEEK\n"
257 " -Q, --query get player status (same as -C status)\n"
259 " -l, --library modify library instead of playlist\n"
260 " -P, --playlist modify playlist (default)\n"
261 " -q, --queue modify play queue instead of playlist\n"
262 " -c, --clear clear playlist, library (-l) or play queue (-q)\n"
264 " Add FILE/DIR/PLAYLIST to playlist, library (-l) or play queue (-q).\n"
267 " -C, --raw treat arguments (instead of stdin) as raw commands\n"
269 " By default cmus-remote reads raw commands from stdin (one command per line).\n"
271 "Report bugs to <cmus-devel@lists.sourceforge.net>.\n";
273 int main(int argc
, char *argv
[])
275 char server_buf
[256];
277 char *play_file
= NULL
;
284 program_name
= argv
[0];
290 idx
= get_option(&argv
, options
, &arg
);
295 switch ((enum flags
)idx
) {
297 printf(usage
, program_name
, program_name
, program_name
);
300 printf("cmus " VERSION
"\nCopyright 2004-2006 Timo Hirvonen\n");
349 if (nr_cmds
&& raw_args
)
350 die("don't mix raw and cooked stuff\n");
352 if (server
== NULL
) {
353 const char *config_dir
= getenv("CMUS_HOME");
355 if (config_dir
&& config_dir
[0]) {
356 snprintf(server_buf
, sizeof(server_buf
), "%s/socket", config_dir
);
358 const char *home
= getenv("HOME");
361 die("error: environment variable HOME not set\n");
362 snprintf(server_buf
, sizeof(server_buf
), "%s/.cmus/socket", home
);
367 remote_connect(server
);
371 send_cmd("%s\n", *argv
++);
375 if (nr_cmds
== 0 && argv
[0] == NULL
) {
378 while (fgets(line
, sizeof(line
), stdin
))
383 if (flags
[FLAG_CLEAR
])
384 send_cmd("clear -%c\n", context
);
385 for (i
= 0; argv
[i
]; i
++) {
386 char *filename
= file_url_absolute(argv
[i
]);
388 send_cmd("add -%c %s\n", context
, filename
);
391 if (flags
[FLAG_REPEAT
])
392 send_cmd("toggle repeat\n");
393 if (flags
[FLAG_SHUFFLE
])
394 send_cmd("toggle shuffle\n");
395 if (flags
[FLAG_STOP
])
396 send_cmd("player-stop\n");
397 if (flags
[FLAG_NEXT
])
398 send_cmd("player-next\n");
399 if (flags
[FLAG_PREV
])
400 send_cmd("player-prev\n");
401 if (flags
[FLAG_PLAY
])
402 send_cmd("player-play\n");
403 if (flags
[FLAG_PAUSE
])
404 send_cmd("player-pause\n");
405 if (flags
[FLAG_FILE
])
406 send_cmd("player-play %s\n", file_url_absolute(play_file
));
408 send_cmd("vol %s\n", volume
);
410 send_cmd("seek %s\n", seek
);
412 send_cmd("status\n");