r24667: Finally merge the registry improvements that Wilco Baan Hofman and I have
[sfrench/samba-autobuild/.git] / source4 / lib / registry / tools / regshell.c
index 18399b5bda5fea64276e9ff84ce298f6833383e4..0887bc91f3d0304f3147a34d0c6ae1bc62ecab3d 100644 (file)
@@ -2,11 +2,11 @@
    Unix SMB/CIFS implementation.
    simple registry frontend
    
-   Copyright (C) Jelmer Vernooij 2004
+   Copyright (C) Jelmer Vernooij 2004-2007
 
    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
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
+#include "lib/registry/registry.h"
+#include "lib/cmdline/popt_common.h"
+#include "lib/events/events.h"
+#include "system/time.h"
+#include "lib/smbreadline/smbreadline.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "lib/registry/tools/common.h"
+
+struct regshell_context {
+       struct registry_context *registry;
+       const char *path;
+       struct registry_key *current;
+};
 
-/* 
+/* *
  * ck/cd - change key
  * ls - list values/keys
  * rmval/rm - remove value
  * rmkey/rmdir - remove key
  * mkkey/mkdir - make key
+ * ch - change hive
+ * info - show key info
+ * save - save hive
+ * print - print value
  * help
  * exit
  */
 
-static REG_KEY *cmd_set(REG_KEY *cur, int argc, char **argv)
+static WERROR cmd_info(struct regshell_context *ctx, int argc, char **argv)
 {
-       /* FIXME */
-       return NULL;
+       struct security_descriptor *sec_desc = NULL;
+       time_t last_mod;
+       WERROR error;
+       const char *classname;
+       NTTIME last_change;
+
+       error = reg_key_get_info(ctx, ctx->current, &classname, NULL, NULL, &last_change);
+       if (!W_ERROR_IS_OK(error)) {
+               printf("Error getting key info: %s\n", win_errstr(error));
+               return error;
+       }
+
+       
+       printf("Name: %s\n", strchr(ctx->path, '\\')?strrchr(ctx->path, '\\')+1: 
+                  ctx->path);
+       printf("Full path: %s\n", ctx->path);
+       printf("Key Class: %s\n", classname);
+       last_mod = nt_time_to_unix(last_change);
+       printf("Time Last Modified: %s\n", ctime(&last_mod));
+
+       error = reg_get_sec_desc(ctx, ctx->current, &sec_desc);
+       if (!W_ERROR_IS_OK(error)) {
+               printf("Error getting security descriptor\n");
+               return error;
+       } 
+       ndr_print_debug((ndr_print_fn_t)ndr_print_security_descriptor, "Security", sec_desc);
+       talloc_free(sec_desc);
+
+       return WERR_OK;
+}
+
+static WERROR cmd_predef(struct regshell_context *ctx, int argc, char **argv)
+{
+       struct registry_key *ret = NULL;
+       if (argc < 2) {
+               fprintf(stderr, "Usage: predef predefined-key-name\n");
+       } else if (!ctx) {
+               fprintf(stderr, "No full registry loaded, no predefined keys defined\n");
+       } else {
+               WERROR error = reg_get_predefined_key_by_name(ctx->registry, argv[1], &ret);
+
+               if (!W_ERROR_IS_OK(error)) {
+                       fprintf(stderr, "Error opening predefined key %s: %s\n", argv[1], win_errstr(error));
+                       return error;
+               }
+       }
+
+       return WERR_OK;
+}
+
+static WERROR cmd_pwd(struct regshell_context *ctx,
+                                                                       int argc, char **argv)
+{
+       printf("%s\n", ctx->path);
+       return WERR_OK;
+}
+
+static WERROR cmd_set(struct regshell_context *ctx, int argc, char **argv)
+{
+       struct registry_value val;
+       WERROR error;
+
+       if (argc < 4) {
+               fprintf(stderr, "Usage: set value-name type value\n");
+               return WERR_INVALID_PARAM;
+       } 
+
+       if (!reg_string_to_val(ctx, argv[2], argv[3], &val.data_type, 
+                                                  &val.data)) {
+               fprintf(stderr, "Unable to interpret data\n");
+               return WERR_INVALID_PARAM;
+       }
+
+       error = reg_val_set(ctx->current, argv[1], val.data_type, val.data);
+       if (!W_ERROR_IS_OK(error)) {
+               fprintf(stderr, "Error setting value: %s\n", win_errstr(error));
+               return error;
+       }
+
+       return WERR_OK;
 }
 
