summaryrefslogtreecommitdiff
path: root/source/utils/net_rpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/utils/net_rpc.c')
-rw-r--r--source/utils/net_rpc.c735
1 files changed, 733 insertions, 2 deletions
diff --git a/source/utils/net_rpc.c b/source/utils/net_rpc.c
index e21f79df303..1a1d76b09a0 100644
--- a/source/utils/net_rpc.c
+++ b/source/utils/net_rpc.c
@@ -2351,6 +2351,7 @@ rpc_share_add_internals(const DOM_SID *domain_sid, const char *domain_name,
uint32 type=0; /* only allow disk shares to be added */
uint32 num_users=0, perms=0;
char *password=NULL; /* don't allow a share password */
+ uint32 level = 2;
path = strchr(sharename, '=');
if (!path)
@@ -2359,7 +2360,8 @@ rpc_share_add_internals(const DOM_SID *domain_sid, const char *domain_name,
result = cli_srvsvc_net_share_add(cli, mem_ctx, sharename, type,
opt_comment, perms, opt_maxusers,
- num_users, path, password);
+ num_users, path, password,
+ level, NULL);
return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
}
@@ -2492,6 +2494,486 @@ rpc_share_list_internals(const DOM_SID *domain_sid, const char *domain_name,
}
/**
+ * Migrate shares from a remote RPC server to the local RPC srever
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+static NTSTATUS
+rpc_share_migrate_shares_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ WERROR result;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ SRV_SHARE_INFO_CTR ctr_src;
+ ENUM_HND hnd;
+ uint32 type = 0; /* only allow disk shares to be added */
+ uint32 num_uses = 0, perms = 0, max_uses = 0;
+ char *password = NULL; /* don't allow a share password */
+ uint32 preferred_len = 0xffffffff, i;
+ BOOL got_dst_srvsvc_pipe = False;
+ struct cli_state *cli_dst = NULL;
+ uint32 level = 502; /* includes secdesc */
+ SEC_DESC *share_sd = NULL;
+
+ init_enum_hnd(&hnd, 0);
+
+ result = cli_srvsvc_net_share_enum(
+ cli, mem_ctx, level, &ctr_src, preferred_len, &hnd);
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ /* connect local PI_SRVSVC */
+ nt_status = connect_pipe(&cli_dst, PI_SRVSVC, &got_dst_srvsvc_pipe);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return nt_status;
+
+
+ for (i = 0; i < ctr_src.num_entries; i++) {
+
+ fstring netname = "", remark = "", path = "";
+ /* reset error-code */
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ rpcstr_pull_unistr2_fstring(
+ netname, &ctr_src.share.info502[i].info_502_str.uni_netname);
+ rpcstr_pull_unistr2_fstring(
+ remark, &ctr_src.share.info502[i].info_502_str.uni_remark);
+ rpcstr_pull_unistr2_fstring(
+ path, &ctr_src.share.info502[i].info_502_str.uni_path);
+ num_uses = ctr_src.share.info502[i].info_502.num_uses;
+ max_uses = ctr_src.share.info502[i].info_502.max_uses;
+ perms = ctr_src.share.info502[i].info_502.perms;
+
+
+ if (opt_acls)
+ share_sd = dup_sec_desc(
+ mem_ctx, ctr_src.share.info502[i].info_502_str.sd);
+
+ /* since we do not have NetShareGetInfo implemented in samba3 we
+ only can skip inside the enum-ctr_src */
+ if (argc == 1) {
+ char *one_share = talloc_strdup(mem_ctx, argv[0]);
+ if (!strequal(netname, one_share))
+ continue;
+ }
+
+ /* skip builtin shares */
+ /* FIXME: should print$ be added too ? */
+ if (strequal(netname,"IPC$") || strequal(netname,"ADMIN$") ||
+ strequal(netname,"global"))
+ continue;
+
+ /* only work with file-shares */
+ if (!cli_send_tconX(cli, netname, "A:", "", 0)) {
+ d_printf("skipping [%s]: not a file share.\n", netname);
+ continue;
+ }
+
+ if (!cli_tdis(cli))
+ goto done;
+
+
+ /* finallly add the share on the dst server
+ please note that samba currently does not allow to
+ add a share without existing directory */
+
+ printf("migrating: [%s], path: %s, comment: %s, %s share-ACLs\n",
+ netname, path, remark, opt_acls ? "including" : "without" );
+
+ if (opt_verbose && opt_acls)
+ display_sec_desc(share_sd);
+
+ result = cli_srvsvc_net_share_add(cli_dst, mem_ctx, netname, type,
+ remark, perms, max_uses,
+ num_uses, path, password,
+ level, share_sd);
+
+ if (W_ERROR_V(result) == W_ERROR_V(WERR_ALREADY_EXISTS)) {
+ printf(" [%s] does already exist\n", netname);
+ continue;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf("cannot add share: %s\n", dos_errstr(result));
+ goto done;
+ }
+
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+ if (got_dst_srvsvc_pipe) {
+ cli_nt_session_close(cli_dst);
+ cli_shutdown(cli_dst);
+ }
+
+ return nt_status;
+
+}
+
+/**
+ * Migrate shares from a rpc-server to another
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_share_migrate_shares(int argc, const char **argv)
+{
+
+ if (!opt_host) {
+ printf("no server to migrate\n");
+ return -1;
+ }
+
+ return run_rpc_command(NULL, PI_SRVSVC, 0,
+ rpc_share_migrate_shares_internals,
+ argc, argv);
+}
+
+typedef struct copy_clistate {
+ TALLOC_CTX *mem_ctx;
+ struct cli_state *cli_share_src;
+ struct cli_state *cli_share_dst;
+ const char *cwd;
+} copy_clistate;
+
+
+/**
+ * Copy a file/dir
+ *
+ * @param f file_info
+ * @param mask current search mask
+ * @param state arg-pointer
+ *
+ **/
+static void copy_fn(file_info *f, const char *mask, void *state)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct copy_clistate *local_state = (struct copy_clistate *)state;
+ fstring filename, new_mask, dir;
+
+ if (strequal(f->name, ".") || strequal(f->name, ".."))
+ return;
+
+ DEBUG(3,("got mask: %s, name: %s\n", mask, f->name));
+
+ /* DIRECTORY */
+ if (f->mode & aDIR) {
+
+ DEBUG(3,("got dir: %s\n", f->name));
+
+ fstrcpy(dir, local_state->cwd);
+ fstrcat(dir, "\\");
+ fstrcat(dir, f->name);
+
+ /* create that directory */
+ nt_status = net_copy_file(local_state->mem_ctx,
+ local_state->cli_share_src,
+ local_state->cli_share_dst,
+ dir, dir,
+ opt_acls? True : False,
+ opt_attrs? True : False,
+ opt_timestamps? True : False,
+ False);
+
+ if (!NT_STATUS_IS_OK(nt_status))
+ printf("could not copy dir %s: %s\n",
+ dir, nt_errstr(nt_status));
+
+ /* search below that directory */
+ fstrcpy(new_mask, dir);
+ fstrcat(new_mask, "\\*");
+
+ if (!sync_files(local_state->mem_ctx,
+ local_state->cli_share_src,
+ local_state->cli_share_dst,
+ new_mask, dir))
+
+ printf("could not sync files\n");
+
+ return;
+ }
+
+
+ /* FILE */
+ fstrcpy(filename, local_state->cwd);
+ fstrcat(filename, "\\");
+ fstrcat(filename, f->name);
+
+ DEBUG(3,("got file: %s\n", filename));
+
+ nt_status = net_copy_file(local_state->mem_ctx,
+ local_state->cli_share_src,
+ local_state->cli_share_dst,
+ filename, filename,
+ opt_acls? True : False,
+ opt_attrs? True : False,
+ opt_timestamps? True: False,
+ True);
+
+ if (!NT_STATUS_IS_OK(nt_status))
+ printf("could not copy file %s: %s\n",
+ filename, nt_errstr(nt_status));
+
+}
+
+/**
+ * sync files, can be called recursivly to list files
+ * and then call copy_fn for each file
+ *
+ * @param mem_ctx TALLOC_CTX
+ * @param cli_share_src a connected share on the originating server
+ * @param cli_share_dst a connected share on the destination server
+ * @param mask the current search mask
+ * @param cwd the current path
+ *
+ * @return Boolean result
+ **/
+BOOL sync_files(TALLOC_CTX *mem_ctx,
+ struct cli_state *cli_share_src,
+ struct cli_state *cli_share_dst,
+ pstring mask, fstring cwd)
+
+{
+
+ uint16 attribute = aSYSTEM | aHIDDEN | aDIR;
+ struct copy_clistate clistate;
+
+ clistate.mem_ctx = mem_ctx;
+ clistate.cli_share_src = cli_share_src;
+ clistate.cli_share_dst = cli_share_dst;
+ clistate.cwd = cwd;
+
+ DEBUG(3,("calling cli_list with mask: %s\n", mask));
+
+ if (cli_list(cli_share_src, mask, attribute, copy_fn, &clistate) == -1) {
+ d_printf("listing %s failed with error: %s\n",
+ mask, cli_errstr(cli_share_src));
+ return False;
+ }
+
+ return True;
+}
+
+
+/**
+ * Sync all files inside a remote share to another share (over smb)
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+static NTSTATUS
+rpc_share_migrate_files_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ WERROR result;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ SRV_SHARE_INFO_CTR ctr_src;
+ ENUM_HND hnd;
+ uint32 preferred_len = 0xffffffff, i;
+ uint32 level = 2;
+ struct cli_state *cli_share_src = NULL;
+ struct cli_state *cli_share_dst = NULL;
+ BOOL got_src_share = False;
+ BOOL got_dst_share = False;
+ pstring mask;
+ char *dst = NULL;
+
+ dst = strdup(opt_destination?opt_destination:"127.0.0.1");
+
+ init_enum_hnd(&hnd, 0);
+
+ result = cli_srvsvc_net_share_enum(
+ cli, mem_ctx, level, &ctr_src, preferred_len, &hnd);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ for (i = 0; i < ctr_src.num_entries; i++) {
+
+ fstring netname = "", remark = "", path = "";
+
+ rpcstr_pull_unistr2_fstring(
+ netname, &ctr_src.share.info2[i].info_2_str.uni_netname);
+ rpcstr_pull_unistr2_fstring(
+ remark, &ctr_src.share.info2[i].info_2_str.uni_remark);
+ rpcstr_pull_unistr2_fstring(
+ path, &ctr_src.share.info2[i].info_2_str.uni_path);
+
+ /* since we do not have NetShareGetInfo implemented in samba3 we
+ only can skip inside the enum-ctr_src */
+ if (argc == 1) {
+ char *one_share = talloc_strdup(mem_ctx, argv[0]);
+ if (!strequal(netname, one_share))
+ continue;
+ }
+
+ /* skip builtin and hidden shares
+ In particular, one might not want to mirror whole discs :) */
+ if (strequal(netname,"IPC$") || strequal(netname,"ADMIN$"))
+ continue;
+
+ if (strequal(netname, "print$") || netname[1] == '$') {
+ d_printf("skipping [%s]: builtin/hidden share\n", netname);
+ continue;
+ }
+
+ if (opt_exclude && in_list(netname, (char *)opt_exclude, False)) {
+ printf("excluding [%s]\n", netname);
+ continue;
+ }
+
+ /* only work with file-shares */
+ if (!cli_send_tconX(cli, netname, "A:", "", 0)) {
+ d_printf("skipping [%s]: not a file share.\n", netname);
+ continue;
+ }
+
+ if (!cli_tdis(cli))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ printf("syncing [%s] files and directories %s ACLs, %s DOS Attributes %s\n",
+ netname,
+ opt_acls ? "including" : "without",
+ opt_attrs ? "including" : "without",
+ opt_timestamps ? "(preserving timestamps)" : "");
+
+
+ /* open share source */
+ nt_status = connect_to_service(&cli_share_src, &cli->dest_ip,
+ cli->desthost, netname, "A:");
+ if (!NT_STATUS_IS_OK(nt_status))
+ goto done;
+
+ got_src_share = True;
+
+
+ /* open share destination */
+ nt_status = connect_to_service(&cli_share_dst, NULL,
+ dst, netname, "A:");
+ if (!NT_STATUS_IS_OK(nt_status))
+ goto done;
+
+ got_dst_share = True;
+
+
+ /* now call the filesync */
+ pstrcpy(mask, "\\*");
+
+ if (!sync_files(mem_ctx, cli_share_src, cli_share_dst, mask, NULL)) {
+ d_printf("could not sync files for share: %s\n", netname);
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+
+ if (got_src_share)
+ cli_shutdown(cli_share_src);
+
+ if (got_dst_share)
+ cli_shutdown(cli_share_dst);
+
+ return nt_status;
+
+}
+
+static int rpc_share_migrate_files(int argc, const char **argv)
+{
+
+ if (!opt_host) {
+ printf("no server to migrate\n");
+ return -1;
+ }
+
+ return run_rpc_command(NULL, PI_SRVSVC, 0,
+ rpc_share_migrate_files_internals,
+ argc, argv);
+}
+
+/**
+ * Migrate shares (including share-definitions, share-acls and files with acls/attrs)
+ * from one server to another
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ *
+ **/
+static int rpc_share_migrate_all(int argc, const char **argv)
+{
+ int ret;
+
+ if (!opt_host) {
+ printf("no server to migrate\n");
+ return -1;
+ }
+
+ ret = run_rpc_command(NULL, PI_SRVSVC, 0, rpc_share_migrate_shares_internals, argc, argv);
+ if (ret)
+ return ret;
+#if 0
+ ret = run_rpc_command(NULL, PI_SRVSVC, 0, rpc_share_migrate_shares_security_internals, argc, argv);
+ if (ret)
+ return ret;
+#endif
+ return run_rpc_command(NULL, PI_SRVSVC, 0, rpc_share_migrate_files_internals, argc, argv);
+}
+
+
+/**
+ * 'net rpc share migrate' entrypoint.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ **/
+static int rpc_share_migrate(int argc, const char **argv)
+{
+
+ struct functable func[] = {
+ {"all", rpc_share_migrate_all},
+ {"files", rpc_share_migrate_files},
+ {"help", rpc_share_usage},
+/* {"security", rpc_share_migrate_security},*/
+ {"shares", rpc_share_migrate_shares},
+ {NULL, NULL}
+ };
+
+ return net_run_function(argc, argv, func, rpc_share_usage);
+}
+
+/**
* 'net rpc share' entrypoint.
* @param argc Standard main() style argc
* @param argv Standard main() style argv. Initial components are already
@@ -2503,6 +2985,7 @@ int net_rpc_share(int argc, const char **argv)
struct functable func[] = {
{"add", rpc_share_add},
{"delete", rpc_share_delete},
+ {"migrate", rpc_share_migrate},
{NULL, NULL}
};
@@ -3570,6 +4053,252 @@ static int rpc_vampire(int argc, const char **argv) {
return run_rpc_command(NULL, PI_NETLOGON, NET_FLAGS_ANONYMOUS, rpc_vampire_internals,
argc, argv);
}
+
+/**
+ * Migrate everything from a print-server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ *
+ * The order is important !
+ * To successfully add drivers the print-queues have to exist !
+ * Applying ACLs should be the last step, because you're easily locked out
+ *
+ **/
+static int rpc_printer_migrate_all(int argc, const char **argv)
+{
+ int ret;
+
+ if (!opt_host) {
+ printf("no server to migrate\n");
+ return -1;
+ }
+
+ ret = run_rpc_command(NULL, PI_SPOOLSS, 0, rpc_printer_migrate_printers_internals, argc, argv);
+ if (ret)
+ return ret;
+
+ ret = run_rpc_command(NULL, PI_SPOOLSS, 0, rpc_printer_migrate_drivers_internals, argc, argv);
+ if (ret)
+ return ret;
+
+ ret = run_rpc_command(NULL, PI_SPOOLSS, 0, rpc_printer_migrate_forms_internals, argc, argv);
+ if (ret)
+ return ret;
+
+ ret = run_rpc_command(NULL, PI_SPOOLSS, 0, rpc_printer_migrate_settings_internals, argc, argv);
+ if (ret)
+ return ret;
+
+ return run_rpc_command(NULL, PI_SPOOLSS, 0, rpc_printer_migrate_security_internals, argc, argv);
+
+}
+
+/**
+ * Migrate print-drivers from a print-server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_printer_migrate_drivers(int argc, const char **argv)
+{
+ if (!opt_host) {
+ printf("no server to migrate\n");
+ return -1;
+ }
+
+ return run_rpc_command(NULL, PI_SPOOLSS, 0,
+ rpc_printer_migrate_drivers_internals,
+ argc, argv);
+}
+
+/**
+ * Migrate print-forms from a print-server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_printer_migrate_forms(int argc, const char **argv)
+{
+ if (!opt_host) {
+ printf("no server to migrate\n");
+ return -1;
+ }
+
+ return run_rpc_command(NULL, PI_SPOOLSS, 0,
+ rpc_printer_migrate_forms_internals,
+ argc, argv);
+}
+
+/**
+ * Migrate printers from a print-server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_printer_migrate_printers(int argc, const char **argv)
+{
+ if (!opt_host) {
+ printf("no server to migrate\n");
+ return -1;
+ }
+
+ return run_rpc_command(NULL, PI_SPOOLSS, 0,
+ rpc_printer_migrate_printers_internals,
+ argc, argv);
+}
+
+/**
+ * Migrate printer-ACLs from a print-server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_printer_migrate_security(int argc, const char **argv)
+{
+ if (!opt_host) {
+ printf("no server to migrate\n");
+ return -1;
+ }
+
+ return run_rpc_command(NULL, PI_SPOOLSS, 0,
+ rpc_printer_migrate_security_internals,
+ argc, argv);
+}
+
+/**
+ * Migrate printer-settings from a print-server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_printer_migrate_settings(int argc, const char **argv)
+{
+ if (!opt_host) {
+ printf("no server to migrate\n");
+ return -1;
+ }
+
+ return run_rpc_command(NULL, PI_SPOOLSS, 0,
+ rpc_printer_migrate_settings_internals,
+ argc, argv);
+}
+
+/**
+ * 'net rpc printer' entrypoint.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ **/
+
+int rpc_printer_migrate(int argc, const char **argv)
+{
+
+ /* ouch: when addriver and setdriver are called from within
+ rpc_printer_migrate_drivers_internals, the printer-queue already
+ *has* to exist */
+
+ struct functable func[] = {
+ {"all", rpc_printer_migrate_all},
+ {"drivers", rpc_printer_migrate_drivers},
+ {"forms", rpc_printer_migrate_forms},
+ {"help", rpc_printer_usage},
+ {"printers", rpc_printer_migrate_printers},
+ {"security", rpc_printer_migrate_security},
+ {"settings", rpc_printer_migrate_settings},
+ {NULL, NULL}
+ };
+
+ return net_run_function(argc, argv, func, rpc_printer_usage);
+}
+
+
+/**
+ * List printers on a remote RPC server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_printer_list(int argc, const char **argv)
+{
+
+ return run_rpc_command(NULL, PI_SPOOLSS, 0,
+ rpc_printer_list_internals,
+ argc, argv);
+}
+
+/**
+ * List printer-drivers on a remote RPC server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_printer_driver_list(int argc, const char **argv)
+{
+
+ return run_rpc_command(NULL, PI_SPOOLSS, 0,
+ rpc_printer_driver_list_internals,
+ argc, argv);
+}
+
+/**
+ * Display rpc printer help page.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ **/
+int rpc_printer_usage(int argc, const char **argv)
+{
+ return net_help_printer(argc, argv);
+}
+
+/**
+ * 'net rpc printer' entrypoint.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ **/
+int net_rpc_printer(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"list", rpc_printer_list},
+ {"migrate", rpc_printer_migrate},
+ {"driver", rpc_printer_driver_list},
+ {NULL, NULL}
+ };
+
+ if (argc == 0)
+ return run_rpc_command(NULL, PI_SPOOLSS, 0,
+ rpc_printer_list_internals,
+ argc, argv);
+
+ return net_run_function(argc, argv, func, rpc_printer_usage);
+}
+
/****************************************************************************/
@@ -3589,7 +4318,8 @@ int net_rpc_usage(int argc, const char **argv)
d_printf(" net rpc user \t\t\tto add, delete and list users\n");
d_printf(" net rpc password <username> [<password>] -Uadmin_username%%admin_pass");
d_printf(" net rpc group \t\tto list groups\n");
- d_printf(" net rpc share \t\tto add, delete, and list shares\n");
+ d_printf(" net rpc share \t\tto add, delete, list and migrate shares\n");
+ d_printf(" net rpc printer \t\tto list and migrate printers\n");
d_printf(" net rpc file \t\t\tto list open files\n");
d_printf(" net rpc changetrustpw \tto change the trust account password\n");
d_printf(" net rpc getsid \t\tfetch the domain sid into the local secrets.tdb\n");
@@ -3659,6 +4389,7 @@ int net_rpc(int argc, const char **argv)
{"group", net_rpc_group},
{"share", net_rpc_share},
{"file", net_rpc_file},
+ {"printer", net_rpc_printer},
{"changetrustpw", net_rpc_changetrustpw},
{"trustdom", rpc_trustdom},
{"abortshutdown", rpc_shutdown_abort},