mirror of
https://github.com/SuperBFG7/ympd
synced 2024-09-29 22:50:40 +00:00
Feat: add ability to define and execute system commands, eg. reboot and shutdown
This commit is contained in:
parent
91f5d4131d
commit
e371453b7b
@ -58,4 +58,5 @@ install(DIRECTORY htdocs/assets DESTINATION share/${PROJECT_NAME}/htdocs)
|
|||||||
install(DIRECTORY DESTINATION ../var/lib/${PROJECT_NAME}/pics)
|
install(DIRECTORY DESTINATION ../var/lib/${PROJECT_NAME}/pics)
|
||||||
install(DIRECTORY DESTINATION ../var/lib/${PROJECT_NAME}/tmp)
|
install(DIRECTORY DESTINATION ../var/lib/${PROJECT_NAME}/tmp)
|
||||||
install(DIRECTORY DESTINATION ../var/lib/${PROJECT_NAME}/state)
|
install(DIRECTORY DESTINATION ../var/lib/${PROJECT_NAME}/state)
|
||||||
|
install(DIRECTORY DESTINATION ../etc/${PROJECT_NAME}/syscmds)
|
||||||
install(DIRECTORY dist/smartpls DESTINATION ../var/lib/${PROJECT_NAME})
|
install(DIRECTORY dist/smartpls DESTINATION ../var/lib/${PROJECT_NAME})
|
||||||
|
1
dist/syscmds/Reboot
vendored
Normal file
1
dist/syscmds/Reboot
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
sudo /sbin/reboot
|
1
dist/syscmds/Shutdown
vendored
Normal file
1
dist/syscmds/Shutdown
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
sudo /sbin/halt
|
@ -34,6 +34,7 @@
|
|||||||
<a class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#modalSettings">Settings</a>
|
<a class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#modalSettings">Settings</a>
|
||||||
<a class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#modalAbout">About</a>
|
<a class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#modalAbout">About</a>
|
||||||
<a id="nav-add2homescreen" class="dropdown-item text-light bg-dark hide" href="#">Add2HomeScreen</a>
|
<a id="nav-add2homescreen" class="dropdown-item text-light bg-dark hide" href="#">Add2HomeScreen</a>
|
||||||
|
<div id="syscmds"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-toolbar col-auto pl-0 pr-0">
|
<div class="btn-toolbar col-auto pl-0 pr-0">
|
||||||
|
@ -379,13 +379,21 @@ function appInit() {
|
|||||||
addFilterLetter('BrowseDatabaseFilterLetters');
|
addFilterLetter('BrowseDatabaseFilterLetters');
|
||||||
addFilterLetter('BrowsePlaylistsFilterLetters');
|
addFilterLetter('BrowsePlaylistsFilterLetters');
|
||||||
|
|
||||||
|
document.getElementById('syscmds').addEventListener('click', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (event.target.nodeName == 'A') {
|
||||||
|
var cmd = JSON.parse(event.target.getAttribute('data-href'));
|
||||||
|
if (typeof window[cmd.cmd] === 'function')
|
||||||
|
window[cmd.cmd](... cmd.options);
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
|
||||||
var hrefs = document.querySelectorAll('[data-href]');
|
var hrefs = document.querySelectorAll('[data-href]');
|
||||||
var hrefsLen = hrefs.length;
|
var hrefsLen = hrefs.length;
|
||||||
for (var i = 0; i < hrefsLen; i++) {
|
for (var i = 0; i < hrefsLen; i++) {
|
||||||
hrefs[i].classList.add('clickable');
|
hrefs[i].classList.add('clickable');
|
||||||
hrefs[i].addEventListener('click', function(event) {
|
hrefs[i].addEventListener('click', function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
//event.stopPropagation();
|
|
||||||
var cmd = JSON.parse(this.getAttribute('data-href'));
|
var cmd = JSON.parse(this.getAttribute('data-href'));
|
||||||
if (typeof window[cmd.cmd] === 'function') {
|
if (typeof window[cmd.cmd] === 'function') {
|
||||||
switch(cmd.cmd) {
|
switch(cmd.cmd) {
|
||||||
@ -934,9 +942,18 @@ function parseSettings(obj) {
|
|||||||
addTagList('searchqueuetag', true);
|
addTagList('searchqueuetag', true);
|
||||||
addTagList('searchtags', true);
|
addTagList('searchtags', true);
|
||||||
|
|
||||||
for (var i = 0; i < obj.data.tags.length; i++) {
|
for (var i = 0; i < obj.data.tags.length; i++)
|
||||||
app.apps.Browse.tabs.Database.views[obj.data.tags[i]] = { "state": "0/-/", "scrollPos": 0 };
|
app.apps.Browse.tabs.Database.views[obj.data.tags[i]] = { "state": "0/-/", "scrollPos": 0 };
|
||||||
|
|
||||||
|
var syscmdsList = '';
|
||||||
|
var syscmdsListLen = obj.data.syscmds.length;
|
||||||
|
if (syscmdsListLen > 0) {
|
||||||
|
syscmdsList = '<div class="dropdown-divider"></div>';
|
||||||
|
for (var i = 0; i < syscmdsListLen; i++)
|
||||||
|
syscmdsList += '<a class="dropdown-item text-light bg-dark" href="#" data-href=\'{"cmd": "execSyscmd", "options": ["' +
|
||||||
|
obj.data.syscmds[i] + '"]}\'>' + obj.data.syscmds[i] + '</a>';
|
||||||
}
|
}
|
||||||
|
document.getElementById('syscmds').innerHTML = syscmdsList;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSettings() {
|
function getSettings() {
|
||||||
@ -1605,6 +1622,10 @@ function parseSongDetails(obj) {
|
|||||||
modal.getElementsByTagName('tbody')[0].innerHTML = songDetails;
|
modal.getElementsByTagName('tbody')[0].innerHTML = songDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function execSyscmd(cmd) {
|
||||||
|
sendAPI({"cmd": "MPD_API_SYSCMD", "data": {"cmd": cmd}});
|
||||||
|
}
|
||||||
|
|
||||||
function playlistDetails(uri) {
|
function playlistDetails(uri) {
|
||||||
document.getElementById('BrowsePlaylistsAllList').classList.add('opacity05');
|
document.getElementById('BrowsePlaylistsAllList').classList.add('opacity05');
|
||||||
appGoto('Browse', 'Playlists', 'Detail', '0/-/' + uri);
|
appGoto('Browse', 'Playlists', 'Detail', '0/-/' + uri);
|
||||||
|
@ -109,6 +109,15 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
|||||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"MPD stickers are disabled\"}");
|
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"MPD stickers are disabled\"}");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case MPD_API_SYSCMD:
|
||||||
|
je = json_scanf(msg.p, msg.len, "{data: {cmd: %Q}}", &p_charbuf1);
|
||||||
|
if (je == 1) {
|
||||||
|
int_buf1 = list_get_value(&syscmds, p_charbuf1);
|
||||||
|
if (int_buf1 == 1)
|
||||||
|
n = mympd_syscmd(mpd.buf, p_charbuf1);
|
||||||
|
free(p_charbuf1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case MPD_API_PLAYER_STATE:
|
case MPD_API_PLAYER_STATE:
|
||||||
n = mympd_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.last_song_id, &mpd.queue_version, &mpd.queue_length);
|
n = mympd_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.last_song_id, &mpd.queue_version, &mpd.queue_length);
|
||||||
break;
|
break;
|
||||||
@ -1214,6 +1223,28 @@ bool mympd_state_set(char *name, char *value) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mympd_syscmd(char *buffer, char *cmd) {
|
||||||
|
int len;
|
||||||
|
char filename[400];
|
||||||
|
char *line;
|
||||||
|
size_t n = 0;
|
||||||
|
ssize_t read;
|
||||||
|
|
||||||
|
snprintf(filename, 400, "%s/syscmds/%s", config.etcdir, cmd);
|
||||||
|
FILE *fp = fopen(filename, "r");
|
||||||
|
if (fp == NULL) {
|
||||||
|
len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't execute cmd %s\"}", cmd);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
read = getline(&line, &n, fp);
|
||||||
|
fclose(fp);
|
||||||
|
strtok(line, "\n");
|
||||||
|
system(line);
|
||||||
|
len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Executed cmd %s\"}", line);
|
||||||
|
CHECK_RETURN_LEN();
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
int mympd_put_settings(char *buffer) {
|
int mympd_put_settings(char *buffer) {
|
||||||
struct mpd_status *status;
|
struct mpd_status *status;
|
||||||
char *replaygain = strdup("");
|
char *replaygain = strdup("");
|
||||||
@ -1276,6 +1307,17 @@ int mympd_put_settings(char *buffer) {
|
|||||||
current = current->next;
|
current = current->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
len += json_printf(&out, "], syscmds: [");
|
||||||
|
|
||||||
|
nr = 0;
|
||||||
|
current = syscmds.list;
|
||||||
|
while (current != NULL) {
|
||||||
|
if (nr ++)
|
||||||
|
len += json_printf(&out, ",");
|
||||||
|
len += json_printf(&out, "%Q", current->data);
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
|
||||||
len += json_printf(&out, "]}}");
|
len += json_printf(&out, "]}}");
|
||||||
|
|
||||||
CHECK_RETURN_LEN();
|
CHECK_RETURN_LEN();
|
||||||
@ -2148,6 +2190,7 @@ int mympd_smartpls_update_all() {
|
|||||||
printf("Can't parse file %s\n", filename);
|
printf("Can't parse file %s\n", filename);
|
||||||
}
|
}
|
||||||
free(smartpltype);
|
free(smartpltype);
|
||||||
|
free(content);
|
||||||
}
|
}
|
||||||
closedir (dir);
|
closedir (dir);
|
||||||
} else {
|
} else {
|
||||||
|
@ -109,6 +109,7 @@
|
|||||||
X(MPD_API_MESSAGE_SEND) \
|
X(MPD_API_MESSAGE_SEND) \
|
||||||
X(MPD_API_WELCOME) \
|
X(MPD_API_WELCOME) \
|
||||||
X(MPD_API_LIKE) \
|
X(MPD_API_LIKE) \
|
||||||
|
X(MPD_API_SYSCMD) \
|
||||||
X(MPD_API_UNKNOWN)
|
X(MPD_API_UNKNOWN)
|
||||||
|
|
||||||
enum mpd_cmd_ids {
|
enum mpd_cmd_ids {
|
||||||
@ -150,6 +151,8 @@ struct t_mpd {
|
|||||||
struct list mpd_tags;
|
struct list mpd_tags;
|
||||||
struct list mympd_tags;
|
struct list mympd_tags;
|
||||||
|
|
||||||
|
struct list syscmds;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
long mpdport;
|
long mpdport;
|
||||||
const char* mpdhost;
|
const char* mpdhost;
|
||||||
@ -167,6 +170,7 @@ typedef struct {
|
|||||||
const char* taglist;
|
const char* taglist;
|
||||||
bool smartpls;
|
bool smartpls;
|
||||||
const char* varlibdir;
|
const char* varlibdir;
|
||||||
|
const char* etcdir;
|
||||||
long max_elements_per_page;
|
long max_elements_per_page;
|
||||||
} t_config;
|
} t_config;
|
||||||
|
|
||||||
@ -207,6 +211,7 @@ void mympd_get_sticker(const char *uri, t_sticker *sticker);
|
|||||||
void mympd_jukebox();
|
void mympd_jukebox();
|
||||||
bool mympd_state_get(char *name, char *value);
|
bool mympd_state_get(char *name, char *value);
|
||||||
bool mympd_state_set(char *name, char *value);
|
bool mympd_state_set(char *name, char *value);
|
||||||
|
int mympd_syscmd(char *buffer, char *cmd);
|
||||||
int mympd_smartpls_save(char *smartpltype, char *playlist, char *tag, char *searchstr, int maxentries, int timerange);
|
int mympd_smartpls_save(char *smartpltype, char *playlist, char *tag, char *searchstr, int maxentries, int timerange);
|
||||||
int mympd_smartpls_put(char *buffer, char *playlist);
|
int mympd_smartpls_put(char *buffer, char *playlist);
|
||||||
int mympd_smartpls_update_all();
|
int mympd_smartpls_update_all();
|
||||||
|
29
src/mympd.c
29
src/mympd.c
@ -29,6 +29,7 @@
|
|||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
#include "../dist/src/mongoose/mongoose.h"
|
#include "../dist/src/mongoose/mongoose.h"
|
||||||
#include "../dist/src/frozen/frozen.h"
|
#include "../dist/src/frozen/frozen.h"
|
||||||
@ -130,12 +131,8 @@ static int inihandler(void* user, const char* section, const char* name, const c
|
|||||||
|
|
||||||
if (MATCH("mpdhost"))
|
if (MATCH("mpdhost"))
|
||||||
p_config->mpdhost = strdup(value);
|
p_config->mpdhost = strdup(value);
|
||||||
else if (MATCH("mpdhost"))
|
|
||||||
p_config->mpdhost = strdup(value);
|
|
||||||
else if (MATCH("mpdport"))
|
else if (MATCH("mpdport"))
|
||||||
p_config->mpdport = strtol(value, &crap, 10);
|
p_config->mpdport = strtol(value, &crap, 10);
|
||||||
else if (MATCH("mpdhost"))
|
|
||||||
p_config->mpdhost = strdup(value);
|
|
||||||
else if (MATCH("mpdpass"))
|
else if (MATCH("mpdpass"))
|
||||||
p_config->mpdpass = strdup(value);
|
p_config->mpdpass = strdup(value);
|
||||||
else if (MATCH("webport"))
|
else if (MATCH("webport"))
|
||||||
@ -187,6 +184,23 @@ static int inihandler(void* user, const char* section, const char* name, const c
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void read_syscmds() {
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *ent;
|
||||||
|
char dirname[400];
|
||||||
|
|
||||||
|
snprintf(dirname, 400, "%s/syscmds", config.etcdir);
|
||||||
|
printf("Reading syscmds: %s\n", dirname);
|
||||||
|
if ((dir = opendir (dirname)) != NULL) {
|
||||||
|
while ((ent = readdir(dir)) != NULL) {
|
||||||
|
if (strncmp(ent->d_name, ".", 1) == 0)
|
||||||
|
continue;
|
||||||
|
list_push(&syscmds, ent->d_name, 1);
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void read_statefiles() {
|
void read_statefiles() {
|
||||||
char *crap;
|
char *crap;
|
||||||
char value[400];
|
char value[400];
|
||||||
@ -266,7 +280,7 @@ int main(int argc, char **argv) {
|
|||||||
config.sslport = "443";
|
config.sslport = "443";
|
||||||
config.sslcert = "/etc/mympd/ssl/server.pem";
|
config.sslcert = "/etc/mympd/ssl/server.pem";
|
||||||
config.sslkey = "/etc/mympd/ssl/server.key";
|
config.sslkey = "/etc/mympd/ssl/server.key";
|
||||||
config.user = "nobody";
|
config.user = "mympd";
|
||||||
config.streamport = 8000;
|
config.streamport = 8000;
|
||||||
config.coverimage = "folder.jpg";
|
config.coverimage = "folder.jpg";
|
||||||
config.varlibdir = "/var/lib/mympd";
|
config.varlibdir = "/var/lib/mympd";
|
||||||
@ -275,6 +289,8 @@ int main(int argc, char **argv) {
|
|||||||
config.taglist = "Artist,Album,AlbumArtist,Title,Track,Genre,Date,Composer,Performer";
|
config.taglist = "Artist,Album,AlbumArtist,Title,Track,Genre,Date,Composer,Performer";
|
||||||
config.smartpls = true;
|
config.smartpls = true;
|
||||||
config.max_elements_per_page = 100;
|
config.max_elements_per_page = 100;
|
||||||
|
char *etcdir = strdup(argv[1]);
|
||||||
|
config.etcdir = dirname(etcdir);
|
||||||
|
|
||||||
mpd.timeout = 3000;
|
mpd.timeout = 3000;
|
||||||
mpd.last_update_sticker_song_id = -1;
|
mpd.last_update_sticker_song_id = -1;
|
||||||
@ -380,6 +396,9 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
read_statefiles();
|
read_statefiles();
|
||||||
|
|
||||||
|
list_init(&syscmds);
|
||||||
|
read_syscmds();
|
||||||
|
|
||||||
list_init(&mpd_tags);
|
list_init(&mpd_tags);
|
||||||
list_init(&mympd_tags);
|
list_init(&mympd_tags);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user