/* * Copyright (c) 2017 Shaun Feakes - All rights reserved * * You may use redistribute and/or modify this code under the terms of * the GNU General Public License version 2 as published by the * Free Software Foundation. For the terms of this license, * see . * * You are free to use this software under the terms of the GNU General * Public License, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * https://github.com/sfeakes/aqualinkd */ #include #include #include #include #include #include #include #include "mongoose.h" #include "aqualink.h" #include "aq_scheduler.h" #include "config.h" //#include "utils.h" /* Example /etc/cron.d/aqualinkd 01 10 1 * * curl localhost:80/api/Filter_Pump/set -d value=2 -X PUT */ bool remount_root_ro(bool readonly) { #ifdef AQ_CONTAINER // In container this is pointless return false; #endif if (readonly) { LOG(SCHD_LOG,LOG_INFO, "reMounting root RO\n"); mount (NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL); return true; } else { struct statvfs fsinfo; statvfs("/", &fsinfo); if ((fsinfo.f_flag & ST_RDONLY) == 0) // We are readwrite, ignore return false; LOG(SCHD_LOG,LOG_INFO, "reMounting root RW\n"); mount (NULL, "/", NULL, MS_REMOUNT, NULL); return true; } } bool passJson_scObj(char* line, int length, aqs_cron *values) { int keystart=0; //int keyend=0; int valuestart=0; int captured=0; bool readingvalue=false; bool invalue=false; //char value; values->enabled = true; //LOG(SCHD_LOG,LOG_DEBUG, "Obj body:'%.*s'\n", length, line); for (int i=0; i < length; i++) { if (line[i] == '}') { return (captured >= 7)?true:false; } else if (line[i] == '"' && keystart==0 && invalue==false && readingvalue==false) { keystart=i+1; } else if (line[i] == '"' && keystart > 0 && invalue==false && readingvalue==false) { //keyend=i; } else if (line[i] == ':' && keystart > 0 ) { invalue=true; } else if (line[i] == '"' && invalue == true && readingvalue == false && keystart > 0 ) { readingvalue=true; valuestart=i+1; } else if (line[i] == '"' && readingvalue == true) { // i is end of key if ( strncmp(&line[keystart], "enabled", 7) == 0) { values->enabled = (line[valuestart]=='0'?false:true); captured++; } else if ( strncmp(&line[keystart], "min", 3) == 0) { strncpy(values->minute, &line[valuestart], (i-valuestart) ); values->minute[i-valuestart] = '\0'; captured++; } else if( strncmp(&line[keystart], "hour", 4) == 0) { strncpy(values->hour, &line[valuestart], (i-valuestart) ); values->hour[i-valuestart] = '\0'; captured++; } else if( strncmp(&line[keystart], "daym", 4) == 0) { strncpy(values->daym, &line[valuestart], (i-valuestart) ); values->daym[i-valuestart] = '\0'; captured++; } else if( strncmp(&line[keystart], "month", 5) == 0) { strncpy(values->month, &line[valuestart], (i-valuestart) ); values->month[i-valuestart] = '\0'; captured++; } else if( strncmp(&line[keystart], "dayw", 4) == 0) { strncpy(values->dayw, &line[valuestart], (i-valuestart) ); values->dayw[i-valuestart] = '\0'; captured++; } else if( strncmp(&line[keystart], "url", 3) == 0) { strncpy(values->url, &line[valuestart], (i-valuestart) ); values->url[i-valuestart] = '\0'; captured++; } else if( strncmp(&line[keystart], "value", 5) == 0) { strncpy(values->value, &line[valuestart], (i-valuestart) ); values->value[i-valuestart] = '\0'; captured++; } keystart=0; //keyend=0; valuestart=0; invalue=false; readingvalue=false; } } return (captured >= 7)?true:false; } int save_schedules_js(char* inBuf, int inSize, char* outBuf, int outSize) { int length=0; FILE *fp; int i; bool inarray = false; aqs_cron cline; bool fileexists = false; if ( !_aqconfig_.enable_scheduler) { LOG(SCHD_LOG,LOG_WARNING, "Schedules are disabled\n"); length += sprintf(outBuf, "{\"message\":\"Error Schedules disabled\"}"); return length; } LOG(SCHD_LOG,LOG_NOTICE, "Saving Schedule:\n"); bool fs = remount_root_ro(false); if (access(CRON_FILE, F_OK) == 0) fileexists = true; fp = fopen(CRON_FILE, "w"); if (fp == NULL) { LOG(SCHD_LOG,LOG_ERR, "Open file failed '%s'\n", CRON_FILE); remount_root_ro(true); length += sprintf(outBuf, "{\"message\":\"Error Saving Schedules\"}"); return length; } fprintf(fp, "#***** AUTO GENERATED DO NOT EDIT *****\n"); fprintf(fp, "PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin\n"); LOG(SCHD_LOG,LOG_DEBUG, "Schedules Message body:\n'%.*s'\n", inSize, inBuf); length += sprintf(outBuf, "{\"message\":\"Saved Schedules\"}"); for (i=0; i < inSize; i++) { if ( inBuf[i] == '[' ) { inarray=true; } else if ( inBuf[i] == ']' ) { inarray=false; } else if ( inarray && inBuf[i] == '{') { passJson_scObj( &inBuf[i], (inSize-i), &cline); LOG(SCHD_LOG,LOG_DEBUG, "Write to cron Min:%s Hour:%s DayM:%s Month:%s DayW:%s URL:%s Value:%s\n",cline.minute,cline.hour,cline.daym,cline.month,cline.dayw,cline.url,cline.value); LOG(SCHD_LOG,LOG_INFO, "%s%s %s %s %s %s curl -s -S --show-error -o /dev/null localhost:%s%s -d value=%s -X PUT\n",(cline.enabled?"":"#"),cline.minute, cline.hour, cline.daym, cline.month, cline.dayw, _aqconfig_.socket_port, cline.url, cline.value); fprintf(fp, "%s%s %s %s %s %s root curl -s -S --show-error -o /dev/null localhost:%s%s -d value=%s -X PUT\n",(cline.enabled?"":"#"),cline.minute, cline.hour, cline.daym, cline.month, cline.dayw, _aqconfig_.socket_port, cline.url, cline.value); } else if ( inarray && inBuf[i] == '}') { //inobj=false; //objed=i; } } fprintf(fp, "#***** AUTO GENERATED DO NOT EDIT *****\n"); fclose(fp); // if we created file, change the permisions if (!fileexists) if ( chmod(CRON_FILE, S_IRUSR | S_IWUSR ) < 0 ) LOG(SCHD_LOG,LOG_ERR, "Could not change permitions on cron file %s, scheduling may not work\n",CRON_FILE); remount_root_ro(fs); return length; } int build_schedules_js(char* buffer, int size) { memset(&buffer[0], 0, size); FILE *fp; char *line = NULL; int length = 0; int rc; aqs_cron cline; size_t len = 0; ssize_t read_size; regex_t regexCompiled; if ( !_aqconfig_.enable_scheduler) { LOG(SCHD_LOG,LOG_WARNING, "Schedules are disabled\n"); length += sprintf(buffer, "{\"message\":\"Error Schedules disabled\"}"); return length; } // Below works for curl but not /usr/bin/curl in command. NSF come back and fix the regexp //char *regexString="([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s.*(/api/.*)\\s-d value=([^\\d]+)\\s(.*)"; // \d doesn't seem to be supported, so using [0-9]+ instead //char *regexString="([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s.*(\\/api\\/.*\\/set).* value=([0-9]+).*"; //char *regexString="([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s.*(\\/api\\/.*\\/set).* value=([0-9]+).*"; char *regexString="(#{0,1})([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s.*(\\/api\\/.*\\/set).* value=([0-9]+).*"; //char *regexString="([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s.*(/api/.*/set).*value=([0-9]+).*"; size_t maxGroups = 15; regmatch_t groupArray[maxGroups]; //static char buf[100]; length += sprintf(buffer+length,"{\"type\": \"schedules\","); if (0 != (rc = regcomp(®exCompiled, regexString, REG_EXTENDED))) { LOG(SCHD_LOG,LOG_ERR, "regcomp() failed, returning nonzero (%d)\n", rc); length += sprintf(buffer+length,"\"message\": \"Error reading schedules\"}"); return length; } fp = fopen(CRON_FILE, "r"); if (fp == NULL) { LOG(SCHD_LOG,LOG_WARNING, "Open file failed '%s'\n", CRON_FILE); length += sprintf(buffer+length,"\"message\": \"Error reading schedules\"}"); return length; } length += sprintf(buffer+length,"\"schedules\": [ "); while ((read_size = getline(&line, &len, fp)) != -1) { //printf("Read from cron:-\n %s", line); //lc++; //rc = regexec(®exCompiled, line, maxGroups, groupArray, 0); if (0 == (rc = regexec(®exCompiled, line, maxGroups, groupArray, REG_EXTENDED))) { // Group 1 is # (enable or not) // Group 2 is minute // Group 3 is hour // Group 4 is day of month // Group 5 is month // Group 6 is day of week // Group 7 is root // Group 8 is curl // Group 9 is URL // Group 10 is value if (groupArray[8].rm_so == (size_t)-1) { LOG(SCHD_LOG,LOG_ERR, "No matching information from cron file\n"); } else { cline.enabled = (line[groupArray[1].rm_so] == '#')?false:true; sprintf(cline.minute, "%.*s", (groupArray[2].rm_eo - groupArray[2].rm_so), (line + groupArray[2].rm_so)); sprintf(cline.hour, "%.*s", (groupArray[3].rm_eo - groupArray[3].rm_so), (line + groupArray[3].rm_so)); sprintf(cline.daym, "%.*s", (groupArray[4].rm_eo - groupArray[4].rm_so), (line + groupArray[4].rm_so)); sprintf(cline.month, "%.*s", (groupArray[5].rm_eo - groupArray[5].rm_so), (line + groupArray[5].rm_so)); sprintf(cline.dayw, "%.*s", (groupArray[6].rm_eo - groupArray[6].rm_so), (line + groupArray[6].rm_so)); sprintf(cline.url, "%.*s", (groupArray[9].rm_eo - groupArray[9].rm_so), (line + groupArray[9].rm_so)); sprintf(cline.value, "%.*s", (groupArray[10].rm_eo - groupArray[10].rm_so), (line + groupArray[10].rm_so)); LOG(SCHD_LOG,LOG_INFO, "Read from cron. Enabled:%d Min:%s Hour:%s DayM:%s Month:%s DayW:%s URL:%s Value:%s\n",cline.enabled,cline.minute,cline.hour,cline.daym,cline.month,cline.dayw,cline.url,cline.value); length += sprintf(buffer+length, "{\"enabled\":\"%d\", \"min\":\"%s\",\"hour\":\"%s\",\"daym\":\"%s\",\"month\":\"%s\",\"dayw\":\"%s\",\"url\":\"%s\",\"value\":\"%s\"},", cline.enabled, cline.minute, cline.hour, cline.daym, cline.month, cline.dayw, cline.url, cline.value); //LOG(SCHD_LOG,LOG_DEBUG, "Read from cron Day %d | Time %d:%d | Zone %d | Runtime %d\n",day,hour,minute,zone,runtime); } } else { LOG(SCHD_LOG,LOG_DEBUG, "regexp no match (%d) %s\n", rc, line); } } buffer[--length] = '\0'; length += sprintf(buffer+length,"]}\n"); fclose(fp); regfree(®exCompiled); return length; }