From b2d1988545185df6e816ab85e5fbc6264acf6995 Mon Sep 17 00:00:00 2001 From: KarnerF Date: Tue, 11 Aug 2026 17:31:22 +0200 Subject: [PATCH] Add auto-start and preferred ELF settings --- main.c | 156 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 107 insertions(+), 49 deletions(-) diff --git a/main.c b/main.c index 0618fb5..c7eee6c 100644 --- a/main.c +++ b/main.c @@ -68,6 +68,29 @@ static void _notify_send(const char *title, const char *sub) { static void notify(const char *msg) { _notify_send(msg, NULL); } +/* SMPlusGui own preferences (not SM config) */ +#define PREFS_PATH "/data/SMPlusGui/prefs.ini" +typedef struct { int auto_start; char preferred_elf[512]; } SMPrefs; + +static void read_prefs(SMPrefs *p) { + memset(p, 0, sizeof(*p)); + FILE *f = fopen(PREFS_PATH, "r"); if(!f) return; + char line[600]; + while(fgets(line, sizeof(line), f)) { + line[strcspn(line,"\r\n")]=0; + if(strncmp(line,"auto_start=",11)==0) p->auto_start=atoi(line+11); + else if(strncmp(line,"preferred_elf=",14)==0) strncpy(p->preferred_elf,line+14,511); + } + fclose(f); +} +static void write_prefs(const SMPrefs *p) { + mkdir("/data/SMPlusGui",0755); + FILE *f = fopen(PREFS_PATH,"w"); if(!f) return; + fprintf(f,"auto_start=%d\n",p->auto_start); + fprintf(f,"preferred_elf=%s\n",p->preferred_elf); + fclose(f); +} + static void get_local_ip(char *buf, size_t buflen) { struct ifaddrs *ifap, *ifa; buf[0] = 0; @@ -732,6 +755,32 @@ static int is_valid_sm_cfg(const char *buf, size_t len){ return found>=2; } +/* Returns 0 on success, -1 elf not found, -2 elfldr not reachable, -3 send error */ +static int send_elf_to_elfldr(const char *path) { + FILE *elf = fopen(path,"rb"); if(!elf) return -1; + int ports[]={9021,9020,0}; int sock=-1; + for(int pi=0;ports[pi];pi++){ + int s=socket(AF_INET,SOCK_STREAM,0); if(s<0) continue; + struct timeval tv={5,0}; + setsockopt(s,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv)); + setsockopt(s,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)); + struct sockaddr_in sa; memset(&sa,0,sizeof(sa)); + sa.sin_family=AF_INET; sa.sin_port=htons((uint16_t)ports[pi]); + sa.sin_addr.s_addr=inet_addr("127.0.0.1"); + if(connect(s,(struct sockaddr*)&sa,sizeof(sa))==0){sock=s;break;} + close(s); + } + if(sock<0){fclose(elf);return -2;} + char buf[8192]; int err=0; size_t n; + while((n=fread(buf,1,sizeof(buf),elf))>0){ + size_t off=0; + while(off" + " Auto-Start", + prefs.auto_start?" checked":""); + H("%s", + prefs.preferred_elf[0]?"":"display:none;", + prefs.preferred_elf, prefs.preferred_elf[0] ? strrchr(prefs.preferred_elf,'/')? strrchr(prefs.preferred_elf,'/')+1 : prefs.preferred_elf : ""); + H("", + prefs.preferred_elf[0]?"":"display:none!important;"); } H(""); /* close left flex */ H("
" "AUTO" @@ -1466,7 +1526,13 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) { /* restore panel after USB-triggered reload */ "var _rp=sessionStorage.getItem('panel');" "if(_rp){var _rb=document.querySelector('.nav-item[data-p=\"'+_rp+'\"]');if(_rb)showP(_rb);}" - "function smVer(ver,maj,min,suf,num){""if(!ver)return false;""var m=ver.match(/^(\\d+)\\.(\\d+)(alpha|beta|test)?(\\d+)?/);""if(!m)return false;""var a=parseInt(m[1]),b=parseInt(m[2]),c=m[3]||'',d=parseInt(m[4]||'0');""if(a>maj)return true;if(amin)return true;if(bmp)return true;if(vp=num;}""function refreshStatus(){""fetch('/api/status').then(r=>r.json()).then(d=>{""var c=document.getElementById('sm-status-chip');""if(!c)return;""var col=d.running?'#10b981':'#f87171';""c.innerHTML=' ShadowMount '+d.status+'';""var v=document.getElementById('sm-ver-chip');""if(v){v.style.display=(d.running&&d.version!=='nicht aktiv')?'flex':'none';""if(d.version!=='nicht aktiv')v.innerHTML='v '+d.version+'';}""var ver=d.version||'';""var ar=!d.running||smVer(ver,1,7,'alpha',3);""var pim=!d.running||smVer(ver,1,7,'alpha',4);""var as=document.getElementById('ar-wrap');""if(as){as.style.opacity=ar?\'1\':\'0.35\';as.style.pointerEvents=ar?\'auto\':\'none\';}""var ab=document.querySelectorAll('.ar-b');ab.forEach(function(e){e.style.display=ar?'none':'inline';});""var pb=document.getElementById('pim-badge');""if(pb)pb.style.display=pim?'none':'inline';""var pr=document.getElementById('pim-row');""if(pr)pr.style.opacity=pim?'1':'0.35';""if(pr)pr.style.pointerEvents=pim?'auto':'none';""var lb=document.getElementById('lang-badge');""if(lb)lb.style.display=pim?'none':'inline';""var lr=document.getElementById('lang-row');""if(lr)lr.style.opacity=pim?'1':'0.35';""if(lr)lr.style.pointerEvents=pim?'auto':'none';""var api=!d.running||smVer(ver,1,7,'alpha',3);""var aw=document.getElementById('api-wrap');""if(aw){aw.style.opacity=api?'1':'0.35';aw.style.pointerEvents=api?'auto':'none';}""var apib=document.getElementById('api-badge');""if(apib)apib.style.display=api?'none':'inline';""var fan=!d.running||smVer(ver,1,7,'alpha',5);""var fr=document.getElementById('fan-row');""if(fr){fr.style.opacity=fan?'1':'0.35';fr.style.pointerEvents=fan?'auto':'none';}""var fb=fr?fr.querySelector('.vbadge'):null;""if(fb)fb.style.display=fan?'none':'inline';""var cb=document.getElementById('sm-ctrl-btn');""if(cb){cb.setAttribute('data-action',d.running?'stop':'start');cb.className='sm-ctrl '+(d.running?'stop':'start');cb.textContent=d.running?'Stop':'Start';}""}).catch(()=>{});}""refreshStatus();""setInterval(refreshStatus,10000);""function smCtrl(){var btn=document.getElementById('sm-ctrl-btn');if(!btn)return;var action=btn.getAttribute('data-action');if(action==='stop'){btn.disabled=true;fetch('/api/sm/stop').then(function(){setTimeout(refreshStatus,1500);setTimeout(function(){btn.disabled=false;},2000);}).catch(function(){btn.disabled=false;});}else{btn.disabled=true;fetch('/api/sm/scan').then(function(r){return r.json();}).then(function(d){if(d.count===0){alert(_t.elf_nf);btn.disabled=false;}else if(d.count===1){launchElf(btn,d.paths[0]);}else{btn.disabled=false;showElfPicker(btn,d.paths);}}).catch(function(){btn.disabled=false;});}}""function launchElf(btn,path){if(btn)btn.disabled=true;fetch('/api/sm/start?path='+encodeURIComponent(path)).then(function(r){return r.json();}).then(function(){setTimeout(refreshStatus,3000);setTimeout(function(){if(btn)btn.disabled=false;},3000);}).catch(function(){if(btn)btn.disabled=false;});}""function showElfPicker(btn,paths){var old=document.getElementById('sm-elf-picker');if(old)old.remove();var picker=document.createElement('div');picker.id='sm-elf-picker';picker.style.cssText='position:fixed;top:0;left:0;width:100%%;height:100%%;background:rgba(0,0,0,.75);display:flex;align-items:center;justify-content:center;z-index:9999;';var box=document.createElement('div');box.style.cssText='background:#0d111a;border:1px solid #1e2a42;border-radius:14px;padding:36px;min-width:500px;max-width:800px;';var title=document.createElement('p');title.style.cssText='margin:0 0 20px;font-size:.7rem;text-transform:uppercase;letter-spacing:.12em;color:#64748b;font-family:monospace;';title.textContent=_t.sel_sm;box.appendChild(title);paths.forEach(function(p){var b=document.createElement('button');b.style.cssText='display:block;width:100%%;margin-bottom:10px;padding:16px 18px;border-radius:8px;border:1px solid #1e2a42;background:#131927;color:#e2e8f0;cursor:pointer;text-align:left;font-family:monospace;font-size:.75rem;word-break:break-all;';b.textContent=p;b.onclick=function(){picker.remove();launchElf(btn,p);};box.appendChild(b);});var cancel=document.createElement('button');cancel.style.cssText='display:block;width:100%%;padding:14px;border-radius:8px;border:1px solid #1e2a42;background:transparent;color:#64748b;cursor:pointer;font-family:monospace;font-size:.75rem;margin-top:6px;';cancel.textContent=_t.cancel;cancel.onclick=function(){picker.remove();};box.appendChild(cancel);picker.appendChild(box);document.body.appendChild(picker);}""document.querySelector('form').addEventListener('keydown',function(e){""if(e.key==='Enter'&&e.target.tagName!=='BUTTON')e.preventDefault();""});" + "function saveAutoStart(v){fetch('/api/prefs/save',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'auto_start='+(v?1:0)});}" + "function clearPrefElf(){fetch('/api/prefs/save',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'preferred_elf='}).then(function(){var l=document.getElementById('pref-elf-lbl'),b=document.getElementById('pref-elf-clr');if(l){l.style.display='none';l.textContent='';}if(b)b.style.display='none';});}" + "function smVer(ver,maj,min,suf,num){""if(!ver)return false;""var m=ver.match(/^(\\d+)\\.(\\d+)(alpha|beta|test)?(\\d+)?/);""if(!m)return false;""var a=parseInt(m[1]),b=parseInt(m[2]),c=m[3]||'',d=parseInt(m[4]||'0');""if(a>maj)return true;if(amin)return true;if(bmp)return true;if(vp=num;}""function refreshStatus(){""fetch('/api/status').then(r=>r.json()).then(d=>{""var c=document.getElementById('sm-status-chip');""if(!c)return;""var col=d.running?'#10b981':'#f87171';""c.innerHTML=' ShadowMount '+d.status+'';""var v=document.getElementById('sm-ver-chip');""if(v){v.style.display=(d.running&&d.version!=='nicht aktiv')?'flex':'none';""if(d.version!=='nicht aktiv')v.innerHTML='v '+d.version+'';}""var ver=d.version||'';""var ar=!d.running||smVer(ver,1,7,'alpha',3);""var pim=!d.running||smVer(ver,1,7,'alpha',4);""var as=document.getElementById('ar-wrap');""if(as){as.style.opacity=ar?\'1\':\'0.35\';as.style.pointerEvents=ar?\'auto\':\'none\';}""var ab=document.querySelectorAll('.ar-b');ab.forEach(function(e){e.style.display=ar?'none':'inline';});""var pb=document.getElementById('pim-badge');""if(pb)pb.style.display=pim?'none':'inline';""var pr=document.getElementById('pim-row');""if(pr)pr.style.opacity=pim?'1':'0.35';""if(pr)pr.style.pointerEvents=pim?'auto':'none';""var lb=document.getElementById('lang-badge');""if(lb)lb.style.display=pim?'none':'inline';""var lr=document.getElementById('lang-row');""if(lr)lr.style.opacity=pim?'1':'0.35';""if(lr)lr.style.pointerEvents=pim?'auto':'none';""var api=!d.running||smVer(ver,1,7,'alpha',3);""var aw=document.getElementById('api-wrap');""if(aw){aw.style.opacity=api?'1':'0.35';aw.style.pointerEvents=api?'auto':'none';}""var apib=document.getElementById('api-badge');""if(apib)apib.style.display=api?'none':'inline';""var fan=!d.running||smVer(ver,1,7,'alpha',5);""var fr=document.getElementById('fan-row');""if(fr){fr.style.opacity=fan?'1':'0.35';fr.style.pointerEvents=fan?'auto':'none';}""var fb=fr?fr.querySelector('.vbadge'):null;""if(fb)fb.style.display=fan?'none':'inline';""var cb=document.getElementById('sm-ctrl-btn');""if(cb){cb.setAttribute('data-action',d.running?'stop':'start');cb.className='sm-ctrl '+(d.running?'stop':'start');cb.textContent=d.running?'Stop':'Start';}""}).catch(()=>{});}""refreshStatus();""setInterval(refreshStatus,10000);""function smCtrl(){var btn=document.getElementById('sm-ctrl-btn');if(!btn)return;var action=btn.getAttribute('data-action');if(action==='stop'){btn.disabled=true;fetch('/api/sm/stop').then(function(){setTimeout(refreshStatus,1500);setTimeout(function(){btn.disabled=false;},2000);}).catch(function(){btn.disabled=false;});}else{btn.disabled=true;fetch('/api/sm/scan').then(function(r){return r.json();}).then(function(d){if(d.count===0){alert(_t.elf_nf);btn.disabled=false;}else if(d.count===1){launchElf(btn,d.paths[0]);}else{btn.disabled=false;showElfPicker(btn,d.paths);}}).catch(function(){btn.disabled=false;});}}""function launchElf(btn,path){if(btn)btn.disabled=true;fetch('/api/sm/start?path='+encodeURIComponent(path)).then(function(r){return r.json();}).then(function(d){" + "if(btn)btn.disabled=false;" + "if(d.ok){var nm=path.split('/').pop();var l=document.getElementById('pref-elf-lbl');var b=document.getElementById('pref-elf-clr');" + "if(l){l.textContent=nm;l.title=path;l.style.display='inline';}if(b)b.style.display='inline';}" + "setTimeout(refreshStatus,2000);})){setTimeout(refreshStatus,3000);setTimeout(function(){if(btn)btn.disabled=false;},3000);}).catch(function(){if(btn)btn.disabled=false;});}""function showElfPicker(btn,paths){var old=document.getElementById('sm-elf-picker');if(old)old.remove();var picker=document.createElement('div');picker.id='sm-elf-picker';picker.style.cssText='position:fixed;top:0;left:0;width:100%%;height:100%%;background:rgba(0,0,0,.75);display:flex;align-items:center;justify-content:center;z-index:9999;';var box=document.createElement('div');box.style.cssText='background:#0d111a;border:1px solid #1e2a42;border-radius:14px;padding:36px;min-width:500px;max-width:800px;';var title=document.createElement('p');title.style.cssText='margin:0 0 20px;font-size:.7rem;text-transform:uppercase;letter-spacing:.12em;color:#64748b;font-family:monospace;';title.textContent=_t.sel_sm;box.appendChild(title);paths.forEach(function(p){var b=document.createElement('button');b.style.cssText='display:block;width:100%%;margin-bottom:10px;padding:16px 18px;border-radius:8px;border:1px solid #1e2a42;background:#131927;color:#e2e8f0;cursor:pointer;text-align:left;font-family:monospace;font-size:.75rem;word-break:break-all;';b.textContent=p;b.onclick=function(){picker.remove();launchElf(btn,p);};box.appendChild(b);});var cancel=document.createElement('button');cancel.style.cssText='display:block;width:100%%;padding:14px;border-radius:8px;border:1px solid #1e2a42;background:transparent;color:#64748b;cursor:pointer;font-family:monospace;font-size:.75rem;margin-top:6px;';cancel.textContent=_t.cancel;cancel.onclick=function(){picker.remove();};box.appendChild(cancel);picker.appendChild(box);document.body.appendChild(picker);}""document.querySelector('form').addEventListener('keydown',function(e){""if(e.key==='Enter'&&e.target.tagName!=='BUTTON')e.preventDefault();""});" "function addPath(){" "var c=document.getElementById('paths-list');" "var d=document.createElement('div');d.className='path-row';" @@ -1961,58 +2027,18 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) { } else if(mg_match(hm->uri,mg_str("/api/sm/start"),NULL)){ char lang[8]; read_lang(lang,sizeof(lang)); int en=(strcmp(lang,"en")==0||strcmp(lang,"fr")==0||strcmp(lang,"es")==0); - /* Launch SM via elfldr - path from query param or auto-scanned */ char elf_path[SM_EPATH]={0}; mg_http_get_var(&hm->query,"path",elf_path,sizeof(elf_path)); if(!elf_path[0]){ char elfs[SM_MAX_ELFS][SM_EPATH]; if(sm_find_elfs(elfs)>0) strncpy(elf_path,elfs[0],SM_EPATH-1); } - FILE *elf=elf_path[0]?fopen(elf_path,"rb"):NULL; - if(!elf){ - notify(en ? "SM ELF not found" : "SM ELF nicht gefunden"); - mg_http_reply(c,200,"Content-Type: application/json\r\nCache-Control: no-cache\r\n", - "{\"ok\":false,\"err\":\"elf_not_found\"}"); - return; - } - /* Try elfldr on port 9021 then 9020 */ - int ports[]={9021,9020,0}; - int sock=-1; - for(int pi=0;ports[pi];pi++){ - int s=socket(AF_INET,SOCK_STREAM,0); - if(s<0) continue; - struct timeval tv={5,0}; - setsockopt(s,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv)); - setsockopt(s,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)); - struct sockaddr_in sa; - memset(&sa,0,sizeof(sa)); - sa.sin_family=AF_INET; - sa.sin_port=htons((uint16_t)ports[pi]); - sa.sin_addr.s_addr=inet_addr("127.0.0.1"); - if(connect(s,(struct sockaddr*)&sa,sizeof(sa))==0){sock=s;break;} - close(s); - } - if(sock<0){ - fclose(elf); - notify(en ? "elfldr not reachable (Port 9021/9020)" : "elfldr nicht erreichbar (Port 9021/9020)"); - mg_http_reply(c,200,"Content-Type: application/json\r\nCache-Control: no-cache\r\n", - "{\"ok\":false,\"err\":\"elfldr_not_reachable\"}"); - return; - } - char buf[8192]; int err=0; size_t n; - while((n=fread(buf,1,sizeof(buf),elf))>0){ - size_t off=0; - while(offuri,mg_str("/api/config/backup"),NULL)){ const char *bdir=BAK_DIR; @@ -2471,6 +2497,22 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) { mg_http_reply(c,200,"Content-Type: text/plain; charset=utf-8\r\nCache-Control: no-cache\r\n","%s",lb); free(lb); } + else if(mg_match(hm->uri,mg_str("/api/prefs/get"),NULL)){ + SMPrefs p; read_prefs(&p); + mg_http_reply(c,200,"Content-Type: application/json\r\n", + "{\"auto_start\":%d,\"preferred_elf\":\"%s\"}",p.auto_start,p.preferred_elf); + } + else if(mg_match(hm->uri,mg_str("/api/prefs/save"),NULL)){ + char v[8]={0},ef[512]={0}; + mg_http_get_var(&hm->body,"auto_start",v,sizeof(v)); + mg_http_get_var(&hm->body,"preferred_elf",ef,sizeof(ef)); + SMPrefs p; read_prefs(&p); + if(v[0]) p.auto_start=atoi(v); + if(ef[0]||mg_http_get_var(&hm->body,"preferred_elf",ef,1)>=0) + strncpy(p.preferred_elf,ef,511); + write_prefs(&p); + mg_http_reply(c,200,"Content-Type: application/json\r\n","{\"ok\":true}"); + } else mg_http_reply(c,404,"","404"); } @@ -2486,8 +2528,24 @@ int payload_main(void) { struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr,"http://0.0.0.0:" HTTP_PORT,fn,NULL); - int polls=0; - while(1){ mg_mgr_poll(&mgr,1000); usleep(100000); if(++polls==3) smplus_install_if_needed(); } + int polls=0, as_done=0; + while(1){ + mg_mgr_poll(&mgr,1000); usleep(100000); + if(++polls==3) smplus_install_if_needed(); + if(polls==6 && !as_done){ /* ~6s after start */ + as_done=1; + SMPrefs prefs; read_prefs(&prefs); + if(prefs.auto_start){ + char sv[64]; get_sm_version(sv,sizeof(sv)); + if(strcmp(sv,"nicht aktiv")==0){ + char elf[SM_EPATH]={0}; + if(prefs.preferred_elf[0]) strncpy(elf,prefs.preferred_elf,SM_EPATH-1); + else{ char elfs[SM_MAX_ELFS][SM_EPATH]; if(sm_find_elfs(elfs)>0) strncpy(elf,elfs[0],SM_EPATH-1); } + if(elf[0]) send_elf_to_elfldr(elf); + } + } + } + } mg_mgr_free(&mgr); return 0; }