#include "cmd.h" // command line message buffer and pointer static uint8_t msg[MAX_MSG_SIZE] = ""; static uint8_t *msg_ptr = msg; int echo_on = 1; int errorlevel = 0; // Exec script stack, this allows executing scripts from within scripts. typedef struct script_s { char m_filename[MAX_FILE_PATH+1]; FsFile m_file; int m_line; } script_t; #define MAX_SCRIPTLEVEL 8 script_t scriptstack[MAX_SCRIPTLEVEL]; int scriptlevel = -1; #define exec_filename scriptstack[scriptlevel].m_filename #define exec_line scriptstack[scriptlevel].m_line char cmd_prefix[256] = "/"; extern uint8_t mbr_bin[]; uint8_t zero[512]; void cmdDisplay() { if(cmd_prefix[0] != 0) { Serial.printf("[%s] $ ", cmd_prefix); } else { strcpy(cmd_prefix, "/"); Serial.print("[/] $ "); } } /* * Like strtok, but with quotes */ char *strmbtok ( char *input, const char *delimit, const char *openblock, const char *closeblock) { static char *token = NULL; char *lead = NULL; char *block = NULL; int iBlock = 0; int iBlockIndex = 0; if ( input != NULL) { token = input; lead = input; } else { lead = token; if ( *token == '\0') { lead = NULL; } } while ( *token != '\0') { // We are in a quote, is this a matching close quote character? if ( iBlock) { if ( closeblock[iBlockIndex] == *token) { iBlock = 0; } token++; continue; } // Is this a valid opening quote? if ( ( block = strchr ( openblock, *token)) != NULL) { iBlock = 1; iBlockIndex = block - openblock; token++; continue; } if ( strchr ( delimit, *token) != NULL) { *token = '\0'; token++; break; } token++; } return lead; } /* * Remove quotes from quoted strings. */ void unquote(char *out, char *in) { char tmp_str[256]; if(!in) return; if(!out) return; char *pout = tmp_str; char inQuote = 0; while(*in != 0) { if(inQuote && (*in == inQuote)) { inQuote = 0; } else if(!inQuote && (*in == '"')) { inQuote = '"'; } else if(!inQuote && (*in == '\'')) { inQuote = '\''; } else { *pout = *in; pout++; } in++; } *pout = 0; // This way you can have out == in strcpy(out, tmp_str); } #define ENVIRONMENT_SIZE 32 #define ENVIRONMENT_KEY_SIZE 32 #define ENVIRONMENT_VALUE_SIZE (MAX_FILE_PATH+1) typedef struct cmdvar_s { char key[ENVIRONMENT_KEY_SIZE]; char value[ENVIRONMENT_VALUE_SIZE]; } cmdvar_t; cmdvar_t cmdenv[ENVIRONMENT_SIZE]; int getvar(const char *varname, int defaultvalue) { if(!strcasecmp(varname, "ERRORLEVEL")) return errorlevel; if(!strcasecmp(varname, "DEBUG")) return debuglog; for(int i = 0; i < ENVIRONMENT_SIZE; i++) { if(!strcmp(cmdenv[i].key, varname)) { return strtol(cmdenv[i].value, NULL, 0); } } return defaultvalue; } char* getvar(const char *varname, char *defaultvalue) { if(!strcasecmp(varname, "ERRORLEVEL")) return NULL; if(!strcasecmp(varname, "DEBUG")) return NULL; for(int i = 0; i < ENVIRONMENT_SIZE; i++) { if(!strcasecmp(cmdenv[i].key, varname)) { return cmdenv[i].value; } } return defaultvalue; } void setvarcmd(int argc, char **argv) { int i; if(argc != 3) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Syntax error in setvar command.\r\n"); return; } if(!strcasecmp(argv[1], "ERRORLEVEL")) return; if(!strcasecmp(argv[1], "DEBUG")) { debuglog = strtoul(argv[2], NULL, 0); return; } // Find existing variable matching name and replace it for(i = 0; i < ENVIRONMENT_SIZE; i++) { if(!strcasecmp(cmdenv[i].key, argv[1])) { strncpy(cmdenv[i].value, argv[2], ENVIRONMENT_VALUE_SIZE); cmdenv[i].value[ENVIRONMENT_VALUE_SIZE-1] = 0; return; } } // Create new variable if we have room. for(i = 0; i < ENVIRONMENT_SIZE; i++) { if(cmdenv[i].key[0] == 0) { strncpy(cmdenv[i].key, argv[1], ENVIRONMENT_KEY_SIZE); cmdenv[i].key[ENVIRONMENT_KEY_SIZE-1] = 0; strncpy(cmdenv[i].value, argv[2], ENVIRONMENT_VALUE_SIZE); cmdenv[i].value[ENVIRONMENT_VALUE_SIZE-1] = 0; return; } } errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Ran out of environment variable space.\r\n"); } void unsetvarcmd(int argc, char **argv) { int i; if(argc != 2) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Syntax error in unsetvar command.\r\n"); return; } if(!strcasecmp(argv[1], "ERRORLEVEL")) return; if(!strcasecmp(argv[1], "DEBUG")) { debuglog = 0; return; } // Find existing variable matching name and delete it for(i = 0; i < ENVIRONMENT_SIZE; i++) { if(!strcasecmp(cmdenv[i].key, argv[1])) { memset(cmdenv[i].key, 0, ENVIRONMENT_KEY_SIZE); memset(cmdenv[i].value, 0, ENVIRONMENT_VALUE_SIZE); return; } } // Quietly fail if the variable doesn't exist } void getvarcmd(int argc, char **argv) { int i; if(argc > 2) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Syntax error in getvar command.\r\n"); return; } if(!strcasecmp(argv[1], "ERRORLEVEL")) { Serial.printf("%d\r\n", errorlevel); return; } if(!strcasecmp(argv[1], "DEBUG")) { Serial.printf("%d\r\n", debuglog); return; } for(i = 0; i < ENVIRONMENT_SIZE; i++) { if(!strcasecmp(cmdenv[i].key, argv[1])) { Serial.printf("%s=%s\r\n", cmdenv[i].key, cmdenv[i].value); return; } } errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": variable '%s' not found.\r\n", argv[1]); return; } void cmdParse(char *cmd) { uint8_t argc, i = 0; char *argv[MAXARG]; // leading whitespace and blank lines are ignored while((*cmd == ' ') || (*cmd == '\t')) cmd++; if(!cmd[0]) return; // ignore labels for regular command parsing if(cmd[0] == ':') return; // parse the statement and tokenize it (with quote handling) argv[i] = strmbtok(cmd, " ", "\"'", "\"'"); while ((argv[i] != NULL) && (i < MAXARG)) { // unquote the previous argument (last argument is always NULL) unquote(argv[i], argv[i]); argv[++i] = strmbtok(NULL, " ", "\"'", "\"'"); }; // save the number of arguments argc = i; while(!strcasecmp(argv[0], "if")) { if(argc > 3) { int aval = getvar(argv[1], 0); int cval = strtol(argv[3], NULL, 0); if(!strcmp(argv[2], ">")) { if(!(aval > cval)) return; } else if(!strcmp(argv[2], "<")) { if(!(aval < cval)) return; } else if(!strcmp(argv[2], ">=")) { if(!(aval >= cval)) return; } else if(!strcmp(argv[2], "<=")) { if(!(aval <= cval)) return; } else if(!strcmp(argv[2], "==")) { if(!(aval == cval)) return; } else if(!strcmp(argv[2], "!=")) { if(!(aval != cval)) return; } else { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Unknown if conditional '%s'.\r\n", argv[1]); return; } // condition met, parse rest of the line, skipping over "then" if present. if(!strcmp(argv[3], "then")) { argc -= 4; for(int iz=0; iz < argc; iz++) { argv[iz] = argv[iz+4]; } } else { argc -= 3; for(int iz=0; iz < argc; iz++) { argv[iz] = argv[iz+3]; } } } else { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Not enough parameters to if command.\r\n"); return; } } // find the handler responsible for this command for(int ii=0; GlobalCommands[ii].Name; ii++) { if (!strcmp(argv[0], GlobalCommands[ii].Name)) { errorlevel = 0; GlobalCommands[ii].Func(argc, argv); return; } } // command not recognized. print message and re-generate prompt. errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Unknown command '%s'.\r\n", argv[0]); } void cmd_handler() { char c = Serial.read(); switch (c) { case '\n': break; case '\r': // terminate the msg and reset the msg ptr. then send // it to the handler for processing. *msg_ptr = '\0'; if(msg[0] != 0) { Serial.print("\r\n"); cmdParse((char *)msg); execLoop(); cmdDisplay(); } else { Serial.print("\r\n"); cmdDisplay(); } msg_ptr = msg; break; case '\b': // backspace if (msg_ptr > msg) { *msg_ptr = '\0'; Serial.print(c); msg_ptr--; } else { Serial.print('\x07'); msg_ptr = msg; msg[0] = 0; } break; default: // normal character entered. add it to the buffer if(msg_ptr < (msg + sizeof(msg) - 1)) { Serial.print(c); *msg_ptr++ = c; *msg_ptr = '\0'; } else { Serial.print('\x07'); } break; } } void cmdPoll() { if (Serial.available()) { cmd_handler(); } } void gotocmd(int argc, char **argv) { fspos_t tmp_pos; int tmp_line = 1; char tmp_cmd[256]; if(scriptlevel < 0) { errorlevel = -1; Serial.printf("ERROR: goto is only valid inside a script.\r\n"); return; } // Save our current position in case we can't find the label (should we exit the script on goto failure?) scriptstack[scriptlevel].m_file.fgetpos(&tmp_pos); // Rewind to the top of the script in case the label is up instead of down. scriptstack[scriptlevel].m_file.rewind(); // Find the label while(scriptstack[scriptlevel].m_file.available()) { int n = scriptstack[scriptlevel].m_file.fgets(tmp_cmd, sizeof(tmp_cmd)-1, NULL); if (n <= 0) { Serial.printf("Reading script '%s' failed at line %d.\r\n", exec_filename, exec_line); break; } if(tmp_cmd[0] == ':') { if(!strcmp(tmp_cmd+1, argv[1])) { // Found it, script continues on the next line after the label. Update the line counter so any errors show the correct line reference. exec_line = tmp_line; return; } } tmp_line++; } // We couldn't find the label, restore the file position. scriptstack[scriptlevel].m_file.fsetpos(&tmp_pos); errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Label '%s' was not found.\r\n", argv[1]); } void exitcmd(int argc, char **argv) { if(scriptlevel < 0) { errorlevel = -1; Serial.printf("ERROR: exit is only valid inside a script.\r\n"); return; } scriptstack[scriptlevel].m_file.close(); exec_line = 0; scriptlevel--; } int execLoop() { char exec_cmd[256]; while(scriptlevel >= 0) { if(scriptstack[scriptlevel].m_file.available()) { int n = scriptstack[scriptlevel].m_file.fgets(exec_cmd, sizeof(exec_cmd)-1, NULL); if (n <= 0) { Serial.printf("Reading script '%s' failed at line %d.\r\n", exec_filename, exec_line); goto close_file; } exec_cmd[sizeof(exec_cmd)-1] = 0; for(int ii = 0; ii < n; ii++) { if(exec_cmd[ii] == '\r') exec_cmd[ii] = 0; if(exec_cmd[ii] == '\n') exec_cmd[ii] = 0; } if(echo_on && exec_cmd[0]!='@') { cmdDisplay(); Serial.printf("%s\r\n", exec_cmd); } if(exec_cmd[0] != '#') cmdParse((exec_cmd[0]!='@') ? exec_cmd : exec_cmd+1); exec_line++; } else { close_file: scriptstack[scriptlevel].m_file.close(); exec_line = 0; scriptlevel--; } } return 0; } void execscript(const char *script) { char tmp_path[MAX_FILE_PATH+1]; char *filename = tmp_path; fixupPath(tmp_path, script); if(!strncmp(filename, "/sd/", 4)) { filename += 3; if(!sd.exists(filename)) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": file '%s' not found.\r\n", script); return; } } else { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": file '%s' not found.\r\n", script); return; } if(scriptlevel >= MAX_SCRIPTLEVEL-1) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Script stack space exhausted.\r\n"); return; } scriptlevel++; scriptstack[scriptlevel].m_file = sd.open(filename, O_RDONLY); if(scriptstack[scriptlevel].m_file.isOpen()) { strcpy(exec_filename, script); exec_line = 1; } else { scriptlevel--; errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Unable to open script '%s'.\r\n", script); return; } } void execcmd(int argc, char **argv) { if(argc > 1) { execscript(argv[1]); } } void echocmd(int argc, char **argv) { if(argc > 1) { if(!strcmp("off", argv[1])) { echo_on = 0; } else if(!strcmp("on", argv[1])) { echo_on = 1; } else { for(int i = 1; i= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Command table invalid.\r\n"); return; } if(singleCommand && table[cmd].FullHelp) { Serial.print(table[cmd].FullHelp); } else if(table[cmd].ShortHelp) { Serial.print("\r\n"); Serial.print(table[cmd].Name); for(uint8_t i = 0; i < (16 - strlen(table[cmd].Name)); i++) { Serial.print(" "); } Serial.printf("%s\r\n", table[cmd].ShortHelp); if(table[cmd].MinParams) { Serial.printf(" (Requires %d %s)\r\n", table[cmd].MinParams, (table[cmd].MinParams>1) ? "Parameters" : "Parameter"); } if(singleCommand) Serial.print("\r\n"); } } void cmdDispatchHelp(Commands_t *table, int argc, char **argv) { if(!table) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": invalid command table.\r\n"); return; } if(argc<2) { for(int ii=0; table[ii].Name; ii++) { cmdCommandHelp(false, table, ii); } Serial.print("\r\n"); return; } for(int ii=0; table[ii].Name; ii++) { if(!strcmp(argv[1], table[ii].Name)) { if(table[ii].Dispatch) { cmdDispatchHelp(table[ii].Dispatch, argc-1, &argv[1]); } else { cmdCommandHelp(true, table, ii); } return; } } } void clearcmd(int argc, char **argv) { Serial.printf("\x1b[2J\x1b[H"); } void help(int argc, char **argv) { cmdDispatchHelp(GlobalCommands, argc, argv); } void cmdDispatch(Commands_t *table, int argc, char **argv) { int ii; if(!table) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Command table invalid.\r\n"); return; } argc--; if(argc<1) { for(ii=0; table[ii].Name; ii++) { cmdCommandHelp(false, table, ii); } return; } for(ii=0; table[ii].Name; ii++) { if(!strcmp(argv[1], table[ii].Name)) { if(argc < table[ii].MinParams) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Not enough parameters given\r\n"); return; } table[ii].Func(argc, &argv[1]); } } } void fixupPath(char *out, const char *in) { char *parent; char tmp_path[MAX_FILE_PATH+1]; if(!in) return; if(!out) return; //unquote(tmp_path, in); strcpy(tmp_path, in); // Skip leading "./" if present if(!strncmp(tmp_path, "./", 2)) { sprintf(out, "%s/%s", cmd_prefix, tmp_path+2); } else if(!strcmp(tmp_path, "..")) { // Ascend if ".." strcpy(tmp_path, cmd_prefix); parent = strrchr(tmp_path, '/'); if(parent && (tmp_path != parent)) { parent[0] = 0; } strcpy(out, tmp_path); } else if(*tmp_path == '/') { // Fully qualified path, replace everything strcpy(out, tmp_path); } else { // Relative path, append parent = strrchr(cmd_prefix, '/'); if(parent[1] != 0) { sprintf(out, "%s/%s", cmd_prefix, tmp_path); } else { sprintf(out, "%s%s", cmd_prefix, tmp_path); } } // Make sure there is not a trailing / parent = strrchr(out, '/'); if((parent != out) && (parent[1] == 0)) { parent[0] = 0; } } // Remove any command prefix levels void changeDirectory(int argc, char **argv) { char new_prefix[256]; char *suffix = NULL; if(argc>=2) { // Parse the path string fixupPath(new_prefix, argv[1]); if(!strncmp("/vdevs/vdev", new_prefix, 11)) { char *vdev_name = new_prefix + 11; uint8_t v = strtol(vdev_name, &suffix, 0); if((v < NUM_VDEV) && (!suffix || (suffix[0] == 0) || !strcmp(suffix, "/"))) { sprintf(cmd_prefix, "/vdevs/vdev%d", v); return; } goto e_invalidpath; } if(!strncmp("/tgts/tgt", new_prefix, 9)) { char *tgt_name = new_prefix + 9; char *lun_name = NULL; uint8_t id = strtol(tgt_name, &lun_name, 0); if(id < NUM_SCSIID) { if(!lun_name || (lun_name[0] == 0) || !strcmp(lun_name, "/")) { sprintf(cmd_prefix, "/tgts/tgt%d", id); return; } else if(!strncmp("/lun", lun_name, 4)) { lun_name += 4; uint8_t lun = strtol(lun_name, &suffix, 0); if((lun < NUM_SCSILUN) && (!suffix || (suffix[0] == 0) || !strcmp(suffix, "/"))) { sprintf(cmd_prefix, "/tgts/tgt%d/lun%d", id, lun); return; } } } goto e_invalidpath; } if(!strcmp(new_prefix, "/") || !strcmp(new_prefix, "/tgts") || !strcmp(new_prefix, "/vdevs") || !strcmp(new_prefix, "/nv") || !strcmp(new_prefix, "/raw") || !strcmp(new_prefix, "/sd") || !strcmp(new_prefix, "/diag")|| !strcmp(new_prefix, "/diag/sd") ) { strcpy(cmd_prefix, new_prefix); return; } else if(!strncmp(new_prefix, "/sd/", 4)) { if((new_prefix[4] == 0) || sd.exists(new_prefix+3)) { strcpy(cmd_prefix, new_prefix); return; } } e_invalidpath: errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Path '%s' not found.\r\n", new_prefix); } else { Serial.printf("%s\r\n", cmd_prefix); } } char tmp_left[256]; char tmp_right[256]; void printDirectory(int level, uint8_t lines, const char *l, const char *r) { for(int pad = 0; pad < level; pad++) { if(lines & (1 << (level - pad))) { Serial.print("| "); } else { Serial.print(" "); } } Serial.print("o- "); int dots = 78 - ((level*2) + 3 + strlen(l) + strlen(r)); Serial.print(l); while(dots--) { Serial.print("."); } Serial.print(r); Serial.print("\r\n"); } void showVDEV(int level, uint8_t lines, uint8_t v) { VirtualDevice_t *h = &m_vdev[v]; if(h->m_enabled) { sprintf(tmp_left, "vdev%d/ ", v); printDirectory(level, lines, tmp_left, " [...]"); level++; lines<<=1; lines|=1; switch(h->m_type) { case DEV_DISK: strcpy(tmp_right, " [disk]"); break; case DEV_OPTICAL: strcpy(tmp_right, " [optical]"); break; case DEV_TAPE: strcpy(tmp_right, " [tape]"); break; default: strcpy(tmp_right, " [unknown]"); break; } printDirectory(level, lines, "$type ", tmp_right); if((h->m_id < NUM_SCSIID) && (h->m_lun < NUM_SCSILUN)) { sprintf(tmp_right, " [/tgts/tgt%d/lun%d]", h->m_id, h->m_lun); printDirectory(level, lines, "$mapping", tmp_right); } if(h->m_file) { sprintf(tmp_right, " [%s]", h->m_filename); printDirectory(level, lines, "$filename ", tmp_right); } if((h->m_fileSize) && (h->m_blocksize)) { sprintf(tmp_right, " [%llu]", h->m_fileSize / h->m_blocksize); printDirectory(level, lines, "$blocks ", tmp_right); } if(h->m_blocksize) { sprintf(tmp_right, " [%d]", h->m_blocksize); printDirectory(level, lines, "$blocksize ", tmp_right); } } } void showLUN(int level, uint8_t lines, uint8_t id, uint8_t lun) { if(!level) { sprintf(tmp_left, "/tgts/tgt%d/lun%d/ ", id, lun); } else { sprintf(tmp_left, "lun%d/ ", lun); } uint8_t v = m_vdevmap[id][lun]; if(v < NUM_VDEV) { printDirectory(level, lines, tmp_left, " [...]"); level++; lines <<= 1; lines |= 1; sprintf(tmp_right, " [/vdevs/vdev%d]", v); printDirectory(level, lines, "$mapping", tmp_right); } } void showTarget(int level, uint8_t lines, uint8_t id) { if(!level) { sprintf(tmp_left, "/tgts/tgt%d/ ", id); } else { sprintf(tmp_left, "tgt%d/ ", id); } if(scsi_id_mask & (1<readSector(0, cardMBR); for(uint8_t partIndex = 0; partIndex < 4; partIndex++) { sprintf(tmp_left, "part%d", partIndex); switch(mbr->part[partIndex].type) { case 0x04: case 0x06: case 0x0B: case 0x0C: case 0x0E: printDirectory(1, 0, tmp_left, "[FAT Filesystem]"); break; case 0x07: printDirectory(1, 0, tmp_left, "[EXFAT/NTFS Filesystem]"); break; case 0x87: sprintf(tmp_right, "[Emulated %dMB Drive]", (int)(mbr->part[partIndex].totalSectors / 2048)); printDirectory(1, 0, tmp_left, tmp_right); break; } } return; } if(!strcmp(local_prefix, "/diag/sd")) { printDirectory(0, 0, "/diag/sd/ ", " [...]"); sprintf(tmp_right, " [SdFat %s]", SD_FAT_VERSION_STR); printDirectory(1, 0, "$version ", tmp_right); switch(SDFAT_FILE_TYPE) { case 0: strcpy(tmp_right, " [FAT16]"); break; case 1: strcpy(tmp_right, " [FAT16/FAT32]"); break; case 2: strcpy(tmp_right, " [ExFAT]"); break; case 3: strcpy(tmp_right, " [FAT16/FAT32/ExFAT]"); break; } printDirectory(1, 0, "$filesystems ", tmp_right); sprintf(tmp_right, " [%d]", MAX_FILE_PATH); printDirectory(1, 0, "$max_filename_length ", tmp_right); csd_t sd_csd; if(sd.card()->readCSD(&sd_csd)) { switch (sd.card()->type()) { case SD_CARD_TYPE_SD1: strcpy(tmp_right, " [SD1]"); break; case SD_CARD_TYPE_SD2: strcpy(tmp_right, " [SD2]"); break; case SD_CARD_TYPE_SDHC: if (sdCardCapacity(&sd_csd) < 70000000) { strcpy(tmp_right, " [SDHC]"); } else { strcpy(tmp_right, " [SDXC]"); } break; default: strcpy(tmp_right, " [unknown]"); break; } printDirectory(1, 0, "$card_type ", tmp_right); uint32_t cardsize = 0.000512 * sdCardCapacity(&sd_csd); if(cardsize >= 10000) { sprintf(tmp_right, " [%lu GB]", cardsize); } else { sprintf(tmp_right, " [%lu MB]", cardsize); } printDirectory(1, 0, "$card_size ", tmp_right); } cid_t sd_cid; if(sd.card()->readCID(&sd_cid)) { sprintf(tmp_right, " [0x%02X]", sd_cid.mid); printDirectory(1, 0, "$card_manufacturer ", tmp_right); sprintf(tmp_right, " [%c%c]", sd_cid.oid[0], sd_cid.oid[1]); printDirectory(1, 0, "$card_oem ", tmp_right); sprintf(tmp_right, " [%c%c%c%c%c]", sd_cid.pnm[0], sd_cid.pnm[1], sd_cid.pnm[2], sd_cid.pnm[3], sd_cid.pnm[4]); printDirectory(1, 0, "$card_name ", tmp_right); sprintf(tmp_right, " [%d.%d]", sd_cid.prv_n, sd_cid.prv_m); printDirectory(1, 0, "$card_version ", tmp_right); sprintf(tmp_right, " [%02d/20%02d]", sd_cid.mdt_month, (sd_cid.mdt_year_high << 4) | sd_cid.mdt_year_low); printDirectory(1, 0, "$card_date ", tmp_right); sprintf(tmp_right, " [%08lX]", sd_cid.psn); printDirectory(1, 0, "$card_serial ", tmp_right); } return; } if(!strcmp(local_prefix, "/tgts")) { sprintf(tmp_right, " [%d Target%s]", NUM_SCSIID, (NUM_SCSIID != 1) ? "s" : ""); printDirectory(0, 0, "/tgts/ ", tmp_right); for(uint8_t id = 0; id < NUM_SCSIID; id++) { showTarget(1, 1, id); } return; } if(!strncmp(local_prefix, "/tgts/tgt", 9)) { char *tgt_name = local_prefix + 9; char *lun_name = NULL; uint8_t id = strtol(tgt_name, &lun_name, 0); if(id < NUM_SCSIID) { if(!lun_name || (lun_name[0] == 0) || !strcmp(lun_name, "/")) { showTarget(0, 0, id); return; } else if(!strncmp("/lun", lun_name, 4)) { lun_name += 4; uint8_t lun = strtol(lun_name, &suffix, 0); if((lun < NUM_SCSILUN) && (!suffix || (suffix[0] == 0) || !strcmp(suffix, "/"))) { showLUN(0, 0, id, lun); return; } } } } if(!strcmp(local_prefix, "/vdevs")) { sprintf(tmp_right, " [%d Storage Objects]", m_vdevcnt); printDirectory(0, 0, "/vdevs/ ", tmp_right); for(v = 0; v < NUM_VDEV; v++) { showVDEV(1, 1, v); } return; } if(!strncmp(local_prefix, "/vdevs/vdev", 11)) { char *vdev_name = local_prefix + 11; v = strtol(vdev_name, &suffix, 0); if((v < NUM_VDEV) && (!suffix || (suffix[0] == 0) || !strcmp(suffix, "/"))) { showVDEV(0, 0, v); return; } } if(!strcmp(local_prefix, "/nv") || !strncmp(local_prefix, "/nv/", 4)) { File root = lfs.open("/"); while(true) { File entry = root.openNextFile(); if(! entry) { break; } if(!entry.isDirectory()) { sprintf(tmp_left, "%s ", entry.name()); if(entry.size() >= 8192) { sprintf(tmp_right, " [%llu KB]", entry.size() >> 10); } else { sprintf(tmp_right, " [%llu Bytes]", entry.size()); } printDirectory(1, 0, tmp_left, tmp_right); } entry.close(); } root.close(); } if(!strcmp(local_prefix, "/sd") || !strncmp(local_prefix, "/sd/", 4)) { char name[MAX_FILE_PATH+1]; SdFile root; SdFile file; if(!strcmp(local_prefix, "/sd")) strcat(local_prefix, "/"); root.open(local_prefix+3); if(!root.isDir()) { sprintf(tmp_left, "%s ", local_prefix); if(root.fileSize() >= 2000000000) { sprintf(tmp_right, " [%llu GB]", root.fileSize() / 1000000000); } else if(root.fileSize() >= 2000000) { sprintf(tmp_right, " [%llu MB]", root.fileSize() / 1000000); } else if(root.fileSize() >= 2000) { sprintf(tmp_right, " [%llu KB]", root.fileSize() / 1000); } else { sprintf(tmp_right, " [%llu Bytes]", root.fileSize()); } printDirectory(0, 0, tmp_left, tmp_right); root.close(); return; } sprintf(tmp_left, "%s ", local_prefix); printDirectory(0, 0, tmp_left, " [...]"); while(file.openNext(&root, O_READ)) { file.getName(name, MAX_FILE_PATH+1); if(file.isDir()) { sprintf(tmp_left, "%s/ ", name); printDirectory(1, 0, tmp_left, " [...]"); } else { sprintf(tmp_left, "%s ", name); if(file.fileSize() >= 2000000000) { sprintf(tmp_right, " [%llu GB]", file.fileSize() / 1000000000); } else if(file.fileSize() >= 2000000) { sprintf(tmp_right, " [%llu MB]", file.fileSize() / 1000000); } else if(file.fileSize() >= 2000) { sprintf(tmp_right, " [%llu KB]", file.fileSize() / 1000); } else { sprintf(tmp_right, " [%llu Bytes]", file.fileSize()); } printDirectory(1, 0, tmp_left, tmp_right); } file.close(); } root.close(); return; } errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Path '%s' not found.\r\n", local_prefix); } void unlinkcmd(int argc, char **argv) { FsFile file; /* File object */ char tmp_path[MAX_FILE_PATH+1]; char *vdev_name = NULL; char *tgt_name = NULL; char *lun_name = NULL; char *suffix = NULL; uint8_t v = 0xff; uint8_t id = 0xff; uint8_t lun = 0xff; VirtualDevice_t *h = NULL; if(argc < 2) { return; } fixupPath(tmp_path, argv[1]); if(!strncmp("/tgts/tgt", tmp_path, 9)) { tgt_name = tmp_path + 9; id = strtol(tgt_name, &lun_name, 0); if(id < NUM_SCSIID) { if((lun_name) && !strncmp("/lun", lun_name, 4)) { // Disable a specific LUN lun_name += 4; lun = strtol(lun_name, &suffix, 0); if((lun < NUM_SCSILUN) && (!suffix || (suffix[0] == 0) || !strcmp(suffix, "/"))) { v = m_vdevmap[id][lun]; if(v < NUM_VDEV) { h = &m_vdev[v]; h->m_id = 0xff; h->m_lun = 0xff; m_vdevmap[id][lun] = 0xff; return; } } } else if(!lun_name || (lun_name[0] == 0) || !strcmp(lun_name, "/")) { // Disable this target and all LUNs under it scsi_id_mask &= ~(1 << id); for(lun = 0; lun < NUM_SCSILUN; lun++) { v = m_vdevmap[id][lun]; if(v < NUM_VDEV) { h = &m_vdev[v]; h->m_id = 0xff; h->m_lun = 0xff; m_vdevmap[id][lun] = 0xff; } } return; } } } if(!strncmp("/vdevs/vdev", tmp_path, 11)) { vdev_name = tmp_path + 11; v = strtol(vdev_name, &suffix, 0); if((v < NUM_VDEV) && (!suffix || (suffix[0] == 0) || !strcmp(suffix, "/"))) { id = h->m_id; lun = h->m_lun; h = &m_vdev[v]; h->m_id = 0xff; h->m_lun = 0xff; if((id < NUM_SCSIID) && (lun < NUM_SCSILUN)) m_vdevmap[id][lun] = 0xff; return; } } if(!strncmp(tmp_path, "/sd/", 4)) { sd.remove(tmp_path+3); return; } Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Path '%s' not found.\r\n", tmp_path); } void makeimagecmd(int argc, char **argv) { FsFile file; char tmp_path[MAX_FILE_PATH+1]; uint64_t fileSize = 0; int i; bool write_mbr = 0; uint64_t cylinders = 0; uint64_t heads = 16; uint64_t sectors = 63; uint64_t blocksize = 512; tmp_path[0] = 0; for(i = 1; i < argc; i++) { if(argv[i][0] == '/') { fixupPath(tmp_path, argv[i]); if(strncmp(tmp_path, "/sd/", 4)) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Can only create images on the SD Card.\r\n"); return; } } if(!strcmp(argv[i], "-msdos")) { write_mbr = 1; } else if(!strcmp(argv[i], "-b") && (i+1 < argc)) { i++; blocksize = strtoul(argv[i], NULL, 0); } else if(!strcmp(argv[i], "-c") && (i+1 < argc)) { i++; cylinders = strtoul(argv[i], NULL, 0); } else if(!strcmp(argv[i], "-h") && (i+1 < argc)) { i++; heads = strtoul(argv[i], NULL, 0); } else if(!strcmp(argv[i], "-s") && (i+1 < argc)) { i++; sectors = strtoul(argv[i], NULL, 0); } else { char *suffix = NULL; fileSize = strtoul(argv[i], &suffix, 0); if(suffix && suffix[0] != 0) { if(!strcmp(suffix, "KB")) fileSize *= 1000ull; if(!strcmp(suffix, "KiB")) fileSize *= 1024ull; if(!strcmp(suffix, "MB")) fileSize *= 1000000ull; if(!strcmp(suffix, "MiB")) fileSize *= 1024ull * 1024ull; if(!strcmp(suffix, "GB")) fileSize *= 1000000000ull; if(!strcmp(suffix, "GiB")) fileSize *= 1024ull * 1024ull * 1024ull; } } } if(tmp_path[0] == 0) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": no path specified.\r\n"); return; } if((fileSize == 0) && (cylinders != 0)) { fileSize = cylinders * heads * sectors * blocksize; } else if(fileSize != 0){ // Fixup image size to our CHS formula // Capping out at ~2GB cylinders = fileSize / (blocksize * heads * sectors); if(fileSize & 0xFFFFF) cylinders++; if(cylinders > 4095) cylinders = 4095; fileSize = cylinders * (blocksize * heads * sectors); } else { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": no image size specified.\r\n"); return; } if(sd.exists(tmp_path+3)) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": file '%s' already exists.\r\n", tmp_path); return; } if(fileSize < (5ull * 1024ull * 1024ull)) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Image would be less than 5 MiB.\r\n"); return; } if(fileSize > (2048ull * 1024ull * 1024ull)) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Image would be larger than 2 GiB.\r\n"); return; } if(!file.open(tmp_path+3, O_WRONLY | O_CREAT | O_TRUNC)) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Unable to open '%s'.\r\n", tmp_path); } else { if(write_mbr) { file.write(mbr_bin, 512); fileSize -= 512; } memset(zero, 0, 512); while(fileSize) { if((fileSize & 0x1FFF) == 0) Serial.printf("."); if((fileSize & 0x7FFFF) == 0) Serial.printf("\r\n"); file.write(zero, (fileSize < 512) ? fileSize : 512); fileSize -= (fileSize < 512) ? fileSize : 512; } Serial.printf("\r\n"); file.close(); return; } errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Unable to process command as requested.\r\n"); return; } void catcmd(int argc, char **argv) { FsFile file; /* File object */ char tmp_path[MAX_FILE_PATH+1]; char tmp_str[256]; char *filename = tmp_path; if(argc < 2) { return; } fixupPath(tmp_path, argv[1]); if(!strncmp(filename, "/nv/", 4)) { filename += 3; } else if(!strncmp(filename, "/sd/", 4)) { filename += 3; } else { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": file '%s' not found.\r\n", tmp_path); return; } if(!sd.exists(filename)) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": file '%s' not found.\r\n", tmp_path); return; } if(!file.open(filename, O_RDONLY)) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Unable to open '%s'.\r\n", tmp_path); } else { while(file.available()) { int n = file.fgets(tmp_str, sizeof(tmp_str)-1, NULL); if (n <= 0) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Reading '%s' failed.\r\n", tmp_path); break; } Serial.print(tmp_str); } file.close(); } } void enablecmd(int argc, char **argv) { uint8_t id = 0xff; char *tgt_name = NULL; char *suffix; char tmp_path[MAX_FILE_PATH+1]; // The logic here is slightly complicated to allow specifying enable /tgts/tgtN from anywhere, but otherwise require we are in /tgts if((argc<2) && strncmp("/tgts", cmd_prefix, 5)) { goto e_invalidpath; } if((argc<2) && !strcmp("/tgts", cmd_prefix)) { goto e_notarget; } if(argc<2) { strcpy(tmp_path, cmd_prefix); } else { fixupPath(tmp_path, argv[1]); } if(strncmp("/tgts/", tmp_path, 6)) { goto e_invalidpath; } if(!strncmp("/tgts/tgt", tmp_path, 9)) { tgt_name = tmp_path + 9; id = strtol(tgt_name, &suffix, 0); } if(id < NUM_SCSIID) { scsi_id_mask |= (1<= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.print(": Invalid target specified.\r\n"); return; e_invalidpath: errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.print(": The enable command is not valid in this context.\r\n"); return; e_notarget: errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.print(": No target specified.\r\n"); return; } void setcmd(int argc, char **argv) { uint8_t v = 0xff; char *vdev_name = NULL; char *param_name = NULL; char tmp_path[MAX_FILE_PATH+1]; if(argc<2) { goto e_syntax; } fixupPath(tmp_path, argv[1]); // Allow specifying set /vdevs/vdevN/parameter from anywhere, but otherwise require we are in /vdevs/vdevN if(!strncmp("/global", tmp_path, 7)) { param_name = tmp_path + 7; if(!strcasecmp(param_name, "/quirks")) { if(argc<3) { Serial.printf("0x%02x\r\n", default_quirks); } else { default_quirks = strtol(argv[2], NULL, 0); } return; } } // Allow specifying set /vdevs/vdevN/parameter from anywhere, but otherwise require we are in /vdevs/vdevN if(!strncmp("/vdevs/vdev", tmp_path, 11)) { vdev_name = tmp_path + 11; v = strtol(vdev_name, ¶m_name, 0); if(v < NUM_VDEV) { VirtualDevice_t *h = &m_vdev[v]; if((param_name) && !strcasecmp(param_name, "/blocksize")) { if(argc<3) { Serial.printf("%d\r\n", h->m_blocksize); } else { h->m_blocksize = strtol(argv[2], NULL, 0); } return; } if((param_name) && !strcasecmp(param_name, "/sectors")) { if(argc<3) { Serial.printf("%d\r\n", h->m_sectors); } else { h->m_blocksize = strtol(argv[2], NULL, 0); } return; } if((param_name) && !strcasecmp(param_name, "/heads")) { if(argc<3) { Serial.printf("%d\r\n", h->m_heads); } else { h->m_blocksize = strtol(argv[2], NULL, 0); } return; } if((param_name) && !strcasecmp(param_name, "/cylinders")) { if(argc<3) { Serial.printf("%d\r\n", h->m_cylinders); } else { h->m_blocksize = strtol(argv[2], NULL, 0); } return; } if((param_name) && !strcasecmp(param_name, "/quirks")) { if(argc<3) { Serial.printf("0x%02x\r\n", h->m_quirks); } if(!strcasecmp(argv[2], "SASI") || !strcasecmp(argv[2], "+SASI")) { h->m_quirks |= QUIRKS_SASI; } if(!strcasecmp(argv[2], "-SASI")) { h->m_quirks &= ~QUIRKS_SASI; } if(!strcasecmp(argv[2], "APPLE") || !strcasecmp(argv[2], "+APPLE")) { h->m_quirks |= QUIRKS_APPLE; } if(!strcasecmp(argv[2], "-APPLE")) { h->m_quirks &= ~QUIRKS_APPLE; } else { h->m_quirks = strtol(argv[2], NULL, 0); } return; } if((param_name) && !strcasecmp(param_name, "/type")) { if(argc<3) { switch(h->m_type) { case DEV_DISK: strcpy(tmp_right, " [disk]"); break; case DEV_OPTICAL: strcpy(tmp_right, " [optical]"); break; case DEV_TAPE: strcpy(tmp_right, " [tape]"); break; default: strcpy(tmp_right, " [unknown]"); break; } printDirectory(0, 0, "$type ", tmp_right); } else { #if SUPPORT_DISK if(!strcasecmp(argv[2], "disk")) { ConfigureDisk(h, NULL); return; } #endif /* SUPPORT_DISK */ #if SUPPORT_OPTICAL if(!strcasecmp(argv[2], "optical") || !strcasecmp(argv[2], "cdrom")) { ConfigureOptical(h, NULL); return; } #endif /* SUPPORT_OPTICAL */ #if SUPPORT_TAPE if(!strcasecmp(argv[2], "tape")) { ConfigureTape(h, NULL); return; } #endif /* SUPPORT_TAPE */ } goto e_invalidvalue; } goto e_invalidparam; } goto e_invalidpath; } e_invalidpath: errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.print(": The set command is not valid in this context.\r\n"); return; e_invalidparam: errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Invalid parameter '%s' specified.\r\n", param_name); return; e_invalidvalue: errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Invalid value '%s' for parameter '%s' specified.\r\n", argv[2], param_name); return; e_syntax: errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.print(": Command syntax violation.\r\n"); return; } void createcmd(int argc, char **argv) { if(strncmp("/vdev", cmd_prefix, 5)) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.print(": The create command is not valid in this context.\r\n"); return; } if(m_vdevcnt >= NUM_VDEV) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.print(": Virtual Device pool exhausted.\r\n"); return; } if(argc<2) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.print(": No device type specified.\r\n"); return; } uint8_t v = m_vdevcnt; VirtualDevice_t *h = &m_vdev[v]; h->m_quirks = default_quirks; #if SUPPORT_DISK if(!strcasecmp(argv[1], "disk")) { ConfigureDisk(h, NULL); goto success; } #endif /* SUPPORT_DISK */ #if SUPPORT_OPTICAL if(!strcasecmp(argv[1], "optical") || !strcasecmp(argv[1], "cdrom")) { ConfigureOptical(h, NULL); goto success; } #endif /* SUPPORT_OPTICAL */ #if SUPPORT_TAPE if(!strcasecmp(argv[1], "tape")) { ConfigureTape(h, NULL); goto success; } #endif /* SUPPORT_TAPE */ #if SUPPORT_CONTROL if(!strcasecmp(argv[1], "control")) { ConfigureControl(h, NULL); goto success; } #endif /* SUPPORT_CONTROL */ //failure: errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.print(": Unknown device type specified.\r\n"); return; success: sprintf(cmd_prefix, "/vdevs/vdev%d", v); h->m_enabled = true; h->m_id = 0xff; h->m_lun = 0xff; m_vdevcnt++; } void mountcmd(int argc, char **argv) { char tmp_path[MAX_FILE_PATH+1]; char *vdev_name = NULL; VirtualDevice_t *h; uint8_t v = 0xff; if(!strncmp("/vdevs/vdev", cmd_prefix, 11)) { vdev_name = cmd_prefix + 11; v = strtol(vdev_name, NULL, 0); } if(v < NUM_VDEV) { h = &m_vdev[v]; if(h->m_blocksize == 0) { if(h->m_type == DEV_TAPE) { h->m_blocksize = 1024; } else { h->m_blocksize = 512; } } } else if(argc>=2) { errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.print(": The mount command is not valid in this context.\r\n"); return; } else { // TODO: Print out all mounted images return; } if(argc<2) { // TODO: Print out the mounted image Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.print(": No backingstore specified.\r\n"); return; } fixupPath(tmp_path, argv[1]); if(!strncmp(tmp_path, "/sd/", 4)) { if(OpenDiskImage(h, tmp_path, 512)) { strcpy(h->m_filename, tmp_path); h->m_enabled = true; return; } } if(!strncmp(tmp_path, "/raw/", 5)) { if(OpenDiskImage(h, tmp_path, 512)) { strcpy(h->m_filename, tmp_path); h->m_enabled = true; return; } } h->m_fileSize = 0; h->m_blocksize = 0; errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.print(": Unsupported backingstore path.\r\n"); return; } void mapcmd(int argc, char **argv) { char tmp_path[MAX_FILE_PATH+1]; char *vdev_name = NULL; char *tgt_name = NULL; char *lun_name = NULL; char *suffix = NULL; VirtualDevice_t *h; uint8_t v = 0xff; uint8_t id = 0xff; uint8_t lun = 0xff; if(!strncmp("/vdevs/vdev", cmd_prefix, 11)) { vdev_name = cmd_prefix + 11; v = strtol(vdev_name, &suffix, 0); if((v < NUM_VDEV) && (!suffix || (suffix[0] == 0) || !strcmp(suffix, "/"))) { h = &m_vdev[v]; if(argc>=2) { fixupPath(tmp_path, argv[1]); if(!strncmp("/tgts/tgt", tmp_path, 9)) { tgt_name = tmp_path + 9; id = strtol(tgt_name, &lun_name, 0); if((id < NUM_SCSIID) && (lun_name)) { if(!strncmp("/lun", lun_name, 4)) { lun_name += 4; lun = strtol(lun_name, &suffix, 0); if((lun < NUM_SCSILUN) && (!suffix || (suffix[0] == 0) || !strcmp(suffix, "/"))) { h->m_id = id; h->m_lun = lun; m_vdevmap[id][lun] = v; return; } } } } goto e_invalidtarget; } else { sprintf(tmp_left, "/vdevs/vdev%d/$mapping ", v); sprintf(tmp_right, " [/tgts/tgt%d/lun%d]\r\n", h->m_id, h->m_lun); printDirectory(0, 0, tmp_left, tmp_right); return; } } } if(!strncmp("/tgts/tgt", cmd_prefix, 9)) { tgt_name = cmd_prefix + 9; id = strtol(tgt_name, &lun_name, 0); if((id < NUM_SCSIID) && (lun_name)) { if(!strncmp("/lun", lun_name, 4)) { lun_name += 4; lun = strtol(lun_name, &suffix, 0); if((lun < NUM_SCSILUN) && (!suffix || (suffix[0] == 0) || !strcmp(suffix, "/"))) { if(argc>=2) { fixupPath(tmp_path, argv[1]); if(!strncmp("/vdevs/vdev", tmp_path, 11)) { vdev_name = cmd_prefix + 11; v = strtol(vdev_name, &suffix, 0); if((v < NUM_VDEV) && (!suffix || (suffix[0] == 0) || !strcmp(suffix, "/"))) { h = &m_vdev[v]; h->m_id = id; h->m_lun = lun; m_vdevmap[id][lun] = v; return; } } goto e_invalidtarget; } else { v = m_vdevmap[id][lun]; if(v < NUM_VDEV) { sprintf(tmp_left, "/tgts/tgt%d/lun%d/$mapping ", id, lun); sprintf(tmp_right, " [/vdevs/vdev%d]\r\n", v); printDirectory(0, 0, tmp_left, tmp_right); } return; } } } } } //e_invalidcontext: errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.print(": The map command is not valid in this context.\r\n"); return; e_invalidtarget: errorlevel = -1; Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Invalid target '%s' specified.\r\n", tmp_path); return; } void saveconfig(int argc, char **argv) { FsFile config_file; config_file = sd.open("/greenscsi.cfg", O_WRONLY | O_CREAT | O_TRUNC); if(!config_file.isOpen()) { Serial.print("ERROR"); if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename); Serial.printf(": Unable to open 'greenscsi.cfg' for writing.\r\n"); return; } config_file.print( "@echo off\r\n\r\n"); if(scsi_id_mask != 0) { config_file.print( "cd /tgts\r\n"); for(uint8_t id = 0; id < NUM_SCSIID; id++) { if(scsi_id_mask & (1<m_enabled) { // The create command automatically changes directory to the created object switch(h->m_type) { case DEV_PROCESSOR: config_file.print("create control\r\n"); break; case DEV_DISK: config_file.print("create disk\r\n"); break; case DEV_OPTICAL: config_file.print("create optical\r\n"); break; case DEV_TAPE: config_file.print("create tape\r\n"); break; default: config_file.printf("create 0x%02x\r\n", h->m_type); break; } if(h->m_file) { config_file.printf("mount %s\r\n", h->m_filename); } if(h->m_blocksize) { config_file.printf("set blocksize %d\r\n", h->m_blocksize); } // The map command should be last, as it will escape back to /vdevs if((h->m_id < NUM_SCSIID) && (h->m_lun < NUM_SCSILUN)) { config_file.printf("map /tgts/tgt%d/lun%d\r\n", h->m_id, h->m_lun); } else { config_file.print("cd ..\r\n"); } config_file.print( "\r\n"); } } } } config_file.close(); } char helponSelfTest[] = "\r\n" "selftest\r\n" "\r\n" " Use the selftest command to check for miswired boards and shorts.\r\n" "\r\n" ; char helponSave[] = "\r\n" "saveconfig\r\n" "\r\n" " Generate a new greenscsi.cfg file containing currently configured parameters.\r\n" "Any existing greenscsi.cfg file will be overwritten and its previous contents lost.\r\n" "\r\n" ; char helponSet[] = "\r\n" "set \r\n" "\r\n" " Use the set command to configure vdev parameters.\r\n" "\r\n" ; char helponCreate[] = "\r\n" "create \r\n\r\n" " The create command allocates and changes the path to a newly created vdev.\r\n" "Recognized values for type:" #if SUPPORT_DISK " disk" #endif #if SUPPORT_OPTICAL " optical" #endif #if SUPPORT_TAPE " tape" #endif ".\r\n" "You can also specify the SCSI device code value directly.\r\n" "\r\n" ; char helponEcho[] = "\r\n" "echo [on|off|]\r\n" "\r\n" " The echo command allows you to turn on or off the echoing of script commands to the console.\r\n" "Additionally, you can use the echo command to relay a message to the console.\r\n" "\r\n" ; char helponCat[] = "\r\n" "cat \r\n" "\r\n" " The cat command dumps files to the console for reading.\r\n" "\r\n" ; char helponUnlink[] = "\r\n" "unlink \r\n" "\r\n" " The unlink command removes objects such as files or vdevs.\r\n" "\r\n" ; char helponExec[] = "\r\n" "exec