|
a/scctl_src/scctl.cpp |
|
b/scctl_src/scctl.cpp |
|
... |
|
... |
24 |
* will just keep playing the same URI), or tell a Receiver to return
|
24 |
* will just keep playing the same URI), or tell a Receiver to return
|
25 |
* to Playlist operation.
|
25 |
* to Playlist operation.
|
26 |
*
|
26 |
*
|
27 |
* To avoid encurring a discovery timeout for each op, there is a
|
27 |
* To avoid encurring a discovery timeout for each op, there is a
|
28 |
* server mode, in which a permanent process executes the above
|
28 |
* server mode, in which a permanent process executes the above
|
29 |
* commands, received on Unix socket, and returns the results.
|
29 |
* commands, received on Unix socket, and returns the results. The
|
|
|
30 |
* Unix socket name is based on the uid, so there is one per active
|
|
|
31 |
* user.
|
30 |
*
|
32 |
*
|
31 |
* When executing any of the ops from the command line, the program
|
33 |
* When executing any of the ops from the command line, the program
|
32 |
* first tries to contact the server, and does things itself if no
|
34 |
* first tries to contact the server, and does things itself if no
|
33 |
* server is found (encurring 2-3 S of timeout in the latter case).
|
35 |
* server is found (encurring 2-3 S of timeout in the latter case).
|
34 |
*/
|
36 |
*/
|
|
... |
|
... |
57 |
|
59 |
|
58 |
using namespace UPnPClient;
|
60 |
using namespace UPnPClient;
|
59 |
using namespace UPnPP;
|
61 |
using namespace UPnPP;
|
60 |
using namespace std;
|
62 |
using namespace std;
|
61 |
using namespace Songcast;
|
63 |
using namespace Songcast;
|
|
|
64 |
|
|
|
65 |
#ifdef LIBUPNPP_VERSION_MAJOR
|
|
|
66 |
#if LIBUPNPP_AT_LEAST(0,16,0)
|
|
|
67 |
#define HAVE_SETSOURCEINDEX_IN_LINN
|
|
|
68 |
#endif
|
|
|
69 |
#endif
|
62 |
|
70 |
|
63 |
#define OPT_L 0x1
|
71 |
#define OPT_L 0x1
|
64 |
#define OPT_S 0x2
|
72 |
#define OPT_S 0x2
|
65 |
#define OPT_f 0x4
|
73 |
#define OPT_f 0x4
|
66 |
#define OPT_h 0x8
|
74 |
#define OPT_h 0x8
|
|
... |
|
... |
117 |
out << endl;
|
125 |
out << endl;
|
118 |
}
|
126 |
}
|
119 |
return out.str();
|
127 |
return out.str();
|
120 |
}
|
128 |
}
|
121 |
|
129 |
|
|
|
130 |
int dosomething(int opflags, const vector<string>& args, string& out)
|
|
|
131 |
{
|
|
|
132 |
if (opflags & OPT_l) {
|
|
|
133 |
out = showReceivers(opflags);
|
|
|
134 |
} else if (opflags & OPT_L) {
|
|
|
135 |
out = showSenders(opflags);
|
|
|
136 |
} else if (opflags & OPT_r) {
|
|
|
137 |
if (args.size() < 2)
|
|
|
138 |
return 1;
|
|
|
139 |
setReceiversFromSender(args[0], vector<string>(args.begin() + 1,
|
|
|
140 |
args.end()));
|
|
|
141 |
} else if (opflags & OPT_s) {
|
|
|
142 |
if (args.size() < 2)
|
|
|
143 |
return 1;
|
|
|
144 |
setReceiversFromSender(args[0], vector<string>(args.begin() + 1,
|
|
|
145 |
args.end()));
|
|
|
146 |
} else if (opflags & OPT_x) {
|
|
|
147 |
if (args.size() < 1)
|
|
|
148 |
return 1;
|
|
|
149 |
stopReceivers(args);
|
|
|
150 |
#ifdef HAVE_SETSOURCEINDEX_IN_LINN
|
|
|
151 |
} else if ((opflags & OPT_i)) {
|
|
|
152 |
if (args.size() < 2)
|
|
|
153 |
return 1;
|
|
|
154 |
setSourceIndex(args[0], std::stoi(args[1]));
|
|
|
155 |
} else if ((opflags & OPT_I)) {
|
|
|
156 |
if (args.size() < 2)
|
|
|
157 |
return 1;
|
|
|
158 |
setSourceIndexByName(args[0], args[1]);
|
|
|
159 |
#endif
|
|
|
160 |
}
|
|
|
161 |
return 0;
|
|
|
162 |
}
|
|
|
163 |
|
122 |
static char *thisprog;
|
164 |
static char *thisprog;
|
123 |
static char usage [] =
|
165 |
static char usage [] =
|
124 |
" -l List renderers with Songcast Receiver capability\n"
|
166 |
" -l List renderers with Songcast Receiver capability\n"
|
125 |
" -L List Songcast Senders\n"
|
167 |
" -L List Songcast Senders\n"
|
126 |
" -m : for above modes: use parseable format\n"
|
168 |
" -m : for above modes: use parseable format\n"
|
127 |
"For the following options the renderers can be designated by their \n"
|
169 |
"For the following options the renderers can be designated by their \n"
|
128 |
"uid (safer) or friendly name\n"
|
170 |
"uid (safer) or friendly name\n"
|
|
|
171 |
#ifdef HAVE_SETSOURCEINDEX_IN_LINN
|
129 |
" -i <renderer> i : set source index\n"
|
172 |
" -i <renderer> i : set source index\n"
|
130 |
" -I <renderer> name : set source index by name\n"
|
173 |
" -I <renderer> name : set source index by name\n"
|
|
|
174 |
#endif
|
131 |
" -s <master> <slave> [slave ...] : Set up the slaves renderers as Songcast\n"
|
175 |
" -s <master> <slave> [slave ...] : Set up the slaves renderers as Songcast\n"
|
132 |
" Receivers and make them play from the same uri as the master receiver\n"
|
176 |
" Receivers and make them play from the same uri as the master receiver\n"
|
133 |
" -x <renderer> [renderer ...] Reset renderers from Songcast to Playlist\n"
|
177 |
" -x <renderer> [renderer ...] Reset renderers from Songcast to Playlist\n"
|
134 |
" -r <sender> <renderer> <renderer> : set up the renderers in Receiver mode\n"
|
178 |
" -r <sender> <renderer> <renderer> : set up the renderers in Receiver mode\n"
|
135 |
" playing data from the sender. This is like -s but we get the uri from \n"
|
179 |
" playing data from the sender. This is like -s but we get the uri from \n"
|
|
... |
|
... |
163 |
int ret;
|
207 |
int ret;
|
164 |
while ((ret = getopt(argc, argv, "fhmLlrsSxiI")) != -1) {
|
208 |
while ((ret = getopt(argc, argv, "fhmLlrsSxiI")) != -1) {
|
165 |
switch (ret) {
|
209 |
switch (ret) {
|
166 |
case 'f': op_flags |= OPT_f; break;
|
210 |
case 'f': op_flags |= OPT_f; break;
|
167 |
case 'h': Usage(stdout); break;
|
211 |
case 'h': Usage(stdout); break;
|
168 |
case 'l':
|
212 |
case 'l': op_flags |= OPT_l; break;
|
169 |
op_flags |= OPT_l;
|
213 |
case 'L': op_flags |= OPT_L; break;
|
170 |
break;
|
214 |
case 'm': op_flags |= OPT_m; break;
|
171 |
case 'L':
|
215 |
case 'r': op_flags |= OPT_r; break;
|
172 |
op_flags |= OPT_L;
|
216 |
case 's': op_flags |= OPT_s; break;
|
173 |
break;
|
217 |
case 'S': op_flags |= OPT_S; break;
|
174 |
case 'm':
|
218 |
case 'x': op_flags |= OPT_x; break;
|
175 |
op_flags |= OPT_m;
|
219 |
case 'i': op_flags |= OPT_i; break;
|
176 |
break;
|
220 |
case 'I': op_flags |= OPT_I; break;
|
177 |
case 'r':
|
|
|
178 |
op_flags |= OPT_r;
|
|
|
179 |
break;
|
|
|
180 |
case 's':
|
|
|
181 |
op_flags |= OPT_s;
|
|
|
182 |
break;
|
|
|
183 |
case 'S':
|
|
|
184 |
op_flags |= OPT_S;
|
|
|
185 |
break;
|
|
|
186 |
case 'x':
|
|
|
187 |
op_flags |= OPT_x;
|
|
|
188 |
break;
|
|
|
189 |
case 'i':
|
|
|
190 |
op_flags |= OPT_i;
|
|
|
191 |
break;
|
|
|
192 |
case 'I':
|
|
|
193 |
op_flags |= OPT_I;
|
|
|
194 |
break;
|
|
|
195 |
default: Usage();
|
221 |
default: Usage();
|
196 |
}
|
222 |
}
|
197 |
}
|
223 |
}
|
198 |
//fprintf(stderr, "argc %d optind %d flgs: 0x%x\n", argc, optind, op_flags);
|
|
|
199 |
|
224 |
|
200 |
// If we're not a server, try to contact one to avoid the
|
225 |
// If we're not to become a server, try to contact one to avoid
|
201 |
// discovery timeout
|
226 |
// the discovery timeout
|
202 |
if (!(op_flags & OPT_S) && tryserver(op_flags, argc -optind,
|
227 |
if (!(op_flags & OPT_S) && tryserver(op_flags, argc - optind,
|
203 |
&argv[optind])) {
|
228 |
&argv[optind])) {
|
204 |
exit(0);
|
229 |
exit(0);
|
205 |
}
|
230 |
}
|
206 |
|
231 |
|
207 |
// At least one action needed.
|
232 |
// At least one action needed.
|
208 |
if ((op_flags & ~(OPT_f|OPT_m)) == 0)
|
233 |
if ((op_flags & ~(OPT_f|OPT_m)) == 0)
|
209 |
Usage();
|
234 |
Usage();
|
210 |
|
235 |
|
|
|
236 |
// Logger::getTheLog("stderr")->setLogLevel(Logger::LLDEB0);
|
|
|
237 |
|
211 |
LibUPnP *mylib = LibUPnP::getLibUPnP();
|
238 |
LibUPnP *mylib = LibUPnP::getLibUPnP();
|
212 |
if (!mylib) {
|
239 |
if (!mylib) {
|
213 |
cerr << "Can't get LibUPnP" << endl;
|
240 |
cerr << "Can't get LibUPnP" << endl;
|
214 |
return 1;
|
241 |
return 1;
|
215 |
}
|
242 |
}
|
|
... |
|
... |
222 |
|
249 |
|
223 |
vector<string> args;
|
250 |
vector<string> args;
|
224 |
while (optind < argc) {
|
251 |
while (optind < argc) {
|
225 |
args.push_back(argv[optind++]);
|
252 |
args.push_back(argv[optind++]);
|
226 |
}
|
253 |
}
|
227 |
|
254 |
|
228 |
if ((op_flags & OPT_l)) {
|
255 |
if ((op_flags & OPT_S)) {
|
229 |
if (args.size())
|
|
|
230 |
Usage();
|
|
|
231 |
string out = showReceivers(op_flags);
|
|
|
232 |
cout << out;
|
|
|
233 |
} else if ((op_flags & OPT_L)) {
|
|
|
234 |
if (args.size())
|
|
|
235 |
Usage();
|
|
|
236 |
string out = showSenders(op_flags);
|
|
|
237 |
cout << out;
|
|
|
238 |
} else if ((op_flags & OPT_r)) {
|
|
|
239 |
if (args.size() < 2)
|
|
|
240 |
Usage();
|
|
|
241 |
setReceiversFromSender(args[0], vector<string>(args.begin() + 1,
|
|
|
242 |
args.end()));
|
|
|
243 |
} else if ((op_flags & OPT_s)) {
|
|
|
244 |
if (args.size() < 2)
|
|
|
245 |
Usage();
|
|
|
246 |
setReceiversFromReceiver(args[0], vector<string>(args.begin()+1,
|
|
|
247 |
args.end()));
|
|
|
248 |
} else if ((op_flags & OPT_x)) {
|
|
|
249 |
if (args.size() < 1)
|
|
|
250 |
Usage();
|
|
|
251 |
stopReceivers(args);
|
|
|
252 |
} else if ((op_flags & OPT_i)) {
|
|
|
253 |
if (args.size() < 2)
|
|
|
254 |
Usage();
|
|
|
255 |
setSourceIndex(args[0], std::stoi(args[1]));
|
|
|
256 |
} else if ((op_flags & OPT_I)) {
|
|
|
257 |
if (args.size() < 2)
|
|
|
258 |
Usage();
|
|
|
259 |
setSourceIndexByName(args[0], args[1]);
|
|
|
260 |
} else if ((op_flags & OPT_S)) {
|
|
|
261 |
exit(runserver());
|
256 |
exit(runserver());
|
262 |
} else {
|
257 |
} else {
|
|
|
258 |
string out;
|
|
|
259 |
if (dosomething(op_flags, args, out)) {
|
263 |
Usage();
|
260 |
Usage();
|
|
|
261 |
}
|
|
|
262 |
cout << out;
|
264 |
}
|
263 |
}
|
265 |
|
264 |
|
266 |
// If we get here, we have executed a local command. If -f is set,
|
265 |
// If we get here, we have executed a local command. If -f is set,
|
267 |
// fork and run the server code so that a next execution will use
|
266 |
// fork and run the server code so that a next execution will use
|
268 |
// this instead (and not incur the discovery timeout)
|
267 |
// this instead (and not incur the discovery timeout)
|
|
... |
|
... |
272 |
runserver();
|
271 |
runserver();
|
273 |
}
|
272 |
}
|
274 |
return 0;
|
273 |
return 0;
|
275 |
}
|
274 |
}
|
276 |
|
275 |
|
|
|
276 |
|
277 |
// The Unix socket path which we use for client-server operation
|
277 |
// The Unix socket path which we use for client-server operation
|
278 |
bool sockname(string& nm)
|
278 |
bool sockname(string& nm)
|
279 |
{
|
279 |
{
|
280 |
char buf[80];
|
280 |
char buf[80];
|
281 |
sprintf(buf, "/tmp/scctl%d", int(getuid()));
|
281 |
sprintf(buf, "/tmp/scctl%d", int(getuid()));
|
|
... |
|
... |
333 |
}
|
333 |
}
|
334 |
}
|
334 |
}
|
335 |
return true;
|
335 |
return true;
|
336 |
}
|
336 |
}
|
337 |
|
337 |
|
|
|
338 |
static vector<string> argvtov(char *argv[])
|
|
|
339 |
{
|
|
|
340 |
vector<string> out;
|
|
|
341 |
while (argv && *argv) {
|
|
|
342 |
out.push_back(*argv++);
|
|
|
343 |
}
|
|
|
344 |
return out;
|
|
|
345 |
}
|
|
|
346 |
|
338 |
bool tryserver(int opflags, int argc, char **argv)
|
347 |
bool tryserver(int opflags, int argc, char *argv[])
|
339 |
{
|
348 |
{
|
340 |
char opts[30];
|
349 |
char opts[30];
|
341 |
sprintf(opts, "0x%x", opflags);
|
350 |
sprintf(opts, "0x%x", opflags);
|
342 |
string cmd(opts);
|
351 |
string cmd(opts);
|
343 |
cmd += " ";
|
352 |
cmd += " ";
|
344 |
|
353 |
vector<string> va = argvtov(argv);
|
345 |
for (int i = 0; i < argc; i++) {
|
354 |
cmd += stringsToString(va);
|
346 |
// May need quoting here ?
|
|
|
347 |
cmd += argv[i];
|
|
|
348 |
cmd += " ";
|
|
|
349 |
}
|
|
|
350 |
cmd += "\n";
|
355 |
cmd += "\n";
|
351 |
return tryserver(cmd);
|
356 |
return tryserver(cmd);
|
352 |
}
|
357 |
}
|
353 |
|
358 |
|
354 |
|
359 |
|
|
... |
|
... |
372 |
std::unique_ptr<Netcon> conhold(con);
|
377 |
std::unique_ptr<Netcon> conhold(con);
|
373 |
|
378 |
|
374 |
// Get command
|
379 |
// Get command
|
375 |
string line;
|
380 |
string line;
|
376 |
{
|
381 |
{
|
|
|
382 |
const int LL(10240);
|
377 |
char buf[2048];
|
383 |
char buf[LL];
|
378 |
if (con->getline(buf, 2048, 2) <= 0) {
|
384 |
if (con->getline(buf, LL, 2) <= 0) {
|
379 |
LOGERR("scctl: server: getline() failed\n");
|
385 |
LOGERR("scctl: server: getline() failed\n");
|
380 |
return 1;
|
386 |
return 1;
|
381 |
}
|
387 |
}
|
382 |
line = buf;
|
388 |
line = buf;
|
383 |
}
|
389 |
}
|
384 |
|
|
|
385 |
trimstring(line, " \n");
|
390 |
trimstring(line, " \n");
|
386 |
|
|
|
387 |
LOGDEB1("scctl: server: got cmd: " << line << endl);
|
391 |
LOGDEB1("scctl: server: got cmd: " << line << endl);
|
388 |
|
392 |
|
389 |
vector<string> toks;
|
393 |
vector<string> args;
|
390 |
stringToTokens(line, toks);
|
394 |
stringToStrings(line, args);
|
391 |
if (toks.empty()) {
|
395 |
if (args.empty()) {
|
392 |
return 1;
|
396 |
return 1;
|
393 |
}
|
397 |
}
|
394 |
|
398 |
|
395 |
int opflags = strtoul(toks[0].c_str(), 0, 0);
|
399 |
int opflags = strtoul(args[0].c_str(), 0, 0);
|
396 |
|
400 |
args.erase(args.begin());
|
397 |
string out;
|
401 |
string out;
|
398 |
if (opflags & OPT_p) {
|
402 |
if (opflags & OPT_p) {
|
399 |
// ping
|
403 |
// ping
|
400 |
out = "Ok\n";
|
404 |
out = "Ok\n";
|
401 |
} else if (opflags & OPT_l) {
|
|
|
402 |
out = showReceivers(opflags);
|
|
|
403 |
} else if (opflags & OPT_L) {
|
|
|
404 |
out = showSenders(opflags);
|
|
|
405 |
} else if (opflags & OPT_s) {
|
|
|
406 |
if (toks.size() < 3)
|
|
|
407 |
return 1;
|
|
|
408 |
vector<string>::iterator beg = toks.begin();
|
|
|
409 |
beg++;
|
|
|
410 |
string master = *beg;
|
|
|
411 |
beg++;
|
|
|
412 |
vector<string> slaves(beg, toks.end());
|
|
|
413 |
ReceiverState mst;
|
|
|
414 |
getReceiverState(master, mst);
|
|
|
415 |
for (auto it = slaves.begin(); it != slaves.end(); it++) {
|
|
|
416 |
ReceiverState st;
|
|
|
417 |
getReceiverState(*it, st);
|
|
|
418 |
setReceiverPlaying(st, mst.uri, mst.meta);
|
|
|
419 |
}
|
|
|
420 |
} else if (opflags & OPT_x) {
|
|
|
421 |
if (toks.size() < 2)
|
|
|
422 |
return 1;
|
|
|
423 |
vector<string>::iterator beg = toks.begin();
|
|
|
424 |
beg++;
|
|
|
425 |
vector<string> slaves(beg, toks.end());
|
|
|
426 |
stopReceivers(slaves);
|
|
|
427 |
} else if (opflags & OPT_r) {
|
|
|
428 |
if (toks.size() < 3)
|
|
|
429 |
return 1;
|
|
|
430 |
vector<string>::iterator beg = toks.begin();
|
|
|
431 |
beg++;
|
|
|
432 |
string sender = *beg;
|
|
|
433 |
beg++;
|
|
|
434 |
vector<string> receivers(beg, toks.end());
|
|
|
435 |
setReceiversFromSender(sender, receivers);
|
|
|
436 |
} else {
|
405 |
} else {
|
437 |
LOGERR("scctl: server: bad cmd:" << toks[0] << endl);
|
406 |
dosomething(opflags, args, out);
|
438 |
return 1;
|
|
|
439 |
}
|
407 |
}
|
440 |
|
408 |
|
441 |
if (con->send(out.c_str(), out.size(), 0) < 0) {
|
409 |
if (con->send(out.c_str(), out.size(), 0) < 0) {
|
442 |
LOGERR("scctl: server: send() failed\n");
|
410 |
LOGERR("scctl: server: send() failed\n");
|
443 |
return 1;
|
411 |
return 1;
|