s3-rpc_client: Move client pipe functions to own header.
[samba.git] / source3 / client / client.c
index e11e3bf930bd1cc30637577542f55343daf8ba84..c7dfaa1cb470020c86f72451caee8900f8de8da4 100644 (file)
 
 #include "includes.h"
 #include "popt_common.h"
+#include "rpc_client/cli_pipe.h"
 #include "client/client_proto.h"
-#include "../librpc/gen_ndr/cli_srvsvc.h"
+#include "../librpc/gen_ndr/ndr_srvsvc_c.h"
+#include "../lib/util/select.h"
+#include "system/readline.h"
+#include "../libcli/smbreadline/smbreadline.h"
+#include "../libcli/security/security.h"
+#include "system/select.h"
 
 #ifndef REGISTER
 #define REGISTER 0
@@ -32,7 +38,6 @@
 
 extern int do_smb_browse(void); /* mDNS browsing */
 
-extern bool AllowDebugChange;
 extern bool override_logfile;
 extern char tar_type;
 
@@ -523,14 +528,15 @@ static bool do_this_one(struct file_info *finfo)
  Display info about a file.
 ****************************************************************************/
 
-static void display_finfo(struct cli_state *cli_state, struct file_info *finfo,
+static NTSTATUS display_finfo(struct cli_state *cli_state, struct file_info *finfo,
                          const char *dir)
 {
        time_t t;
        TALLOC_CTX *ctx = talloc_tos();
+       NTSTATUS status = NT_STATUS_OK;
 
        if (!do_this_one(finfo)) {
-               return;
+               return NT_STATUS_OK;
        }
 
        t = finfo->mtime_ts.tv_sec; /* the time is assumed to be passed as GMT */
@@ -544,11 +550,10 @@ static void display_finfo(struct cli_state *cli_state, struct file_info *finfo,
        } else {
                char *afname = NULL;
                uint16_t fnum;
-               NTSTATUS status;
 
                /* skip if this is . or .. */
                if ( strequal(finfo->name,"..") || strequal(finfo->name,".") )
-                       return;
+                       return NT_STATUS_OK;
                /* create absolute filename for cli_ntcreate() FIXME */
                afname = talloc_asprintf(ctx,
                                        "%s%s%s",
@@ -556,7 +561,7 @@ static void display_finfo(struct cli_state *cli_state, struct file_info *finfo,
                                        CLI_DIRSEP_STR,
                                        finfo->name);
                if (!afname) {
-                       return;
+                       return NT_STATUS_NO_MEMORY;
                }
                /* print file meta date header */
                d_printf( "FILENAME:%s\n", finfo->name);
@@ -577,6 +582,7 @@ static void display_finfo(struct cli_state *cli_state, struct file_info *finfo,
                                DEBUG( 0, ("display_finfo() failed to "
                                        "get security descriptor: %s",
                                        cli_errstr(cli_state)));
+                               status = cli_nt_error(cli_state);
                        } else {
                                display_sec_desc(sd);
                        }
@@ -584,18 +590,20 @@ static void display_finfo(struct cli_state *cli_state, struct file_info *finfo,
                }
                TALLOC_FREE(afname);
        }
+       return status;
 }
 
 /****************************************************************************
  Accumulate size of a file.
 ****************************************************************************/
 
-static void do_du(struct cli_state *cli_state, struct file_info *finfo,
+static NTSTATUS do_du(struct cli_state *cli_state, struct file_info *finfo,
                  const char *dir)
 {
        if (do_this_one(finfo)) {
                dir_total += finfo->size;
        }
+       return NT_STATUS_OK;
 }
 
 static bool do_list_recurse;
@@ -604,7 +612,7 @@ static char *do_list_queue = 0;
 static long do_list_queue_size = 0;
 static long do_list_queue_start = 0;
 static long do_list_queue_end = 0;
-static void (*do_list_fn)(struct cli_state *cli_state, struct file_info *,
+static NTSTATUS (*do_list_fn)(struct cli_state *cli_state, struct file_info *,
                          const char *dir);
 
 /****************************************************************************
@@ -722,18 +730,19 @@ static int do_list_queue_empty(void)
  A helper for do_list.
 ****************************************************************************/
 
-static void do_list_helper(const char *mntpoint, struct file_info *f,
+static NTSTATUS do_list_helper(const char *mntpoint, struct file_info *f,
                           const char *mask, void *state)
 {
        struct cli_state *cli_state = (struct cli_state *)state;
        TALLOC_CTX *ctx = talloc_tos();
        char *dir = NULL;
        char *dir_end = NULL;
+       NTSTATUS status = NT_STATUS_OK;
 
        /* Work out the directory. */
        dir = talloc_strdup(ctx, mask);
        if (!dir) {
-               return;
+               return NT_STATUS_NO_MEMORY;
        }
        if ((dir_end = strrchr(dir, CLI_DIRSEP_CHAR)) != NULL) {
                *dir_end = '\0';
@@ -741,7 +750,10 @@ static void do_list_helper(const char *mntpoint, struct file_info *f,
 
        if (f->mode & aDIR) {
                if (do_list_dirs && do_this_one(f)) {
-                       do_list_fn(cli_state, f, dir);
+                       status = do_list_fn(cli_state, f, dir);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               return status;
+                       }
                }
                if (do_list_recurse &&
                    f->name &&
@@ -753,7 +765,7 @@ static void do_list_helper(const char *mntpoint, struct file_info *f,
                        if (!f->name[0]) {
                                d_printf("Empty dir name returned. Possible server misconfiguration.\n");
                                TALLOC_FREE(dir);
-                               return;
+                               return NT_STATUS_UNSUCCESSFUL;
                        }
 
                        mask2 = talloc_asprintf(ctx,
@@ -762,7 +774,7 @@ static void do_list_helper(const char *mntpoint, struct file_info *f,
                                        mask);
                        if (!mask2) {
                                TALLOC_FREE(dir);
-                               return;
+                               return NT_STATUS_NO_MEMORY;
                        }
                        p = strrchr_m(mask2,CLI_DIRSEP_CHAR);
                        if (p) {
@@ -776,28 +788,29 @@ static void do_list_helper(const char *mntpoint, struct file_info *f,
                                        CLI_DIRSEP_STR);
                        if (!mask2) {
                                TALLOC_FREE(dir);
-                               return;
+                               return NT_STATUS_NO_MEMORY;
                        }
                        add_to_do_list_queue(mask2);
                        TALLOC_FREE(mask2);
                }
                TALLOC_FREE(dir);
-               return;
+               return NT_STATUS_OK;
        }
 
        if (do_this_one(f)) {
-               do_list_fn(cli_state, f, dir);
+               status = do_list_fn(cli_state, f, dir);
        }
        TALLOC_FREE(dir);
+       return status;
 }
 
 /****************************************************************************
  A wrapper around cli_list that adds recursion.
 ****************************************************************************/
 
-void do_list(const char *mask,
+NTSTATUS do_list(const char *mask,
                        uint16 attribute,
-                       void (*fn)(struct cli_state *cli_state, struct file_info *,
+                       NTSTATUS (*fn)(struct cli_state *cli_state, struct file_info *,
                                   const char *dir),
                        bool rec,
                        bool dirs)
@@ -806,6 +819,8 @@ void do_list(const char *mask,
        TALLOC_CTX *ctx = talloc_tos();
        struct cli_state *targetcli = NULL;
        char *targetpath = NULL;
+       NTSTATUS ret_status = NT_STATUS_OK;
+       NTSTATUS status = NT_STATUS_OK;
 
        if (in_do_list && rec) {
                fprintf(stderr, "INTERNAL ERROR: do_list called recursively when the recursive flag is true\n");
@@ -833,7 +848,7 @@ void do_list(const char *mask,
                        char *head = talloc_strdup(ctx, do_list_queue_head());
 
                        if (!head) {
-                               return;
+                               return NT_STATUS_NO_MEMORY;
                        }
 
                        /* check for dfs */
@@ -844,8 +859,13 @@ void do_list(const char *mask,
                                continue;
                        }
 
-                       cli_list(targetcli, targetpath, attribute,
+                       status = cli_list(targetcli, targetpath, attribute,
                                 do_list_helper, targetcli);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               d_printf("%s listing %s\n",
+                                        nt_errstr(status), targetpath);
+                               ret_status = status;
+                       }
                        remove_do_list_queue_head();
                        if ((! do_list_queue_empty()) && (fn == display_finfo)) {
                                char *next_file = do_list_queue_head();
@@ -873,22 +893,24 @@ void do_list(const char *mask,
        } else {
                /* check for dfs */
                if (cli_resolve_path(ctx, "", auth_info, cli, mask, &targetcli, &targetpath)) {
-                       NTSTATUS status;
 
                        status = cli_list(targetcli, targetpath, attribute,
                                          do_list_helper, targetcli);
                        if (!NT_STATUS_IS_OK(status)) {
                                d_printf("%s listing %s\n",
                                         nt_errstr(status), targetpath);
+                               ret_status = status;
                        }
                        TALLOC_FREE(targetpath);
                } else {
                        d_printf("do_list: [%s] %s\n", mask, cli_errstr(cli));
+                       ret_status = cli_nt_error(cli);
                }
        }
 
        in_do_list = 0;
        reset_do_list_queue();
+       return ret_status;
 }
 
 /****************************************************************************
@@ -902,6 +924,7 @@ static int cmd_dir(void)
        char *mask = NULL;
        char *buf = NULL;
        int rc = 1;
+       NTSTATUS status;
 
        dir_total = 0;
        mask = talloc_strdup(ctx, client_get_cur_dir());
@@ -928,7 +951,10 @@ static int cmd_dir(void)
                client_set_cwd(client_get_cur_dir());
        }
 
-       do_list(mask, attribute, display_finfo, recurse, true);
+       status = do_list(mask, attribute, display_finfo, recurse, true);
+       if (!NT_STATUS_IS_OK(status)) {
+               return 1;
+       }
 
        rc = do_dskattr();
 
@@ -947,6 +973,7 @@ static int cmd_du(void)
        uint16 attribute = aDIR | aSYSTEM | aHIDDEN;
        char *mask = NULL;
        char *buf = NULL;
+       NTSTATUS status;
        int rc = 1;
 
        dir_total = 0;
@@ -972,7 +999,10 @@ static int cmd_du(void)
                mask = talloc_strdup(ctx, "*");
        }
 
-       do_list(mask, attribute, do_du, recurse, true);
+       status = do_list(mask, attribute, do_du, recurse, true);
+       if (!NT_STATUS_IS_OK(status)) {
+               return 1;
+       }
 
        rc = do_dskattr();
 
@@ -1023,7 +1053,7 @@ static int do_get(const char *rname, const char *lname_in, bool reget)
        int handle = 0;
        uint16_t fnum;
        bool newhandle = false;
-       struct timeval tp_start;
+       struct timespec tp_start;
        uint16 attr;
        SMB_OFF_T size;
        off_t start = 0;
@@ -1048,7 +1078,7 @@ static int do_get(const char *rname, const char *lname_in, bool reget)
                return 1;
        }
 
-       GetTimeOfDay(&tp_start);
+       clock_gettime_mono(&tp_start);
 
        status = cli_open(targetcli, targetname, O_RDONLY, DENY_NONE, &fnum);
        if (!NT_STATUS_IS_OK(status)) {
@@ -1080,12 +1110,15 @@ static int do_get(const char *rname, const char *lname_in, bool reget)
        }
 
 
-       if (!cli_qfileinfo(targetcli, fnum,
-                          &attr, &size, NULL, NULL, NULL, NULL, NULL) &&
-           !NT_STATUS_IS_OK(cli_getattrE(targetcli, fnum,
-                         &attr, &size, NULL, NULL, NULL))) {
-               d_printf("getattrib: %s\n",cli_errstr(targetcli));
-               return 1;
+       status = cli_qfileinfo_basic(targetcli, fnum, &attr, &size, NULL, NULL,
+                                    NULL, NULL, NULL);
+       if (!NT_STATUS_IS_OK(status)) {
+               status = cli_getattrE(targetcli, fnum, &attr, &size, NULL, NULL,
+                                     NULL);
+               if(!NT_STATUS_IS_OK(status)) {
+                       d_printf("getattrib: %s\n", nt_errstr(status));
+                       return 1;
+               }
        }
 
        DEBUG(1,("getting file %s of size %.0f as %s ",
@@ -1115,13 +1148,11 @@ static int do_get(const char *rname, const char *lname_in, bool reget)
        }
 
        {
-               struct timeval tp_end;
+               struct timespec tp_end;
                int this_time;
 
-               GetTimeOfDay(&tp_end);
-               this_time =
-                       (tp_end.tv_sec - tp_start.tv_sec)*1000 +
-                       (tp_end.tv_usec - tp_start.tv_usec)/1000;
+               clock_gettime_mono(&tp_end);
+               this_time = nsec_time_diff(&tp_end,&tp_start)/1000000;
                get_total_time_ms += this_time;
                get_total_size += nread;
 
@@ -1175,10 +1206,11 @@ static int cmd_get(void)
  Do an mget operation on one file.
 ****************************************************************************/
 
-static void do_mget(struct cli_state *cli_state, struct file_info *finfo,
+static NTSTATUS do_mget(struct cli_state *cli_state, struct file_info *finfo,
                    const char *dir)
 {
        TALLOC_CTX *ctx = talloc_tos();
+       NTSTATUS status = NT_STATUS_OK;
        char *rname = NULL;
        char *quest = NULL;
        char *saved_curdir = NULL;
@@ -1186,32 +1218,32 @@ static void do_mget(struct cli_state *cli_state, struct file_info *finfo,
        char *new_cd = NULL;
 
        if (!finfo->name) {
-               return;
+               return NT_STATUS_OK;
        }
 
        if (strequal(finfo->name,".") || strequal(finfo->name,".."))
-               return;
+               return NT_STATUS_OK;
 
        if (abort_mget) {
                d_printf("mget aborted\n");
-               return;
+               return NT_STATUS_UNSUCCESSFUL;
        }
 
        if (finfo->mode & aDIR) {
                if (asprintf(&quest,
                         "Get directory %s? ",finfo->name) < 0) {
-                       return;
+                       return NT_STATUS_NO_MEMORY;
                }
        } else {
                if (asprintf(&quest,
                         "Get file %s? ",finfo->name) < 0) {
-                       return;
+                       return NT_STATUS_NO_MEMORY;
                }
        }
 
        if (prompt && !yesno(quest)) {
                SAFE_FREE(quest);
-               return;
+               return NT_STATUS_OK;
        }
        SAFE_FREE(quest);
 
@@ -1221,17 +1253,17 @@ static void do_mget(struct cli_state *cli_state, struct file_info *finfo,
                                client_get_cur_dir(),
                                finfo->name);
                if (!rname) {
-                       return;
+                       return NT_STATUS_NO_MEMORY;
                }
                do_get(rname, finfo->name, false);
                TALLOC_FREE(rname);
-               return;
+               return NT_STATUS_OK;
        }
 
        /* handle directories */
        saved_curdir = talloc_strdup(ctx, client_get_cur_dir());
        if (!saved_curdir) {
-               return;
+               return NT_STATUS_NO_MEMORY;
        }
 
        new_cd = talloc_asprintf(ctx,
@@ -1240,7 +1272,7 @@ static void do_mget(struct cli_state *cli_state, struct file_info *finfo,
                                finfo->name,
                                CLI_DIRSEP_STR);
        if (!new_cd) {
-               return;
+               return NT_STATUS_NO_MEMORY;
        }
        client_set_cur_dir(new_cd);
 
@@ -1253,13 +1285,13 @@ static void do_mget(struct cli_state *cli_state, struct file_info *finfo,
            mkdir(finfo->name,0777) != 0) {
                d_printf("failed to create directory %s\n",finfo->name);
                client_set_cur_dir(saved_curdir);
-               return;
+               return map_nt_error_from_unix(errno);
        }
 
        if (chdir(finfo->name) != 0) {
                d_printf("failed to chdir to directory %s\n",finfo->name);
                client_set_cur_dir(saved_curdir);
-               return;
+               return map_nt_error_from_unix(errno);
        }
 
        mget_mask = talloc_asprintf(ctx,
@@ -1267,18 +1299,24 @@ static void do_mget(struct cli_state *cli_state, struct file_info *finfo,
                        client_get_cur_dir());
 
        if (!mget_mask) {
-               return;
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       status = do_list(mget_mask, aSYSTEM | aHIDDEN | aDIR,do_mget,false, true);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
-       do_list(mget_mask, aSYSTEM | aHIDDEN | aDIR,do_mget,false, true);
        if (chdir("..") == -1) {
                d_printf("do_mget: failed to chdir to .. (error %s)\n",
                        strerror(errno) );
+               return map_nt_error_from_unix(errno);
        }
        client_set_cur_dir(saved_curdir);
        TALLOC_FREE(mget_mask);
        TALLOC_FREE(saved_curdir);
        TALLOC_FREE(new_cd);
+       return NT_STATUS_OK;
 }
 
 /****************************************************************************
@@ -1356,6 +1394,7 @@ static int cmd_mget(void)
        uint16 attribute = aSYSTEM | aHIDDEN;
        char *mget_mask = NULL;
        char *buf = NULL;
+       NTSTATUS status = NT_STATUS_OK;
 
        if (recurse) {
                attribute |= aDIR;
@@ -1364,6 +1403,7 @@ static int cmd_mget(void)
        abort_mget = false;
 
        while (next_token_talloc(ctx, &cmd_ptr,&buf,NULL)) {
+
                mget_mask = talloc_strdup(ctx, client_get_cur_dir());
                if (!mget_mask) {
                        return 1;
@@ -1377,7 +1417,10 @@ static int cmd_mget(void)
                if (!mget_mask) {
                        return 1;
                }
-               do_list(mget_mask, attribute, do_mget, false, true);
+               status = do_list(mget_mask, attribute, do_mget, false, true);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return 1;
+               }
        }
 
        if (mget_mask == NULL) {
@@ -1392,7 +1435,10 @@ static int cmd_mget(void)
                if (!mget_mask) {
                        return 1;
                }
-               do_list(mget_mask, attribute, do_mget, false, true);
+               status = do_list(mget_mask, attribute, do_mget, false, true);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return 1;
+               }
        }
 
        return 0;
@@ -1590,8 +1636,11 @@ static int do_allinfo(const char *name)
        uint16_t mode;
        SMB_INO_T ino;
        NTTIME tmp;
+       uint16_t fnum;
        unsigned int num_streams;
        struct stream_struct *streams;
+       int num_snapshots;
+       char **snapshots;
        unsigned int i;
        NTSTATUS status;
 
@@ -1638,6 +1687,49 @@ static int do_allinfo(const char *name)
                         (unsigned long long)streams[i].size);
        }
 
+       status = cli_open(cli, name, O_RDONLY, DENY_NONE, &fnum);
+       if (!NT_STATUS_IS_OK(status)) {
+               /*
+                * Ignore failure, it does not hurt if we can't list
+                * snapshots
+                */
+               return 0;
+       }
+       status = cli_shadow_copy_data(talloc_tos(), cli, fnum,
+                                     true, &snapshots, &num_snapshots);
+       if (!NT_STATUS_IS_OK(status)) {
+               cli_close(cli, fnum);
+               return 0;
+       }
+
+       for (i=0; i<num_snapshots; i++) {
+               char *snap_name;
+
+               d_printf("%s\n", snapshots[i]);
+               snap_name = talloc_asprintf(talloc_tos(), "%s%s",
+                                           snapshots[i], name);
+               status = cli_qpathinfo2(cli, snap_name, &b_time, &a_time,
+                                       &m_time, &c_time, &size,
+                                       NULL, NULL);
+               if (!NT_STATUS_IS_OK(status)) {
+                       d_fprintf(stderr, "pathinfo(%s) failed: %s\n",
+                                 snap_name, nt_errstr(status));
+                       TALLOC_FREE(snap_name);
+                       continue;
+               }
+               unix_timespec_to_nt_time(&tmp, b_time);
+               d_printf("create_time:    %s\n", nt_time_string(talloc_tos(), tmp));
+               unix_timespec_to_nt_time(&tmp, a_time);
+               d_printf("access_time:    %s\n", nt_time_string(talloc_tos(), tmp));
+               unix_timespec_to_nt_time(&tmp, m_time);
+               d_printf("write_time:     %s\n", nt_time_string(talloc_tos(), tmp));
+               unix_timespec_to_nt_time(&tmp, c_time);
+               d_printf("change_time:    %s\n", nt_time_string(talloc_tos(), tmp));
+               d_printf("size: %d\n", (int)size);
+       }
+
+       TALLOC_FREE(snapshots);
+
        return 0;
 }
 
@@ -1681,7 +1773,7 @@ static int do_put(const char *rname, const char *lname, bool reput)
        XFILE *f;
        SMB_OFF_T start = 0;
        int rc = 0;
-       struct timeval tp_start;
+       struct timespec tp_start;
        struct cli_state *targetcli;
        char *targetname = NULL;
        struct push_state state;
@@ -1692,12 +1784,15 @@ static int do_put(const char *rname, const char *lname, bool reput)
                return 1;
        }
 
-       GetTimeOfDay(&tp_start);
+       clock_gettime_mono(&tp_start);
 
        if (reput) {
                status = cli_open(targetcli, targetname, O_RDWR|O_CREAT, DENY_NONE, &fnum);
                if (NT_STATUS_IS_OK(status)) {
-                       if (!cli_qfileinfo(targetcli, fnum, NULL, &start, NULL, NULL, NULL, NULL, NULL) &&
+                       if (!NT_STATUS_IS_OK(cli_qfileinfo_basic(
+                                                    targetcli, fnum, NULL,
+                                                    &start, NULL, NULL,
+                                                    NULL, NULL, NULL)) &&
                            !NT_STATUS_IS_OK(cli_getattrE(targetcli, fnum, NULL, &start, NULL, NULL, NULL))) {
                                d_printf("getattrib: %s\n",cli_errstr(cli));
                                return 1;
@@ -1766,13 +1861,11 @@ static int do_put(const char *rname, const char *lname, bool reput)
        }
 
        {
-               struct timeval tp_end;
+               struct timespec tp_end;
                int this_time;
 
-               GetTimeOfDay(&tp_end);
-               this_time =
-                       (tp_end.tv_sec - tp_start.tv_sec)*1000 +
-                       (tp_end.tv_usec - tp_start.tv_usec)/1000;
+               clock_gettime_mono(&tp_end);
+               this_time = nsec_time_diff(&tp_end,&tp_start)/1000000;
                put_total_time_ms += this_time;
                put_total_size += state.nread;
 
@@ -2161,7 +2254,7 @@ static int cmd_queue(void)
  Delete some files.
 ****************************************************************************/
 
-static void do_del(struct cli_state *cli_state, struct file_info *finfo,
+static NTSTATUS do_del(struct cli_state *cli_state, struct file_info *finfo,
                   const char *dir)
 {
        TALLOC_CTX *ctx = talloc_tos();
@@ -2174,12 +2267,12 @@ static void do_del(struct cli_state *cli_state, struct file_info *finfo,
                                CLI_DIRSEP_CHAR,
                                finfo->name);
        if (!mask) {
-               return;
+               return NT_STATUS_NO_MEMORY;
        }
 
        if (finfo->mode & aDIR) {
                TALLOC_FREE(mask);
-               return;
+               return NT_STATUS_OK;
        }
 
        status = cli_unlink(cli_state, mask, aSYSTEM | aHIDDEN);
@@ -2188,6 +2281,7 @@ static void do_del(struct cli_state *cli_state, struct file_info *finfo,
                         nt_errstr(status), mask);
        }
        TALLOC_FREE(mask);
+       return status;
 }
 
 /****************************************************************************
@@ -2199,6 +2293,7 @@ static int cmd_del(void)
        TALLOC_CTX *ctx = talloc_tos();
        char *mask = NULL;
        char *buf = NULL;
+       NTSTATUS status = NT_STATUS_OK;
        uint16 attribute = aSYSTEM | aHIDDEN;
 
        if (recurse) {
@@ -2218,7 +2313,10 @@ static int cmd_del(void)
                return 1;
        }
 
-       do_list(mask,attribute,do_del,false,false);
+       status = do_list(mask,attribute,do_del,false,false);
+       if (!NT_STATUS_IS_OK(status)) {
+               return 1;
+       }
        return 0;
 }
 
@@ -2869,21 +2967,16 @@ static int cmd_symlink(void)
        char *newname = NULL;
        char *buf = NULL;
        char *buf2 = NULL;
-       char *targetname = NULL;
-       struct cli_state *targetcli;
+       struct cli_state *newcli;
 
        if (!next_token_talloc(ctx, &cmd_ptr,&buf,NULL) ||
            !next_token_talloc(ctx, &cmd_ptr,&buf2,NULL)) {
                d_printf("symlink <oldname> <newname>\n");
                return 1;
        }
-       oldname = talloc_asprintf(ctx,
-                       "%s%s",
-                       client_get_cur_dir(),
-                       buf);
-       if (!oldname) {
-               return 1;
-       }
+       /* Oldname (link target) must be an untouched blob. */
+       oldname = buf;
+
        newname = talloc_asprintf(ctx,
                        "%s%s",
                        client_get_cur_dir(),
@@ -2892,19 +2985,20 @@ static int cmd_symlink(void)
                return 1;
        }
 
-       if (!cli_resolve_path(ctx, "", auth_info, cli, oldname, &targetcli, &targetname)) {
+       /* New name must be present in share namespace. */
+       if (!cli_resolve_path(ctx, "", auth_info, cli, newname, &newcli, &newname)) {
                d_printf("link %s: %s\n", oldname, cli_errstr(cli));
                return 1;
        }
 
-       if (!SERVER_HAS_UNIX_CIFS(targetcli)) {
+       if (!SERVER_HAS_UNIX_CIFS(newcli)) {
                d_printf("Server doesn't support UNIX CIFS calls.\n");
                return 1;
        }
 
-       if (!NT_STATUS_IS_OK(cli_posix_symlink(targetcli, targetname, newname))) {
+       if (!NT_STATUS_IS_OK(cli_posix_symlink(newcli, oldname, newname))) {
                d_printf("%s symlinking files (%s -> %s)\n",
-                       cli_errstr(targetcli), newname, targetname);
+                       cli_errstr(newcli), newname, newname);
                return 1;
        }
 
@@ -3231,6 +3325,110 @@ static int cmd_getfacl(void)
        return 0;
 }
 
+static void printf_cb(const char *buf, void *private_data)
+{
+       printf("%s", buf);
+}
+
+/****************************************************************************
+ Get the EA list of a file
+****************************************************************************/
+
+static int cmd_geteas(void)
+{
+       TALLOC_CTX *ctx = talloc_tos();
+       char *src = NULL;
+       char *name = NULL;
+       char *targetname = NULL;
+       struct cli_state *targetcli;
+       NTSTATUS status;
+       size_t i, num_eas;
+       struct ea_struct *eas;
+
+       if (!next_token_talloc(ctx, &cmd_ptr,&name,NULL)) {
+               d_printf("geteas filename\n");
+               return 1;
+       }
+       src = talloc_asprintf(ctx,
+                       "%s%s",
+                       client_get_cur_dir(),
+                       name);
+       if (!src) {
+               return 1;
+       }
+
+       if (!cli_resolve_path(ctx, "", auth_info, cli, src, &targetcli,
+                             &targetname)) {
+               d_printf("stat %s: %s\n", src, cli_errstr(cli));
+               return 1;
+       }
+
+       status = cli_get_ea_list_path(targetcli, targetname, talloc_tos(),
+                                     &num_eas, &eas);
+       if (!NT_STATUS_IS_OK(status)) {
+               d_printf("cli_get_ea_list_path: %s\n", nt_errstr(status));
+               return 1;
+       }
+
+       for (i=0; i<num_eas; i++) {
+               d_printf("%s (%d) =\n", eas[i].name, (int)eas[i].flags);
+               dump_data_cb(eas[i].value.data, eas[i].value.length, false,
+                            printf_cb, NULL);
+               d_printf("\n");
+       }
+
+       TALLOC_FREE(eas);
+
+       return 0;
+}
+
+/****************************************************************************
+ Set an EA of a file
+****************************************************************************/
+
+static int cmd_setea(void)
+{
+       TALLOC_CTX *ctx = talloc_tos();
+       char *src = NULL;
+       char *name = NULL;
+       char *eaname = NULL;
+       char *eavalue = NULL;
+       char *targetname = NULL;
+       struct cli_state *targetcli;
+       NTSTATUS status;
+
+       if (!next_token_talloc(ctx, &cmd_ptr, &name, NULL)
+           || !next_token_talloc(ctx, &cmd_ptr, &eaname, NULL)) {
+               d_printf("setea filename eaname value\n");
+               return 1;
+       }
+       if (!next_token_talloc(ctx, &cmd_ptr, &eavalue, NULL)) {
+               eavalue = talloc_strdup(ctx, "");
+       }
+       src = talloc_asprintf(ctx,
+                       "%s%s",
+                       client_get_cur_dir(),
+                       name);
+       if (!src) {
+               return 1;
+       }
+
+       if (!cli_resolve_path(ctx, "", auth_info, cli, src, &targetcli,
+                             &targetname)) {
+               d_printf("stat %s: %s\n", src, cli_errstr(cli));
+               return 1;
+       }
+
+       status =  cli_set_ea_path(targetcli, targetname, eaname, eavalue,
+                                 strlen(eavalue));
+       if (!NT_STATUS_IS_OK(status)) {
+               d_printf("set_ea %s: %s\n", src, nt_errstr(status));
+               return 1;
+       }
+
+       return 0;
+}
+
 /****************************************************************************
  UNIX stat.
 ****************************************************************************/
@@ -3779,6 +3977,7 @@ static bool browse_host_rpc(bool sort)
        uint32_t resume_handle = 0;
        uint32_t total_entries = 0;
        int i;
+       struct dcerpc_binding_handle *b;
 
        status = cli_rpc_pipe_open_noauth(cli, &ndr_table_srvsvc.syntax_id,
                                          &pipe_hnd);
@@ -3790,13 +3989,15 @@ static bool browse_host_rpc(bool sort)
                return false;
        }
 
+       b = pipe_hnd->binding_handle;
+
        ZERO_STRUCT(info_ctr);
        ZERO_STRUCT(ctr1);
 
        info_ctr.level = 1;
        info_ctr.ctr.ctr1 = &ctr1;
 
-       status = rpccli_srvsvc_NetShareEnumAll(pipe_hnd, frame,
+       status = dcerpc_srvsvc_NetShareEnumAll(b, frame,
                                              pipe_hnd->desthost,
                                              &info_ctr,
                                              0xffffffff,
@@ -3913,6 +4114,7 @@ static int cmd_logon(void)
 {
        TALLOC_CTX *ctx = talloc_tos();
        char *l_username, *l_password;
+       NTSTATUS nt_status;
 
        if (!next_token_talloc(ctx, &cmd_ptr,&l_username,NULL)) {
                d_printf("logon <username> [<password>]\n");
@@ -3929,11 +4131,12 @@ static int cmd_logon(void)
                return 1;
        }
 
-       if (!NT_STATUS_IS_OK(cli_session_setup(cli, l_username,
-                                              l_password, strlen(l_password),
-                                              l_password, strlen(l_password),
-                                              lp_workgroup()))) {
-               d_printf("session setup failed: %s\n", cli_errstr(cli));
+       nt_status = cli_session_setup(cli, l_username,
+                                     l_password, strlen(l_password),
+                                     l_password, strlen(l_password),
+                                     lp_workgroup());
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               d_printf("session setup failed: %s\n", nt_errstr(nt_status));
                return -1;
        }
 
@@ -4013,6 +4216,26 @@ int cmd_iosize(void)
        return 0;
 }
 
+/****************************************************************************
+history
+****************************************************************************/
+static int cmd_history(void)
+{
+#if defined(HAVE_LIBREADLINE) && defined(HAVE_HISTORY_LIST)
+       HIST_ENTRY **hlist;
+       int i;
+
+       hlist = history_list();
+
+       for (i = 0; hlist && hlist[i]; i++) {
+               DEBUG(0, ("%d: %s\n", i, hlist[i]->line));
+       }
+#else
+       DEBUG(0,("no history without readline support\n"));
+#endif
+
+       return 0;
+}
 
 /* Some constants for completing filename arguments */
 
@@ -4050,6 +4273,8 @@ static struct {
   {"exit",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}},
   {"get",cmd_get,"<remote name> [local name] get a file",{COMPL_REMOTE,COMPL_LOCAL}},
   {"getfacl",cmd_getfacl,"<file name> get the POSIX ACL on a file (UNIX extensions only)",{COMPL_REMOTE,COMPL_LOCAL}},
+  {"geteas", cmd_geteas, "<file name> get the EA list of a file",
+   {COMPL_REMOTE, COMPL_LOCAL}},
   {"hardlink",cmd_hardlink,"<src> <dest> create a Windows hard link",{COMPL_REMOTE,COMPL_REMOTE}},
   {"help",cmd_help,"[command] give help on a command",{COMPL_NONE,COMPL_NONE}},
   {"history",cmd_history,"displays the command history",{COMPL_NONE,COMPL_NONE}},
@@ -4090,6 +4315,8 @@ static struct {
   {"rm",cmd_del,"<mask> delete all matching files",{COMPL_REMOTE,COMPL_NONE}},
   {"rmdir",cmd_rmdir,"<directory> remove a directory",{COMPL_NONE,COMPL_NONE}},
   {"showacls",cmd_showacls,"toggle if ACLs are shown or not",{COMPL_NONE,COMPL_NONE}},  
+  {"setea", cmd_setea, "<file name> <eaname> <eaval> Set an EA of a file",
+   {COMPL_REMOTE, COMPL_LOCAL}},
   {"setmode",cmd_setmode,"filename <setmode string> change modes of file",{COMPL_REMOTE,COMPL_NONE}},
   {"stat",cmd_stat,"filename Do a UNIX extensions stat call on a file",{COMPL_REMOTE,COMPL_REMOTE}},
   {"symlink",cmd_symlink,"<oldname> <newname> create a UNIX symlink",{COMPL_REMOTE,COMPL_REMOTE}},
@@ -4236,7 +4463,7 @@ struct completion_remote {
        int len;
 };
 
-static void completion_remote_filter(const char *mnt,
+static NTSTATUS completion_remote_filter(const char *mnt,
                                struct file_info *f,
                                const char *mask,
                                void *state)
@@ -4244,13 +4471,13 @@ static void completion_remote_filter(const char *mnt,
        struct completion_remote *info = (struct completion_remote *)state;
 
        if (info->count >= MAX_COMPLETIONS - 1) {
-               return;
+               return NT_STATUS_OK;
        }
        if (strncmp(info->text, f->name, info->len) != 0) {
-               return;
+               return NT_STATUS_OK;
        }
        if (ISDOT(f->name) || ISDOTDOT(f->name)) {
-               return;
+               return NT_STATUS_OK;
        }
 
        if ((info->dirmask[0] == 0) && !(f->mode & aDIR))
@@ -4262,12 +4489,12 @@ static void completion_remote_filter(const char *mnt,
                tmp = talloc_strdup(ctx,info->dirmask);
                if (!tmp) {
                        TALLOC_FREE(ctx);
-                       return;
+                       return NT_STATUS_NO_MEMORY;
                }
                tmp = talloc_asprintf_append(tmp, "%s", f->name);
                if (!tmp) {
                        TALLOC_FREE(ctx);
-                       return;
+                       return NT_STATUS_NO_MEMORY;
                }
                if (f->mode & aDIR) {
                        tmp = talloc_asprintf_append(tmp, "%s",
@@ -4275,13 +4502,13 @@ static void completion_remote_filter(const char *mnt,
                }
                if (!tmp) {
                        TALLOC_FREE(ctx);
-                       return;
+                       return NT_STATUS_NO_MEMORY;
                }
                info->matches[info->count] = SMB_STRDUP(tmp);
                TALLOC_FREE(ctx);
        }
        if (info->matches[info->count] == NULL) {
-               return;
+               return NT_STATUS_OK;
        }
        if (f->mode & aDIR) {
                smb_readline_ca_char(0);
@@ -4296,6 +4523,7 @@ static void completion_remote_filter(const char *mnt,
                }
        }
        info->count++;
+       return NT_STATUS_OK;
 }
 
 static char **remote_completion(const char *text, int len)
@@ -4509,12 +4737,13 @@ static bool finished;
 
 static void readline_callback(void)
 {
-       fd_set fds;
-       struct timeval timeout;
        static time_t last_t;
+       struct timespec now;
        time_t t;
+       int ret, revents;
 
-       t = time(NULL);
+       clock_gettime_mono(&now);
+       t = now.tv_sec;
 
        if (t - last_t < 5)
                return;
@@ -4526,18 +4755,14 @@ static void readline_callback(void)
        if (cli->fd == -1)
                return;
 
-       FD_ZERO(&fds);
-       FD_SET(cli->fd,&fds);
-
-       timeout.tv_sec = 0;
-       timeout.tv_usec = 0;
-       sys_select_intr(cli->fd+1,&fds,NULL,NULL,&timeout);
-
        /* We deliberately use receive_smb_raw instead of
           client_receive_smb as we want to receive
           session keepalives and then drop them here.
        */
-       if (FD_ISSET(cli->fd,&fds)) {
+
+       ret = poll_intr_one_fd(cli->fd, POLLIN|POLLHUP, 0, &revents);
+
+       if ((ret > 0) && (revents & (POLLIN|POLLHUP|POLLERR))) {
                NTSTATUS status;
                size_t len;
 
@@ -4691,7 +4916,7 @@ static int process(const char *base_directory)
 static int do_host_query(const char *query_host)
 {
        cli = cli_cm_open(talloc_tos(), NULL,
-                       query_host, "IPC$", auth_info, true, smb_encrypt,
+                       have_ip ? dest_ss_str : query_host, "IPC$", auth_info, true, smb_encrypt,
                        max_protocol, port, name_type);
        if (!cli)
                return 1;
@@ -4717,7 +4942,8 @@ static int do_host_query(const char *query_host)
 
                cli_shutdown(cli);
                cli = cli_cm_open(talloc_tos(), NULL,
-                               query_host, "IPC$", auth_info, true, smb_encrypt,
+                               have_ip ? dest_ss_str : query_host, "IPC$",
+                               auth_info, true, smb_encrypt,
                                max_protocol, 139, name_type);
        }
 
@@ -4871,14 +5097,11 @@ static int do_message_op(struct user_auth_info *a_info)
        set_global_myname( "" );
 
         /* set default debug level to 1 regardless of what smb.conf sets */
-       setup_logging( "smbclient", true );
-       DEBUGLEVEL_CLASS[DBGC_ALL] = 1;
-       if ((dbf = x_fdup(x_stderr))) {
-               x_setbuf( dbf, NULL );
-       }
-
+       setup_logging( "smbclient", DEBUG_DEFAULT_STDERR );
        load_case_tables();
 
+       lp_set_cmdline("log level", "1");
+
        auth_info = user_auth_info_init(frame);
        if (auth_info == NULL) {
                exit(1);
@@ -4944,10 +5167,7 @@ static int do_message_op(struct user_auth_info *a_info)
                        }
                        break;
                case 'E':
-                       if (dbf) {
-                               x_fclose(dbf);
-                       }
-                       dbf = x_stderr;
+                       setup_logging("smbclient", DEBUG_STDERR );
                        display_set_stderr();
                        break;
 
@@ -5021,12 +5241,6 @@ static int do_message_op(struct user_auth_info *a_info)
                                               poptGetArg(pc));
        }
 
-       /*
-        * Don't load debug level from smb.conf. It should be
-        * set by cmdline arg or remain default (0)
-        */
-       AllowDebugChange = false;
-
        /* save the workgroup...
 
           FIXME!! do we need to do this for other options as well
@@ -5040,7 +5254,7 @@ static int do_message_op(struct user_auth_info *a_info)
        }
 
        if ( override_logfile )
-               setup_logging( lp_logfile(), false );
+               setup_logging( lp_logfile(), DEBUG_FILE );
 
        if (!lp_load(get_dyn_CONFIGFILE(),true,false,false,true)) {
                fprintf(stderr, "%s: Can't load %s - run testparm to debug it\n",