2 Samba Unix/Linux SMB client library
4 Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
5 Copyright (C) 2001 Remus Koos (remuskoos@yahoo.com)
6 Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "../utils/net.h"
28 int net_ads_usage(int argc, const char **argv)
31 "\nnet ads join <org_unit>"\
32 "\n\tjoins the local machine to a ADS realm\n"\
34 "\n\tremoves the local machine from a ADS realm\n"\
36 "\n\ttests that an exiting join is OK\n"\
38 "\n\tlist, add, or delete users in the realm\n"\
40 "\n\tlist, add, or delete groups in the realm\n"\
42 "\n\tshows some info on the server\n"\
44 "\n\tdump the machine account details to stdout\n"
46 "\n\tperform a CLDAP search on the server\n"
47 "\nnet ads password <username@realm> -Uadmin_username@realm%%admin_pass"\
48 "\n\tchange a user's password using an admin account"\
49 "\n\t(note: use realm in UPPERCASE)\n"\
50 "\nnet ads chostpass"\
51 "\n\tchange the trust account password of this machine in the AD tree\n"\
52 "\nnet ads printer [info | publish | remove] <printername> <servername>"\
53 "\n\t lookup, add, or remove directory entry for a printer\n"\
55 "\n\tperform a raw LDAP search and dump the results\n"
62 this implements the CLDAP based netlogon lookup requests
63 for finding the domain controller of a ADS domain
65 static int net_ads_lookup(int argc, const char **argv)
69 ads = ads_init(NULL, NULL, opt_host);
71 ads->auth.flags |= ADS_AUTH_NO_BIND;
76 if (!ads || !ads->config.realm) {
77 d_printf("Didn't find the cldap server!\n");
81 return ads_cldap_netlogon(ads);
86 static int net_ads_info(int argc, const char **argv)
90 ads = ads_init(NULL, NULL, opt_host);
93 ads->auth.flags |= ADS_AUTH_NO_BIND;
98 if (!ads || !ads->config.realm) {
99 d_printf("Didn't find the ldap server!\n");
103 d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip));
104 d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
105 d_printf("Realm: %s\n", ads->config.realm);
106 d_printf("Bind Path: %s\n", ads->config.bind_path);
107 d_printf("LDAP port: %d\n", ads->ldap_port);
108 d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
114 static ADS_STRUCT *ads_startup(void)
118 BOOL need_password = False;
119 BOOL second_time = False;
121 ads = ads_init(NULL, NULL, opt_host);
123 if (!opt_user_name) {
124 opt_user_name = "administrator";
127 if (opt_user_specified)
128 need_password = True;
131 if (!opt_password && need_password) {
133 asprintf(&prompt,"%s password: ", opt_user_name);
134 opt_password = getpass(prompt);
139 ads->auth.password = strdup(opt_password);
141 ads->auth.user_name = strdup(opt_user_name);
143 status = ads_connect(ads);
144 if (!ADS_ERR_OK(status)) {
145 if (!need_password && !second_time) {
146 need_password = True;
150 DEBUG(1,("ads_connect: %s\n", ads_errstr(status)));
159 Check to see if connection can be made via ads.
160 ads_startup() stores the password in opt_password if it needs to so
161 that rpc or rap can use it without re-prompting.
163 int net_ads_check(void)
175 determine the netbios workgroup name for a domain
177 static int net_ads_workgroup(int argc, const char **argv)
183 if (!(ads = ads_startup())) return -1;
185 if (!(ctx = talloc_init("net_ads_workgroup"))) {
189 if (!ADS_ERR_OK(ads_workgroup_name(ads, ctx, &workgroup))) {
190 d_printf("Failed to find workgroup for realm '%s'\n",
196 d_printf("Workgroup: %s\n", workgroup);
205 static BOOL usergrp_display(char *field, void **values, void *data_area)
207 char **disp_fields = (char **) data_area;
209 if (!field) { /* must be end of record */
210 if (!strchr_m(disp_fields[0], '$')) {
212 d_printf("%-21.21s %-50.50s\n",
213 disp_fields[0], disp_fields[1]);
215 d_printf("%s\n", disp_fields[0]);
217 SAFE_FREE(disp_fields[0]);
218 SAFE_FREE(disp_fields[1]);
221 if (!values) /* must be new field, indicate string field */
223 if (StrCaseCmp(field, "sAMAccountName") == 0) {
224 disp_fields[0] = strdup((char *) values[0]);
226 if (StrCaseCmp(field, "description") == 0)
227 disp_fields[1] = strdup((char *) values[0]);
231 static int net_ads_user_usage(int argc, const char **argv)
233 return net_help_user(argc, argv);
236 static int ads_user_add(int argc, const char **argv)
244 if (argc < 1) return net_ads_user_usage(argc, argv);
246 if (!(ads = ads_startup())) return -1;
248 status = ads_find_user_acct(ads, &res, argv[0]);
250 if (!ADS_ERR_OK(status)) {
251 d_printf("ads_user_add: %s\n", ads_errstr(status));
255 if (ads_count_replies(ads, res)) {
256 d_printf("ads_user_add: User %s already exists\n", argv[0]);
260 status = ads_add_user_acct(ads, argv[0], opt_container, opt_comment);
262 if (!ADS_ERR_OK(status)) {
263 d_printf("Could not add user %s: %s\n", argv[0],
268 /* if no password is to be set, we're done */
270 d_printf("User %s added\n", argv[0]);
275 /* try setting the password */
276 asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
277 status = krb5_set_password(ads->auth.kdc_server, upn, argv[1], ads->auth.time_offset);
279 if (ADS_ERR_OK(status)) {
280 d_printf("User %s added\n", argv[0]);
285 /* password didn't set, delete account */
286 d_printf("Could not add user %s. Error setting password %s\n",
287 argv[0], ads_errstr(status));
288 ads_msgfree(ads, res);
289 status=ads_find_user_acct(ads, &res, argv[0]);
290 if (ADS_ERR_OK(status)) {
291 userdn = ads_get_dn(ads, res);
292 ads_del_dn(ads, userdn);
293 ads_memfree(ads, userdn);
298 ads_msgfree(ads, res);
303 static int ads_user_info(int argc, const char **argv)
308 const char *attrs[] = {"memberOf", NULL};
309 char *searchstring=NULL;
311 char *escaped_user = escape_ldap_string_alloc(argv[0]);
313 if (argc < 1) return net_ads_user_usage(argc, argv);
315 if (!(ads = ads_startup())) return -1;
318 d_printf("ads_user_info: failed to escape user %s\n", argv[0]);
322 asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
323 rc = ads_search(ads, &res, searchstring, attrs);
324 safe_free(searchstring);
326 if (!ADS_ERR_OK(rc)) {
327 d_printf("ads_search: %s\n", ads_errstr(rc));
331 grouplist = ldap_get_values(ads->ld, res, "memberOf");
336 for (i=0;grouplist[i];i++) {
337 groupname = ldap_explode_dn(grouplist[i], 1);
338 d_printf("%s\n", groupname[0]);
339 ldap_value_free(groupname);
341 ldap_value_free(grouplist);
344 ads_msgfree(ads, res);
350 static int ads_user_delete(int argc, const char **argv)
357 if (argc < 1) return net_ads_user_usage(argc, argv);
359 if (!(ads = ads_startup())) return -1;
361 rc = ads_find_user_acct(ads, &res, argv[0]);
362 if (!ADS_ERR_OK(rc)) {
363 DEBUG(0, ("User %s does not exist\n", argv[0]));
366 userdn = ads_get_dn(ads, res);
367 ads_msgfree(ads, res);
368 rc = ads_del_dn(ads, userdn);
369 ads_memfree(ads, userdn);
370 if (!ADS_ERR_OK(rc)) {
371 d_printf("User %s deleted\n", argv[0]);
374 d_printf("Error deleting user %s: %s\n", argv[0],
379 int net_ads_user(int argc, const char **argv)
381 struct functable func[] = {
382 {"ADD", ads_user_add},
383 {"INFO", ads_user_info},
384 {"DELETE", ads_user_delete},
389 const char *shortattrs[] = {"sAMAccountName", NULL};
390 const char *longattrs[] = {"sAMAccountName", "description", NULL};
391 char *disp_fields[2] = {NULL, NULL};
394 if (!(ads = ads_startup())) return -1;
396 if (opt_long_list_entries)
397 d_printf("\nUser name Comment"\
398 "\n-----------------------------\n");
400 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
402 "(objectclass=user)",
403 opt_long_list_entries ? longattrs :
404 shortattrs, usergrp_display,
410 return net_run_function(argc, argv, func, net_ads_user_usage);
413 static int net_ads_group_usage(int argc, const char **argv)
415 return net_help_group(argc, argv);
418 static int ads_group_add(int argc, const char **argv)
425 if (argc < 1) return net_ads_group_usage(argc, argv);
427 if (!(ads = ads_startup())) return -1;
429 status = ads_find_user_acct(ads, &res, argv[0]);
431 if (!ADS_ERR_OK(status)) {
432 d_printf("ads_group_add: %s\n", ads_errstr(status));
436 if (ads_count_replies(ads, res)) {
437 d_printf("ads_group_add: Group %s already exists\n", argv[0]);
438 ads_msgfree(ads, res);
442 status = ads_add_group_acct(ads, argv[0], opt_container, opt_comment);
444 if (ADS_ERR_OK(status)) {
445 d_printf("Group %s added\n", argv[0]);
448 d_printf("Could not add group %s: %s\n", argv[0],
454 ads_msgfree(ads, res);
459 static int ads_group_delete(int argc, const char **argv)
466 if (argc < 1) return net_ads_group_usage(argc, argv);
468 if (!(ads = ads_startup())) return -1;
470 rc = ads_find_user_acct(ads, &res, argv[0]);
471 if (!ADS_ERR_OK(rc)) {
472 DEBUG(0, ("Group %s does not exist\n", argv[0]));
475 groupdn = ads_get_dn(ads, res);
476 ads_msgfree(ads, res);
477 rc = ads_del_dn(ads, groupdn);
478 ads_memfree(ads, groupdn);
479 if (!ADS_ERR_OK(rc)) {
480 d_printf("Group %s deleted\n", argv[0]);
483 d_printf("Error deleting group %s: %s\n", argv[0],
488 int net_ads_group(int argc, const char **argv)
490 struct functable func[] = {
491 {"ADD", ads_group_add},
492 {"DELETE", ads_group_delete},
497 const char *shortattrs[] = {"sAMAccountName", NULL};
498 const char *longattrs[] = {"sAMAccountName", "description", NULL};
499 char *disp_fields[2] = {NULL, NULL};
502 if (!(ads = ads_startup())) return -1;
504 if (opt_long_list_entries)
505 d_printf("\nGroup name Comment"\
506 "\n-----------------------------\n");
507 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
509 "(objectclass=group)",
510 opt_long_list_entries ? longattrs :
511 shortattrs, usergrp_display,
517 return net_run_function(argc, argv, func, net_ads_group_usage);
520 static int net_ads_status(int argc, const char **argv)
526 if (!(ads = ads_startup())) return -1;
528 rc = ads_find_machine_acct(ads, &res, global_myname());
529 if (!ADS_ERR_OK(rc)) {
530 d_printf("ads_find_machine_acct: %s\n", ads_errstr(rc));
534 if (ads_count_replies(ads, res) == 0) {
535 d_printf("No machine account for '%s' found\n", global_myname());
544 static int net_ads_leave(int argc, const char **argv)
546 ADS_STRUCT *ads = NULL;
549 if (!secrets_init()) {
550 DEBUG(1,("Failed to initialise secrets database\n"));
556 asprintf(&user_name, "%s$", global_myname());
557 opt_password = secrets_fetch_machine_password();
558 opt_user_name = user_name;
561 if (!(ads = ads_startup())) {
565 rc = ads_leave_realm(ads, global_myname());
566 if (!ADS_ERR_OK(rc)) {
567 d_printf("Failed to delete host '%s' from the '%s' realm.\n",
568 global_myname(), ads->config.realm);
572 d_printf("Removed '%s' from realm '%s'\n", global_myname(), ads->config.realm);
577 static int net_ads_join_ok(void)
580 ADS_STRUCT *ads = NULL;
582 if (!secrets_init()) {
583 DEBUG(1,("Failed to initialise secrets database\n"));
587 asprintf(&user_name, "%s$", global_myname());
588 opt_user_name = user_name;
589 opt_password = secrets_fetch_machine_password();
591 if (!(ads = ads_startup())) {
600 check that an existing join is OK
602 int net_ads_testjoin(int argc, const char **argv)
604 /* Display success or failure */
605 if (net_ads_join_ok() != 0) {
606 fprintf(stderr,"Join to domain is not valid\n");
610 printf("Join is OK\n");
615 join a domain using ADS
617 int net_ads_join(int argc, const char **argv)
623 const char *org_unit = "Computers";
629 if (argc > 0) org_unit = argv[0];
631 if (!secrets_init()) {
632 DEBUG(1,("Failed to initialise secrets database\n"));
636 tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
637 password = strdup(tmp_password);
639 if (!(ads = ads_startup())) return -1;
641 ou_str = ads_ou_string(org_unit);
642 asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path);
645 rc = ads_search_dn(ads, &res, dn, NULL);
646 ads_msgfree(ads, res);
648 if (rc.error_type == ADS_ERROR_LDAP && rc.err.rc == LDAP_NO_SUCH_OBJECT) {
649 d_printf("ads_join_realm: organizational unit %s does not exist (dn:%s)\n",
655 if (!ADS_ERR_OK(rc)) {
656 d_printf("ads_join_realm: %s\n", ads_errstr(rc));
660 rc = ads_join_realm(ads, global_myname(), org_unit);
661 if (!ADS_ERR_OK(rc)) {
662 d_printf("ads_join_realm: %s\n", ads_errstr(rc));
666 rc = ads_domain_sid(ads, &dom_sid);
667 if (!ADS_ERR_OK(rc)) {
668 d_printf("ads_domain_sid: %s\n", ads_errstr(rc));
672 rc = ads_set_machine_password(ads, global_myname(), password);
673 if (!ADS_ERR_OK(rc)) {
674 d_printf("ads_set_machine_password: %s\n", ads_errstr(rc));
678 if (!secrets_store_domain_sid(lp_workgroup(), &dom_sid)) {
679 DEBUG(1,("Failed to save domain sid\n"));
683 if (!secrets_store_machine_password(password)) {
684 DEBUG(1,("Failed to save machine password\n"));
688 d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->config.realm);
695 int net_ads_printer_usage(int argc, const char **argv)
698 "\nnet ads printer info <printer> <server>"
699 "\n\tlookup info in directory for printer on server"
700 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
701 "\nnet ads printer publish <printername>"
702 "\n\tpublish printer in directory"
703 "\n\t(note: printer name is required)\n"
704 "\nnet ads printer remove <printername>"
705 "\n\tremove printer from directory"
706 "\n\t(note: printer name is required)\n");
710 static int net_ads_printer_info(int argc, const char **argv)
714 const char *servername, *printername;
717 if (!(ads = ads_startup())) return -1;
720 printername = argv[0];
725 servername = argv[1];
727 servername = global_myname();
729 rc = ads_find_printer_on_server(ads, &res, printername, servername);
731 if (!ADS_ERR_OK(rc)) {
732 d_printf("ads_find_printer_on_server: %s\n", ads_errstr(rc));
733 ads_msgfree(ads, res);
737 if (ads_count_replies(ads, res) == 0) {
738 d_printf("Printer '%s' not found\n", printername);
739 ads_msgfree(ads, res);
744 ads_msgfree(ads, res);
749 void do_drv_upgrade_printer(int msg_type, pid_t src, void *buf, size_t len)
754 static int net_ads_printer_publish(int argc, const char **argv)
758 const char *servername;
759 struct cli_state *cli;
760 struct in_addr server_ip;
762 TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
763 ADS_MODLIST mods = ads_init_mods(mem_ctx);
764 char *prt_dn, *srv_dn, **srv_cn;
767 if (!(ads = ads_startup())) return -1;
770 return net_ads_printer_usage(argc, argv);
773 servername = argv[1];
775 servername = global_myname();
777 ads_find_machine_acct(ads, &res, servername);
778 srv_dn = ldap_get_dn(ads->ld, res);
779 srv_cn = ldap_explode_dn(srv_dn, 1);
780 asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn[0], argv[0], srv_dn);
782 resolve_name(servername, &server_ip, 0x20);
784 nt_status = cli_full_connection(&cli, global_myname(), servername,
787 opt_user_name, opt_workgroup,
788 opt_password ? opt_password : "",
789 CLI_FULL_CONNECTION_USE_KERBEROS,
792 cli_nt_session_open(cli, PI_SPOOLSS);
793 get_remote_printer_publishing_data(cli, mem_ctx, &mods, argv[0]);
795 rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
796 if (!ADS_ERR_OK(rc)) {
797 d_printf("ads_publish_printer: %s\n", ads_errstr(rc));
801 d_printf("published printer\n");
806 static int net_ads_printer_remove(int argc, const char **argv)
810 const char *servername;
814 if (!(ads = ads_startup())) return -1;
817 return net_ads_printer_usage(argc, argv);
820 servername = argv[1];
822 servername = global_myname();
824 rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
826 if (!ADS_ERR_OK(rc)) {
827 d_printf("ads_find_printer_on_server: %s\n", ads_errstr(rc));
828 ads_msgfree(ads, res);
832 if (ads_count_replies(ads, res) == 0) {
833 d_printf("Printer '%s' not found\n", argv[1]);
834 ads_msgfree(ads, res);
838 prt_dn = ads_get_dn(ads, res);
839 ads_msgfree(ads, res);
840 rc = ads_del_dn(ads, prt_dn);
841 ads_memfree(ads, prt_dn);
843 if (!ADS_ERR_OK(rc)) {
844 d_printf("ads_del_dn: %s\n", ads_errstr(rc));
851 static int net_ads_printer(int argc, const char **argv)
853 struct functable func[] = {
854 {"INFO", net_ads_printer_info},
855 {"PUBLISH", net_ads_printer_publish},
856 {"REMOVE", net_ads_printer_remove},
860 return net_run_function(argc, argv, func, net_ads_printer_usage);
864 static int net_ads_password(int argc, const char **argv)
867 const char *auth_principal = opt_user_name;
868 const char *auth_password = opt_password;
870 char *new_password = NULL;
876 if ((argc != 1) || (opt_user_name == NULL) ||
877 (opt_password == NULL) || (strchr(opt_user_name, '@') == NULL) ||
878 (strchr(argv[0], '@') == NULL)) {
879 return net_ads_usage(argc, argv);
882 c = strchr(auth_principal, '@');
885 /* use the realm so we can eventually change passwords for users
886 in realms other than default */
887 if (!(ads = ads_init(realm, NULL, NULL))) return -1;
889 asprintf(&prompt, "Enter new password for %s:", argv[0]);
891 new_password = getpass(prompt);
893 ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
894 auth_password, argv[0], new_password, ads->auth.time_offset);
895 if (!ADS_ERR_OK(ret)) {
896 d_printf("Password change failed :-( ...\n");
902 d_printf("Password change for %s completed.\n", argv[0]);
910 static int net_ads_change_localhost_pass(int argc, const char **argv)
913 char *host_principal;
918 if (!secrets_init()) {
919 DEBUG(1,("Failed to initialise secrets database\n"));
923 asprintf(&user_name, "%s$", global_myname());
924 opt_user_name = user_name;
926 opt_password = secrets_fetch_machine_password();
928 if (!(ads = ads_startup())) {
932 hostname = strdup(global_myname());
934 asprintf(&host_principal, "%s@%s", hostname, ads->config.realm);
936 d_printf("Changing password for principal: HOST/%s\n", host_principal);
938 ret = ads_change_trust_account_password(ads, host_principal);
940 if (!ADS_ERR_OK(ret)) {
941 d_printf("Password change failed :-( ...\n");
943 SAFE_FREE(host_principal);
947 d_printf("Password change for principal HOST/%s succeeded.\n", host_principal);
949 SAFE_FREE(host_principal);
955 help for net ads search
957 static int net_ads_search_usage(int argc, const char **argv)
960 "\nnet ads search <expression> <attributes...>\n"\
961 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
962 "The expression is a standard LDAP search expression, and the\n"\
963 "attributes are a list of LDAP fields to show in the results\n\n"\
964 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
966 net_common_flags_usage(argc, argv);
972 general ADS search function. Useful in diagnosing problems in ADS
974 static int net_ads_search(int argc, const char **argv)
983 return net_ads_search_usage(argc, argv);
986 if (!(ads = ads_startup())) {
993 rc = ads_do_search_all(ads, ads->config.bind_path,
996 if (!ADS_ERR_OK(rc)) {
997 d_printf("search failed: %s\n", ads_errstr(rc));
1001 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1003 /* dump the results */
1006 ads_msgfree(ads, res);
1013 int net_ads_help(int argc, const char **argv)
1015 struct functable func[] = {
1016 {"USER", net_ads_user_usage},
1017 {"GROUP", net_ads_group_usage},
1018 {"PRINTER", net_ads_printer_usage},
1019 {"SEARCH", net_ads_search_usage},
1021 {"INFO", net_ads_info},
1022 {"JOIN", net_ads_join},
1023 {"LEAVE", net_ads_leave},
1024 {"STATUS", net_ads_status},
1025 {"PASSWORD", net_ads_password},
1026 {"CHOSTPASS", net_ads_change_localhost_pass},
1031 return net_run_function(argc, argv, func, net_ads_usage);
1034 int net_ads(int argc, const char **argv)
1036 struct functable func[] = {
1037 {"INFO", net_ads_info},
1038 {"JOIN", net_ads_join},
1039 {"TESTJOIN", net_ads_testjoin},
1040 {"LEAVE", net_ads_leave},
1041 {"STATUS", net_ads_status},
1042 {"USER", net_ads_user},
1043 {"GROUP", net_ads_group},
1044 {"PASSWORD", net_ads_password},
1045 {"CHOSTPASS", net_ads_change_localhost_pass},
1046 {"PRINTER", net_ads_printer},
1047 {"SEARCH", net_ads_search},
1048 {"WORKGROUP", net_ads_workgroup},
1049 {"LOOKUP", net_ads_lookup},
1050 {"HELP", net_ads_help},
1054 return net_run_function(argc, argv, func, net_ads_usage);
1059 static int net_ads_noads(void)
1061 d_printf("ADS support not compiled in\n");
1065 int net_ads_usage(int argc, const char **argv)
1067 return net_ads_noads();
1070 int net_ads_help(int argc, const char **argv)
1072 return net_ads_noads();
1075 int net_ads_join(int argc, const char **argv)
1077 return net_ads_noads();
1080 int net_ads_user(int argc, const char **argv)
1082 return net_ads_noads();
1085 int net_ads_group(int argc, const char **argv)
1087 return net_ads_noads();
1090 /* this one shouldn't display a message */
1091 int net_ads_check(void)
1096 int net_ads(int argc, const char **argv)
1098 return net_ads_usage(argc, argv);