-static REG_KEY *cmd_ck(REG_KEY *cur, int argc, char **argv)
+static WERROR cmd_ck(struct regshell_context *ctx, int argc, char **argv)
 { 
-       REG_KEY *new = NULL;
+       struct registry_key *new = NULL;
        WERROR error;
+
        if(argc < 2) {
-               new = cur;
+               new = ctx->current;
        } else {
-               error = reg_open_key(cur, argv[1], &new);
+               error = reg_open_key(ctx->registry, ctx->current, argv[1], &new);
                if(!W_ERROR_IS_OK(error)) {
                        DEBUG(0, ("Error opening specified key: %s\n", win_errstr(error)));
-                       return NULL;
+                       return error;
                }
        } 
 
-       printf("Current path is: %s\n", reg_key_get_path(new));
+       ctx->path = talloc_asprintf(ctx, "%s\\%s", ctx->path, argv[1]);
+       printf("Current path is: %s\n", ctx->path);
+       ctx->current = new;
        
-       return new;
+       return WERR_OK;
 }
 
-static REG_KEY *cmd_ls(REG_KEY *cur, int argc, char **argv)
+static WERROR cmd_print(struct regshell_context *ctx, int argc, char **argv)
 {
-       int i, num;
+       uint32_t value_type;
+       DATA_BLOB value_data;
        WERROR error;
-       REG_VAL *value;
-       REG_KEY *sub;
-       for(i = 0; W_ERROR_IS_OK(error = reg_key_get_subkey_by_index(cur, i, &sub)); i++) {
-               printf("K %s\n", reg_key_name(sub));
+
+       if (argc != 2) {
+               fprintf(stderr, "Usage: print <valuename>");
+               return WERR_INVALID_PARAM;
        }
+       
+       error = reg_key_get_value_by_name(ctx, ctx->current, argv[1], 
+                                                                         &value_type, &value_data);
+       if (!W_ERROR_IS_OK(error)) {
+               fprintf(stderr, "No such value '%s'\n", argv[1]);
+               return error;
+       }
+
+       printf("%s\n%s\n", str_regtype(value_type), 
+                  reg_val_data_string(ctx, value_type, value_data));
+
+       return WERR_OK;
+}
 
-       if(!W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS)) {
-               DEBUG(0, ("Error occured while browsing thru keys\n"));
+static WERROR cmd_ls(struct regshell_context *ctx, int argc, char **argv)
+{
+       int i;
+       WERROR error;
+       struct registry_value *value;
+       uint32_t data_type;
+       DATA_BLOB data;
+       const char *name;
+
+       for (i = 0; W_ERROR_IS_OK(error = reg_key_get_subkey_by_index(ctx, ctx->current, i, &name, NULL, NULL)); i++) {
+               printf("K %s\n", name);
+       }
+
+       if (!W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS)) {
+               DEBUG(0, ("Error occured while browsing thru keys: %s\n", win_errstr(error)));
        }
 
-       for(i = 0; W_ERROR_IS_OK(error = reg_key_get_value_by_index(cur, i, &value)); i++) {
-               printf("V \"%s\" %s %s\n", reg_val_name(value), str_regtype(reg_val_type(value)), reg_val_data_string(value));
+       for (i = 0; W_ERROR_IS_OK(error = reg_key_get_value_by_index(ctx, ctx->current, i, &name, &data_type, &data)); i++) {
+               printf("V \"%s\" %s %s\n", value->name, str_regtype(data_type), 
+                          reg_val_data_string(ctx, data_type, data));
        }
        
-       return NULL
+       return WERR_OK
 }
-static REG_KEY *cmd_mkkey(REG_KEY *cur, int argc, char **argv)
+static WERROR cmd_mkkey(struct regshell_context *ctx, int argc, char **argv)
 { 
-       REG_KEY *tmp;
+       struct registry_key *tmp;
+       WERROR error;
+
        if(argc < 2) {
                fprintf(stderr, "Usage: mkkey <keyname>\n");
-               return NULL;
+               return WERR_INVALID_PARAM;
        }
+
+       error = reg_key_add_name(ctx, ctx->current, argv[1], 0, NULL, &tmp);
        
-       if(!W_ERROR_IS_OK(reg_key_add_name(cur, argv[1], 0, NULL, &tmp))) {
+       if (!W_ERROR_IS_OK(error)) {
                fprintf(stderr, "Error adding new subkey '%s'\n", argv[1]);
-               return NULL;
+               return error;
        }
 
-       fprintf(stderr, "Successfully added new subkey '%s' to '%s'\n", argv[1], reg_key_get_path(cur));
-       
-       return NULL; 
+       return WERR_OK; 
 }
 
-static REG_KEY *cmd_rmkey(REG_KEY *cur, int argc, char **argv)
+static WERROR cmd_rmkey(struct regshell_context *ctx,
+                                                                         int argc, char **argv)
 { 
-       REG_KEY *key;
+       WERROR error;
+
        if(argc < 2) {
                fprintf(stderr, "Usage: rmkey <name>\n");
-               return NULL;
-       }
-
-       if(!W_ERROR_IS_OK(reg_open_key(cur, argv[1], &key))) {
-               fprintf(stderr, "No such subkey '%s'\n", argv[1]);
-               return NULL;
+               return WERR_INVALID_PARAM;
        }
 
-       if(!W_ERROR_IS_OK(reg_key_del(key))) {
+       error = reg_key_del(ctx->current, argv[1]);
+       if(!W_ERROR_IS_OK(error)) {
                fprintf(stderr, "Error deleting '%s'\n", argv[1]);
+               return error;
        } else {
                fprintf(stderr, "Successfully deleted '%s'\n", argv[1]);
        }
        
-       return NULL; 
+       return WERR_OK;
 }
 
-static REG_KEY *cmd_rmval(REG_KEY *cur, int argc, char **argv)
+static WERROR cmd_rmval(struct regshell_context *ctx, int argc, char **argv)
 { 
-       REG_VAL *val;
+       WERROR error;
+
        if(argc < 2) {
                fprintf(stderr, "Usage: rmval <valuename>\n");
-               return NULL;
+               return WERR_INVALID_PARAM;
        }
 
-       if(!W_ERROR_IS_OK(reg_key_get_value_by_name(cur, argv[1], &val))) {
-               fprintf(stderr, "No such value '%s'\n", argv[1]);
-               return NULL;
-       }
-
-       if(!W_ERROR_IS_OK(reg_val_del(val))) {
+       error = reg_del_value(ctx->current, argv[1]);
+       if(!W_ERROR_IS_OK(error)) {
                fprintf(stderr, "Error deleting value '%s'\n", argv[1]);
+               return error;
        } else {
                fprintf(stderr, "Successfully deleted value '%s'\n", argv[1]);
        }
 
-       return NULL
+       return WERR_OK
 }
 
-static REG_KEY *cmd_exit(REG_KEY *cur, int argc, char **argv)
+static WERROR cmd_exit(struct regshell_context *ctx,
+                                                                        int argc, char **argv)
 {
        exit(0);
-       return NULL; 
+       return WERR_OK;
 }
 
-static REG_KEY *cmd_help(REG_KEY *, int, char **);
+static WERROR cmd_help(struct regshell_context *ctx, int, char **);
 
-struct {
+static struct {
        const char *name;
        const char *alias;
        const char *help;
-       REG_KEY *(*handle)(REG_KEY *, int argc, char **argv);
+       WERROR (*handle)(struct regshell_context *ctx,
+                                                                  int argc, char **argv);
 } regshell_cmds[] = {
        {"ck", "cd", "Change current key", cmd_ck },
+       {"info", "i", "Show detailed information of a key", cmd_info },
        {"list", "ls", "List values/keys in current key", cmd_ls },
+       {"print", "p", "Print value", cmd_print },
        {"mkkey", "mkdir", "Make new key", cmd_mkkey },
        {"rmval", "rm", "Remove value", cmd_rmval },
        {"rmkey", "rmdir", "Remove key", cmd_rmkey },
+       {"pwd", "pwk", "Printing current key", cmd_pwd },
        {"set", "update", "Update value", cmd_set },
        {"help", "?", "Help", cmd_help },
        {"exit", "quit", "Exit", cmd_exit },
+       {"predef", "predefined", "Go to predefined key", cmd_predef },
        {NULL }
 };
 
-static REG_KEY *cmd_help(REG_KEY *cur, int argc, char **argv)
+static WERROR cmd_help(struct regshell_context *ctx,
+                                                                        int argc, char **argv)
 {
        int i;
        printf("Available commands:\n");
        for(i = 0; regshell_cmds[i].name; i++) {
                printf("%s - %s\n", regshell_cmds[i].name, regshell_cmds[i].help);
        }
-       return NULL;
+       return WERR_OK;
 } 
 
-REG_KEY *process_cmd(REG_KEY *k, char *line)
+static WERROR process_cmd(struct regshell_context *ctx,
+                                                                               char *line)
 {
        int argc;
        char **argv = NULL;
@@ -181,72 +313,224 @@ REG_KEY *process_cmd(REG_KEY *k, char *line)
 
        if ((ret = poptParseArgvString(line, &argc, (const char ***) &argv)) != 0) {
                fprintf(stderr, "regshell: %s\n", poptStrerror(ret));
-               return k;
+               return WERR_INVALID_PARAM;
        }
 
        for(i = 0; regshell_cmds[i].name; i++) {
                if(!strcmp(regshell_cmds[i].name, argv[0]) || 
                   (regshell_cmds[i].alias && !strcmp(regshell_cmds[i].alias, argv[0]))) {
-                       return regshell_cmds[i].handle(k, argc, argv);
+                       return regshell_cmds[i].handle(ctx, argc, argv);
                }
        }
 
        fprintf(stderr, "No such command '%s'\n", argv[0]);
        
-       return k;
+       return WERR_INVALID_PARAM;
+}
+
+#define MAX_COMPLETIONS 100
+
+static struct registry_key *current_key = NULL;
+
+static char **reg_complete_command(const char *text, int start, int end)
+{
+       /* Complete command */
+       char **matches;
+       int i, len, samelen=0, count=1;
+
+       matches = malloc_array_p(char *, MAX_COMPLETIONS);
+       if (!matches) return NULL;
+       matches[0] = NULL;
+
+       len = strlen(text);
+       for (i=0;regshell_cmds[i].handle && count < MAX_COMPLETIONS-1;i++) {
+               if (strncmp(text, regshell_cmds[i].name, len) == 0) {
+                       matches[count] = strdup(regshell_cmds[i].name);
+                       if (!matches[count])
+                               goto cleanup;
+                       if (count == 1)
+                               samelen = strlen(matches[count]);
+                       else
+                               while (strncmp(matches[count], matches[count-1], samelen) != 0)
+                                       samelen--;
+                       count++;
+               }
+       }
+
+       switch (count) {
+       case 0: /* should never happen */
+       case 1:
+               goto cleanup;
+       case 2:
+               matches[0] = strdup(matches[1]);
+               break;
+       default:
+               matches[0] = strndup(matches[1], samelen);
+       }
+       matches[count] = NULL;
+       return matches;
+
+cleanup:
+       count--;
+       while (count >= 0) {
+               free(matches[count]);
+               count--;
+       }
+       free(matches);
+       return NULL;
+}
+
+static char **reg_complete_key(const char *text, int start, int end)
+{
+       struct registry_key *base;
+       const char *subkeyname;
+       int i, j = 1;
+       int samelen = 0;
+       int len;
+       char **matches;
+       const char *base_n = "";
+       TALLOC_CTX *mem_ctx;
+       WERROR status;
+
+       matches = malloc_array_p(char *, MAX_COMPLETIONS);
+       if (!matches) return NULL;
+       matches[0] = NULL;
+       mem_ctx = talloc_init("completion");
+
+       base = current_key;
+
+       len = strlen(text);
+       for(i = 0; j < MAX_COMPLETIONS-1; i++) {
+               status = reg_key_get_subkey_by_index(mem_ctx, base, i, &subkeyname, 
+                                                                                        NULL, NULL);
+               if(W_ERROR_IS_OK(status)) {
+                       if(!strncmp(text, subkeyname, len)) {
+                               matches[j] = strdup(subkeyname);
+                               j++;
+
+                               if (j == 1)
+                                       samelen = strlen(matches[j]);
+                               else
+                                       while (strncmp(matches[j], matches[j-1], samelen) != 0)
+                                               samelen--;
+                       }
+               } else if(W_ERROR_EQUAL(status, WERR_NO_MORE_ITEMS)) {
+                       break;
+               } else {
+                       printf("Error creating completion list: %s\n", win_errstr(status));
+                       talloc_free(mem_ctx);
+                       return NULL;
+               }
+       }
+
+       if (j == 1) { /* No matches at all */
+               SAFE_FREE(matches);
+               talloc_free(mem_ctx);
+               return NULL;
+       }
+
+       if (j == 2) { /* Exact match */
+               asprintf(&matches[0], "%s%s", base_n, matches[1]);
+       } else {
+               asprintf(&matches[0], "%s%s", base_n, 
+                               talloc_strndup(mem_ctx, matches[1], samelen));
+       }               
+       talloc_free(mem_ctx);
+
+       matches[j] = NULL;
+       return matches;
 }
 
-int main (int argc, char **argv)
+static char **reg_completion(const char *text, int start, int end)
+{
+       smb_readline_ca_char(' ');
+
+       if (start == 0) {
+               return reg_complete_command(text, start, end);
+       } else {
+               return reg_complete_key(text, start, end);
+       }
+}
+
+int main(int argc, char **argv)
 {
-       uint32  setparms, checkparms;
        int opt;
-       char *backend = "dir";
-       char *credentials = NULL;
-       REG_KEY *curkey = NULL;;
+       const char *file = NULL;
        poptContext pc;
-       WERROR error;
-       REG_HANDLE *h;
+       const char *remote = NULL;
+       struct regshell_context *ctx;
+       bool ret = true;
        struct poptOption long_options[] = {
                POPT_AUTOHELP
+               {"file", 'F', POPT_ARG_STRING, &file, 0, "open hive file", NULL },
+               {"remote", 'R', POPT_ARG_STRING, &remote, 0, "connect to specified remote server", NULL},
                POPT_COMMON_SAMBA
-               {"backend", 'b', POPT_ARG_STRING, &backend, 0, "backend to use", NULL},
-               {"credentials", 'c', POPT_ARG_STRING, &credentials, 0, "credentials", NULL},
-               POPT_TABLEEND
+               POPT_COMMON_CREDENTIALS
+               POPT_COMMON_VERSION
+               { NULL }
        };
-       
+
        pc = poptGetContext(argv[0], argc, (const char **) argv, long_options,0);
        
        while((opt = poptGetNextOpt(pc)) != -1) {
        }
 
-       error = reg_open(backend, poptPeekArg(pc), credentials, &h);
-       if(!W_ERROR_IS_OK(error)) {
-               fprintf(stderr, "Unable to open '%s' with backend '%s'\n", poptGetArg(pc), backend);
-               return 1;
-       }
-       poptFreeContext(pc);
+       ctx = talloc_zero(NULL, struct regshell_context);
 
-    setup_logging("regtree", True);
+       if (remote != NULL) {
+               ctx->registry = reg_common_open_remote(remote, cmdline_credentials);
+       } else if (file != NULL) {
+               ctx->current = reg_common_open_file(file, cmdline_credentials);
+               ctx->registry = ctx->current->context;
+               ctx->path = talloc_strdup(ctx, "");
+       } else {
+               ctx->registry = reg_common_open_local(cmdline_credentials);
+       }
 
-       error = reg_get_root(h, &curkey);
+       if (ctx->registry == NULL)
+               return 1;
 
-       if(!W_ERROR_IS_OK(error)) return 1;
+       if (ctx->current == NULL) {
+               int i;
+
+               for (i = 0; reg_predefined_keys[i].handle; i++) {
+                       WERROR err;
+                       err = reg_get_predefined_key(ctx->registry, 
+                                                                                reg_predefined_keys[i].handle, 
+                                                                                &ctx->current);
+                       if (W_ERROR_IS_OK(err)) {
+                               ctx->path = talloc_strdup(ctx, reg_predefined_keys[i].name);
+                               break;
+                       } else {
+                               ctx->current = NULL;
+                       }
+               }
+       }
 
-       while(True) {
+       if (ctx->current == NULL) {
+               fprintf(stderr, "Unable to access any of the predefined keys\n");
+               return -1;
+       }
+       
+       poptFreeContext(pc);
+       
+       while (true) {
                char *line, *prompt;
                
-               asprintf(&prompt, "%s> ", reg_key_get_path(curkey));
+               asprintf(&prompt, "%s> ", ctx->path);
                
-               line = smb_readline(prompt, NULL, NULL);
+               current_key = ctx->current;             /* No way to pass a void * pointer 
+                                                                                          via readline :-( */
+               line = smb_readline(prompt, NULL, reg_completion);
 
-               if(!line)
+               if (line == NULL)
                        break;
 
-               if(line[0] != '\n') {
-                       REG_KEY *new = process_cmd(curkey, line);
-                       if(new)curkey = new;
+               if (line[0] != '\n') {
+                       ret = W_ERROR_IS_OK(process_cmd(ctx, line));
                }
        }
+       talloc_free(ctx);
 
-       return 0;
+       return (ret?0:1);
 }