/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * restore.c: Restore the SPL VM state from a dump file * * WARNING: We assume that it is save to cast pointers to long and vice versa here. * This is true for all (most) UNIX systems, but the C standard doesn't require * long to have the same storage size as a pointer. */ #include #include #include #ifdef ENABLE_PTHREAD_SUPPORT #include #endif #include "spl.h" enum KEY_IDS { KEY_NONE, KEY_IDS, KEY_NODE, KEY_CODE, KEY_TASK, KEY_VM, KEY_CLS, KEY_CTX, KEY_CTX_TYPE, KEY_SHAONE, KEY_CODE_IP, KEY_CODE_TYPE, KEY_NIDX, KEY_SUBS, KEY_VALUE, KEY_HOSTED, KEY_HDATA, KEY_STACK, KEY_ROOT, KEY_SIZE, KEY_FLAGS, KEY_ID, KEY_MOD, KEY_NAME, KEY_END }; static struct { char *string; int id; } keywords[] = { { "IDS", KEY_IDS }, { "NODE", KEY_NODE }, { "CODE", KEY_CODE }, { "TASK", KEY_TASK }, { "VM", KEY_VM }, { "CLS", KEY_CLS }, { "CTX", KEY_CTX }, { "CTX_TYPE", KEY_CTX_TYPE }, { "SHAONE", KEY_SHAONE }, { "CODE_IP", KEY_CODE_IP }, { "CODE_TYPE", KEY_CODE_TYPE }, { "NIDX", KEY_NIDX }, { "SUBS", KEY_SUBS }, { "VALUE", KEY_VALUE }, { "HOSTED", KEY_HOSTED }, { "HDATA", KEY_HDATA }, { "STACK", KEY_STACK }, { "ROOT", KEY_ROOT }, { "SIZE", KEY_SIZE }, { "FLAGS", KEY_FLAGS }, { "ID", KEY_ID }, { "MOD", KEY_MOD }, { "NAME", KEY_NAME }, { "END", KEY_END }, { 0, 0 } }; static FILE *restore_file; static struct spl_code **code_map; static struct spl_node **node_map; #ifdef ENABLE_PTHREAD_SUPPORT static pthread_mutex_t restore_lck = PTHREAD_MUTEX_INITIALIZER; #endif static int read_key() { char buffer[10]; int ch = fgetc(restore_file); if (ch == EOF) return 0; if (ch != '\n' && ch != ' ') ungetc(ch, restore_file); for (int i=0; i<9; i++) { ch = fgetc(restore_file); if ( ch == EOF ) return 0; if ( (ch < 'A' || ch > 'Z') && ch != '_' ) { ungetc(ch, restore_file); buffer[i] = 0; break; } else buffer[i] = ch; } buffer[9] = 0; for (int i=0; keywords[i].string; i++) if (!strcmp(buffer, keywords[i].string)) return keywords[i].id; return -1; } static long read_num() { int ch = fgetc(restore_file); int value = 0; if (ch != '=' && ch != ',') return 0; while (1) { ch = fgetc(restore_file); if (ch == EOF) return 0; if ( ch < '0' || ch > '9' ) { ungetc(ch, restore_file); return value; } value = value*10 + (ch - '0'); } } static char *read_string() { int ch = fgetc(restore_file); if (ch == '(') ungetc(ch, restore_file); else if (ch != '=') { ungetc(ch, restore_file); return 0; } ch = fgetc(restore_file); if (ch != '(') return 0; char *value = malloc(1024); int size = 0, roof = 1024; while (1) { ch = fgetc(restore_file); if (ch == EOF) return 0; if ( ch == ')' ) { value[size] = 0; return realloc(value, size+1); } if ( ch == '%' ) { int c1 = fgetc(restore_file); int c2 = fgetc(restore_file); if ( c1 == EOF || c2 == EOF ) return 0; ch = 0; ch |= ( c1 >= '0' && c1 <= '9' ? c1 - '0' : (c1 - 'A') + 10 ) << 4; ch |= ( c2 >= '0' && c2 <= '9' ? c2 - '0' : (c2 - 'A') + 10 ); } value[size++] = ch; if ( size > roof-10 ) { roof += 1024; value = realloc(value, roof); } } } static unsigned char *read_data(void) { int ch = fgetc(restore_file); if (ch != '=') return 0; ch = fgetc(restore_file); if (ch != '{') return 0; char *value = malloc(1024); int size = 0, roof = 1024; while (1) { int c1 = fgetc(restore_file); if (c1 == EOF) return 0; if ( c1 == '}' ) { return realloc(value, size); } int c2 = fgetc(restore_file); if (c2 == EOF) return 0; ch = 0; ch |= ( c1 >= '0' && c1 <= '9' ? c1 - '0' : (c1 - 'A') + 10 ) << 4; ch |= ( c2 >= '0' && c2 <= '9' ? c2 - '0' : (c2 - 'A') + 10 ); value[size++] = ch; if ( size > roof-10 ) { roof += 1024; value = realloc(value, roof); } } } #define EXPECT(x) \ ({ if (!(x)) { spl_report(SPL_REPORT_RESTORE, vm, "Syntax error in tuple %d (%s:%d).\n", \ lineno, __FILE__, __LINE__); vm=0; goto restore_finished; } }) struct spl_vm *spl_restore_ascii(struct spl_vm *vm, FILE *file) { struct spl_task **last_taskp = &vm->task_list; int map_size = 0; int lineno = 0; #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_lock(&restore_lck); #endif restore_file = file; spl_put(vm, vm->root); vm->root = 0; while (1) { int key = read_key(); long id = read_num(); lineno++; switch ( key ) { case KEY_IDS: { EXPECT(read_key() == KEY_VALUE); map_size = read_num(); EXPECT(read_key() == KEY_VALUE); char *sig = read_string(); int sigcheck = !strcmp(sig, SPL_SIGNATURE); free(sig); EXPECT(sigcheck); code_map = calloc(map_size, sizeof(struct spl_code*)); node_map = calloc(map_size, sizeof(struct spl_node*)); break; } case KEY_CODE: { struct spl_code *code = calloc(1, sizeof(struct spl_code)); spl_state_counter_malloc_inc(); code_map[id] = code; EXPECT(read_key() == KEY_CODE_TYPE); code->code_type = read_num(); code->code_type = SPL_CODE_MALLOCED; EXPECT(read_key() == KEY_SIZE); code->size = read_num(); EXPECT(read_key() == KEY_ID); code->id = read_string(); EXPECT(read_key() == KEY_SHAONE); code->sha1 = read_string(); EXPECT(read_key() == KEY_CODE); code->code = read_data(); if (!code->code && code->sha1 && vm->codecache_dir) { int fn_size = strlen(vm->codecache_dir) + 100; char fn[fn_size]; snprintf(fn, fn_size, "%s/%s.splb", vm->codecache_dir, code->sha1); code->code_type = SPL_CODE_MAPPED; code->code = spl_mmap_file(fn, &code->size); EXPECT(code->code != NULL); } break; } case KEY_NODE: { struct spl_node *node = calloc(1, sizeof(struct spl_node)); spl_state_counter_malloc_inc(); node_map[id] = node; EXPECT(read_key() == KEY_FLAGS); node->flags = read_num() & ~SPL_NODE_FLAG_GC; EXPECT(read_key() == KEY_CLS); node->cls = (struct spl_node*)read_num(); EXPECT(read_key() == KEY_CTX); node->ctx = (struct spl_node*)read_num(); EXPECT(read_key() == KEY_CTX_TYPE); node->ctx_type = read_num(); EXPECT(read_key() == KEY_CODE); node->code = code_map[read_num()]; if ( node->code ) node->code->ref_counter++; EXPECT(read_key() == KEY_CODE_IP); node->code_ip = read_num(); EXPECT(read_key() == KEY_NIDX); node->subs_next_idx = read_num(); EXPECT(read_key() == KEY_SUBS); while ( (id = read_num()) ) { struct spl_node_sub *s = calloc(1, sizeof(struct spl_node_sub)); char ch = fgetc(restore_file); if ( ch == ':' ) s->module = read_string(); else ungetc(ch, restore_file); s->node = (struct spl_node*)id; s->key = read_string(); node->subs_counter++; if ( !node->subs_end ) { node->subs_begin = s; node->subs_end = s; } else { node->subs_end->next = s; s->last = node->subs_end; node->subs_end = s; } } EXPECT(read_key() == KEY_VALUE); char *str = read_string(); if (str) { node->value_string = spl_string_new(0, 0, 0, str, 0); node->value = SPL_VALUE_STRING; } EXPECT(read_key() == KEY_HOSTED); node->hnode_name = read_string(); EXPECT(read_key() == KEY_HDATA); node->hnode_dump = read_string(); break; } case KEY_TASK: { struct spl_task *task = calloc(1, sizeof(struct spl_task)); spl_state_counter_malloc_inc(); EXPECT(read_key() == KEY_CTX); task->ctx = node_map[read_num()]; if ( task->ctx ) task->ctx->ref_counter++; EXPECT(read_key() == KEY_CODE); task->code = code_map[read_num()]; if ( task->code ) task->code->ref_counter++; EXPECT(read_key() == KEY_CODE_IP); task->code_ip = read_num(); EXPECT(read_key() == KEY_FLAGS); task->flags = read_num(); EXPECT(read_key() == KEY_ID); task->id = read_string(); EXPECT(read_key() == KEY_STACK); struct spl_node_stack **l = &task->stack; while ( (id = read_num()) ) { struct spl_node_stack *s = spl_vm_malloc_node_stack_element(vm); s->next = 0; s->node = node_map[id]; if ( s->node ) s->node->ref_counter++; *l = s; l = &s->next; } task->vm = vm; *last_taskp = task; last_taskp = &task->next; break; } case KEY_VM: { EXPECT(read_key() == KEY_ROOT); vm->root = node_map[read_num()]; if ( vm->root ) vm->root->ref_counter++; free(code_map); goto reference_nodes; } default: EXPECT(0); } } reference_nodes: for (int i=0; ictx = node_map[(long)node_map[i]->ctx]; if ( node_map[i]->ctx ) node_map[i]->ctx->ref_counter++; node_map[i]->cls = node_map[(long)node_map[i]->cls]; if ( node_map[i]->cls ) node_map[i]->cls->ref_counter++; struct spl_node_sub *s = node_map[i]->subs_begin; while (s) { s->node = node_map[(long)s->node]; if ( s->node ) s->node->ref_counter++; s = s->next; } } free(node_map); while (1) { int key = read_key(); read_num(); lineno++; switch ( key ) { case KEY_MOD: { EXPECT(read_key() == KEY_NAME); char *name = read_string(); EXPECT(name != 0); fflush(stdout); spl_module_load(vm, name, 1); free(name); break; } case KEY_END: goto restore_finished; default: EXPECT(0); } } restore_finished: #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&restore_lck); #endif return vm; }