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 write_line(const char *line
)
48 if (write_all(sock
, line
, strlen(line
)) == -1)
52 static void send_cmd(const char *format
, ...)
58 vsnprintf(buf
, sizeof(buf
), format
, ap
);
64 static void remote_connect(const char *address
)
68 struct sockaddr_un un
;
69 struct sockaddr_in in
;
73 if (strchr(address
, '/')) {
75 warn("password ignored for unix connections\n");
78 addr
.sa
.sa_family
= AF_UNIX
;
79 strncpy(addr
.un
.sun_path
, address
, sizeof(addr
.un
.sun_path
) - 1);
81 addrlen
= sizeof(addr
.un
);
83 char *s
= strchr(address
, ':');
84 int port
= DEFAULT_PORT
;
88 die("password required for tcp/ip connection\n");
94 hent
= gethostbyname(address
);
96 gethostbyname_failed();
98 addr
.sa
.sa_family
= hent
->h_addrtype
;
99 switch (addr
.sa
.sa_family
) {
101 memcpy(&addr
.in
.sin_addr
, hent
->h_addr_list
[0], hent
->h_length
);
102 addr
.in
.sin_port
= htons(port
);
104 addrlen
= sizeof(addr
.in
);
107 die("unsupported address type\n");
111 sock
= socket(addr
.sa
.sa_family
, SOCK_STREAM
, 0);
115 if (connect(sock
, &addr
.sa
, addrlen
)) {
116 if (errno
== ENOENT
|| errno
== ECONNREFUSED
) {
117 /* "cmus-remote -C" can be used to check if cmus is running */
119 warn("cmus is not running\n");
122 die_errno("connect");
126 send_cmd("%s\n", passwd
);
129 static char *file_url_absolute(const char *str
)
133 if (strncmp(str
, "http://", 7) == 0)
136 absolute
= path_absolute(str
);
137 if (absolute
== NULL
)
138 die_errno("get_current_dir_name");
165 #define NR_FLAGS (FLAG_RAW + 1)
168 static struct option options
[NR_FLAGS
+ 1] = {
180 { 'R', "repeat", 0 },
181 { 'S', "shuffle", 0 },
182 { 'v', "volume", 1 },
185 { 'l', "library", 0 },
186 { 'P', "playlist", 0 },
194 static int flags
[NR_FLAGS
] = { 0, };
196 static const char *usage
=
197 "Usage: %s [OPTION]... [FILE|DIR|PLAYLIST]...\n"
198 " or: %s -C COMMAND...\n"
200 "Control cmus through socket.\n"
202 " --server ADDR connect using ADDR instead of ~/.cmus/socket\n"
203 " ADDR is either a UNIX socket or host[:port]\n"
204 " WARNING: using TCP/IP is insecure!\n"
205 " --passwd PASSWD password to use for TCP/IP connection\n"
206 " --help display this help and exit\n"
207 " --version " VERSION
"\n"
210 " -p, --play player-play\n"
211 " -u, --pause player-pause\n"
212 " -s, --stop player-stop\n"
213 " -n, --next player-next\n"
214 " -r, --prev player-prev\n"
215 " -f, --file player-play FILE\n"
216 " -R, --repeat toggle repeat\n"
217 " -S, --shuffle toggle shuffle\n"
218 " -v, --volume VOL vol VOL\n"
219 " -k, --seek SEEK seek SEEK\n"
221 " -l, --library modify library instead of playlist\n"
222 " -P, --playlist modify playlist (default)\n"
223 " -q, --queue modify play queue instead of playlist\n"
224 " -c, --clear clear playlist, library (-l) or play queue (-q)\n"
226 " Add FILE/DIR/PLAYLIST to playlist, library (-l) or play queue (-q).\n"
229 " -C, --raw treat arguments (instead of stdin) as raw commands\n"
231 " By default cmus-remote reads raw commands from stdin (one command per line).\n"
233 "Report bugs to <cmus-devel@lists.sourceforge.net>.\n";
235 int main(int argc
, char *argv
[])
237 char server_buf
[256];
239 char *play_file
= NULL
;
245 program_name
= argv
[0];
251 idx
= get_option(&argv
, options
, &arg
);
256 switch ((enum flags
)idx
) {
258 printf(usage
, program_name
, program_name
, program_name
);
261 printf("cmus " VERSION
"\nCopyright 2004-2006 Timo Hirvonen\n");
306 if (nr_cmds
&& raw_args
)
307 die("don't mix raw and cooked stuff\n");
309 if (server
== NULL
) {
310 const char *config_dir
= getenv("CMUS_HOME");
312 if (config_dir
&& config_dir
[0]) {
313 snprintf(server_buf
, sizeof(server_buf
), "%s/socket", config_dir
);
315 const char *home
= getenv("HOME");
318 die("error: environment variable HOME not set\n");
319 snprintf(server_buf
, sizeof(server_buf
), "%s/.cmus/socket", home
);
324 remote_connect(server
);
328 send_cmd("%s\n", *argv
++);
332 if (nr_cmds
== 0 && argv
[0] == NULL
) {
335 while (fgets(line
, sizeof(line
), stdin
))
340 if (flags
[FLAG_CLEAR
])
341 send_cmd("clear -%c\n", context
);
342 for (i
= 0; argv
[i
]; i
++) {
343 char *filename
= file_url_absolute(argv
[i
]);
345 send_cmd("add -%c %s\n", context
, filename
);
348 if (flags
[FLAG_REPEAT
])
349 send_cmd("toggle repeat\n");
350 if (flags
[FLAG_SHUFFLE
])
351 send_cmd("toggle shuffle\n");
352 if (flags
[FLAG_STOP
])
353 send_cmd("player-stop\n");
354 if (flags
[FLAG_NEXT
])
355 send_cmd("player-next\n");
356 if (flags
[FLAG_PREV
])
357 send_cmd("player-prev\n");
358 if (flags
[FLAG_PLAY
])
359 send_cmd("player-play\n");
360 if (flags
[FLAG_PAUSE
])
361 send_cmd("player-pause\n");
362 if (flags
[FLAG_FILE
])
363 send_cmd("player-play %s\n", file_url_absolute(play_file
));
365 send_cmd("vol %s\n", volume
);
367 send_cmd("seek %s\n", seek
);