From 05e6ebb7f812eed95b8407e65cf438e04d6e3789 Mon Sep 17 00:00:00 2001 From: Günther Deschner Date: Thu, 21 May 2009 18:12:29 +0200 Subject: s4-smbtorture: re-work test_Create{User,Group,Alias} a little. Guenther --- source4/torture/rpc/samr.c | 52 +++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 14 deletions(-) (limited to 'source4') diff --git a/source4/torture/rpc/samr.c b/source4/torture/rpc/samr.c index 692dddf73b..ae0c8c032f 100644 --- a/source4/torture/rpc/samr.c +++ b/source4/torture/rpc/samr.c @@ -3999,9 +3999,11 @@ static bool test_DeleteAlias(struct dcerpc_pipe *p, } static bool test_CreateAlias(struct dcerpc_pipe *p, struct torture_context *tctx, - struct policy_handle *domain_handle, + struct policy_handle *domain_handle, + const char *alias_name, struct policy_handle *alias_handle, - const struct dom_sid *domain_sid) + const struct dom_sid *domain_sid, + bool test_alias) { NTSTATUS status; struct samr_CreateDomAlias r; @@ -4009,7 +4011,7 @@ static bool test_CreateAlias(struct dcerpc_pipe *p, struct torture_context *tctx uint32_t rid; bool ret = true; - init_lsa_String(&name, TEST_ALIASNAME); + init_lsa_String(&name, alias_name); r.in.domain_handle = domain_handle; r.in.alias_name = &name; r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; @@ -4043,6 +4045,10 @@ static bool test_CreateAlias(struct dcerpc_pipe *p, struct torture_context *tctx return false; } + if (!test_alias) { + return ret; + } + if (!test_alias_ops(p, tctx, alias_handle, domain_sid)) { ret = false; } @@ -4217,10 +4223,12 @@ static bool test_ChangePassword(struct dcerpc_pipe *p, static bool test_CreateUser(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *domain_handle, + const char *user_name, struct policy_handle *user_handle_out, struct dom_sid *domain_sid, enum torture_samr_choice which_ops, - struct cli_credentials *machine_credentials) + struct cli_credentials *machine_credentials, + bool test_user) { TALLOC_CTX *user_ctx; @@ -4239,7 +4247,7 @@ static bool test_CreateUser(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle user_handle; user_ctx = talloc_named(tctx, 0, "test_CreateUser2 per-user context"); - init_lsa_String(&name, TEST_ACCOUNT_NAME); + init_lsa_String(&name, user_name); r.in.domain_handle = domain_handle; r.in.account_name = &name; @@ -4269,11 +4277,21 @@ static bool test_CreateUser(struct dcerpc_pipe *p, struct torture_context *tctx, } status = dcerpc_samr_CreateUser(p, user_ctx, &r); } + if (!NT_STATUS_IS_OK(status)) { talloc_free(user_ctx); printf("CreateUser failed - %s\n", nt_errstr(status)); return false; - } else { + } + + if (!test_user) { + if (user_handle_out) { + *user_handle_out = user_handle; + } + return ret; + } + + { q.in.user_handle = &user_handle; q.in.level = 16; q.out.info = &info; @@ -5931,10 +5949,12 @@ static bool test_AddGroupMember(struct dcerpc_pipe *p, struct torture_context *t static bool test_CreateDomainGroup(struct dcerpc_pipe *p, - struct torture_context *tctx, + struct torture_context *tctx, struct policy_handle *domain_handle, + const char *group_name, struct policy_handle *group_handle, - struct dom_sid *domain_sid) + struct dom_sid *domain_sid, + bool test_group) { NTSTATUS status; struct samr_CreateDomainGroup r; @@ -5942,7 +5962,7 @@ static bool test_CreateDomainGroup(struct dcerpc_pipe *p, struct lsa_String name; bool ret = true; - init_lsa_String(&name, TEST_GROUPNAME); + init_lsa_String(&name, group_name); r.in.domain_handle = domain_handle; r.in.name = &name; @@ -5984,6 +6004,10 @@ static bool test_CreateDomainGroup(struct dcerpc_pipe *p, } torture_assert_ntstatus_ok(tctx, status, "CreateDomainGroup"); + if (!test_group) { + return ret; + } + if (!test_AddGroupMember(p, tctx, domain_handle, group_handle)) { printf("CreateDomainGroup failed - %s\n", nt_errstr(status)); ret = false; @@ -6060,7 +6084,7 @@ static bool test_OpenDomain(struct dcerpc_pipe *p, struct torture_context *tctx, if (!torture_setting_bool(tctx, "samba3", false)) { ret &= test_CreateUser2(p, tctx, &domain_handle, sid, which_ops, NULL); } - ret &= test_CreateUser(p, tctx, &domain_handle, &user_handle, sid, which_ops, NULL); + ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, which_ops, NULL, true); /* This test needs 'complex' users to validate */ ret &= test_QueryDisplayInfo(p, tctx, &domain_handle); if (!ret) { @@ -6071,13 +6095,13 @@ static bool test_OpenDomain(struct dcerpc_pipe *p, struct torture_context *tctx, if (!torture_setting_bool(tctx, "samba3", false)) { ret &= test_CreateUser2(p, tctx, &domain_handle, sid, which_ops, machine_credentials); } - ret &= test_CreateUser(p, tctx, &domain_handle, &user_handle, sid, which_ops, machine_credentials); + ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, which_ops, machine_credentials, true); if (!ret) { printf("Testing PASSWORDS PWDLASTSET on domain %s failed!\n", dom_sid_string(tctx, sid)); } break; case TORTURE_SAMR_OTHER: - ret &= test_CreateUser(p, tctx, &domain_handle, &user_handle, sid, which_ops, NULL); + ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, which_ops, NULL, true); if (!ret) { printf("Failed to CreateUser in SAMR-OTHER on domain %s!\n", dom_sid_string(tctx, sid)); } @@ -6085,8 +6109,8 @@ static bool test_OpenDomain(struct dcerpc_pipe *p, struct torture_context *tctx, ret &= test_QuerySecurity(p, tctx, &domain_handle); } ret &= test_RemoveMemberFromForeignDomain(p, tctx, &domain_handle); - ret &= test_CreateAlias(p, tctx, &domain_handle, &alias_handle, sid); - ret &= test_CreateDomainGroup(p, tctx, &domain_handle, &group_handle, sid); + ret &= test_CreateAlias(p, tctx, &domain_handle, TEST_ALIASNAME, &alias_handle, sid, true); + ret &= test_CreateDomainGroup(p, tctx, &domain_handle, TEST_GROUPNAME, &group_handle, sid, true); ret &= test_QueryDomainInfo(p, tctx, &domain_handle); ret &= test_QueryDomainInfo2(p, tctx, &domain_handle); ret &= test_EnumDomainUsers(p, tctx, &domain_handle); -- cgit From a75698bdf3b62d43e4909e5bfded70f6675b2058 Mon Sep 17 00:00:00 2001 From: Günther Deschner Date: Fri, 22 May 2009 19:04:25 +0200 Subject: s4-smbtorture: rename test_EnumDomain{Users,Groups,Aliases} in RPC-SAMR. Guenther --- source4/torture/rpc/samr.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'source4') diff --git a/source4/torture/rpc/samr.c b/source4/torture/rpc/samr.c index ae0c8c032f..3bc98b6315 100644 --- a/source4/torture/rpc/samr.c +++ b/source4/torture/rpc/samr.c @@ -4887,8 +4887,9 @@ static bool check_mask(struct dcerpc_pipe *p, struct torture_context *tctx, return ret; } -static bool test_EnumDomainUsers(struct dcerpc_pipe *p, struct torture_context *tctx, - struct policy_handle *handle) +static bool test_EnumDomainUsers_all(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle) { NTSTATUS status = STATUS_MORE_ENTRIES; struct samr_EnumDomainUsers r; @@ -5016,9 +5017,9 @@ static bool test_EnumDomainUsers_async(struct dcerpc_pipe *p, struct torture_con return true; } -static bool test_EnumDomainGroups(struct dcerpc_pipe *p, - struct torture_context *tctx, - struct policy_handle *handle) +static bool test_EnumDomainGroups_all(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle) { NTSTATUS status; struct samr_EnumDomainGroups r; @@ -5056,9 +5057,9 @@ static bool test_EnumDomainGroups(struct dcerpc_pipe *p, return ret; } -static bool test_EnumDomainAliases(struct dcerpc_pipe *p, - struct torture_context *tctx, - struct policy_handle *handle) +static bool test_EnumDomainAliases_all(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle) { NTSTATUS status; struct samr_EnumDomainAliases r; @@ -6113,10 +6114,10 @@ static bool test_OpenDomain(struct dcerpc_pipe *p, struct torture_context *tctx, ret &= test_CreateDomainGroup(p, tctx, &domain_handle, TEST_GROUPNAME, &group_handle, sid, true); ret &= test_QueryDomainInfo(p, tctx, &domain_handle); ret &= test_QueryDomainInfo2(p, tctx, &domain_handle); - ret &= test_EnumDomainUsers(p, tctx, &domain_handle); + ret &= test_EnumDomainUsers_all(p, tctx, &domain_handle); ret &= test_EnumDomainUsers_async(p, tctx, &domain_handle); - ret &= test_EnumDomainGroups(p, tctx, &domain_handle); - ret &= test_EnumDomainAliases(p, tctx, &domain_handle); + ret &= test_EnumDomainGroups_all(p, tctx, &domain_handle); + ret &= test_EnumDomainAliases_all(p, tctx, &domain_handle); ret &= test_QueryDisplayInfo2(p, tctx, &domain_handle); ret &= test_QueryDisplayInfo3(p, tctx, &domain_handle); ret &= test_QueryDisplayInfo_continue(p, tctx, &domain_handle); -- cgit From eb5e8dc82efae20c95a391a15c1264f2267e5a74 Mon Sep 17 00:00:00 2001 From: Günther Deschner Date: Mon, 25 May 2009 13:08:58 +0200 Subject: s4-smbtorture: add RPC-SAMR-LARGE-DC test. This rather simple test creates 4500 objects on a domain controller and checks the enum calls for the correct number of results. Guenther --- source4/torture/rpc/rpc.c | 1 + source4/torture/rpc/samr.c | 332 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 332 insertions(+), 1 deletion(-) (limited to 'source4') diff --git a/source4/torture/rpc/rpc.c b/source4/torture/rpc/rpc.c index 48a488741f..19b223beba 100644 --- a/source4/torture/rpc/rpc.c +++ b/source4/torture/rpc/rpc.c @@ -409,6 +409,7 @@ NTSTATUS torture_rpc_init(void) torture_suite_add_suite(suite, torture_rpc_samr_accessmask(suite)); torture_suite_add_suite(suite, torture_rpc_samr_passwords_pwdlastset(suite)); torture_suite_add_suite(suite, torture_rpc_samr_user_privileges(suite)); + torture_suite_add_suite(suite, torture_rpc_samr_large_dc(suite)); torture_suite_add_suite(suite, torture_rpc_epmapper(suite)); torture_suite_add_suite(suite, torture_rpc_initshutdown(suite)); torture_suite_add_suite(suite, torture_rpc_oxidresolve(suite)); diff --git a/source4/torture/rpc/samr.c b/source4/torture/rpc/samr.c index 3bc98b6315..92ce66fef2 100644 --- a/source4/torture/rpc/samr.c +++ b/source4/torture/rpc/samr.c @@ -48,7 +48,10 @@ enum torture_samr_choice { TORTURE_SAMR_PASSWORDS_PWDLASTSET, TORTURE_SAMR_USER_ATTRIBUTES, TORTURE_SAMR_USER_PRIVILEGES, - TORTURE_SAMR_OTHER + TORTURE_SAMR_OTHER, + TORTURE_SAMR_MANY_ACCOUNTS, + TORTURE_SAMR_MANY_GROUPS, + TORTURE_SAMR_MANY_ALIASES }; static bool test_QueryUserInfo(struct dcerpc_pipe *p, @@ -6041,7 +6044,235 @@ static bool test_RemoveMemberFromForeignDomain(struct dcerpc_pipe *p, return true; } +static bool test_EnumDomainUsers(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *domain_handle, + uint32_t *total_num_entries_p) +{ + NTSTATUS status; + struct samr_EnumDomainUsers r; + uint32_t resume_handle = 0; + uint32_t num_entries = 0; + uint32_t total_num_entries = 0; + struct samr_SamArray *sam; + + r.in.domain_handle = domain_handle; + r.in.acct_flags = ACB_NORMAL; + r.in.max_size = (uint32_t)-1; + r.in.resume_handle = &resume_handle; + r.out.sam = &sam; + r.out.num_entries = &num_entries; + r.out.resume_handle = &resume_handle; + + printf("Testing EnumDomainUsers\n"); + + do { + status = dcerpc_samr_EnumDomainUsers(p, tctx, &r); + if (NT_STATUS_IS_ERR(status)) { + torture_assert_ntstatus_ok(tctx, status, + "failed to enumerate users"); + } + + total_num_entries += num_entries; + } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); + + if (total_num_entries_p) { + *total_num_entries_p = total_num_entries; + } + + return true; +} + +static bool test_EnumDomainGroups(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *domain_handle, + uint32_t *total_num_entries_p) +{ + NTSTATUS status; + struct samr_EnumDomainGroups r; + uint32_t resume_handle = 0; + uint32_t num_entries = 0; + uint32_t total_num_entries = 0; + struct samr_SamArray *sam; + + r.in.domain_handle = domain_handle; + r.in.max_size = (uint32_t)-1; + r.in.resume_handle = &resume_handle; + + r.out.sam = &sam; + r.out.num_entries = &num_entries; + r.out.resume_handle = &resume_handle; + + printf("Testing EnumDomainGroups\n"); + + do { + status = dcerpc_samr_EnumDomainGroups(p, tctx, &r); + if (NT_STATUS_IS_ERR(status)) { + torture_assert_ntstatus_ok(tctx, status, + "failed to enumerate groups"); + } + + total_num_entries += num_entries; + } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); + + if (total_num_entries_p) { + *total_num_entries_p = total_num_entries; + } + + return true; +} + +static bool test_EnumDomainAliases(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *domain_handle, + uint32_t *total_num_entries_p) +{ + NTSTATUS status; + struct samr_EnumDomainAliases r; + uint32_t resume_handle = 0; + uint32_t num_entries = 0; + uint32_t total_num_entries = 0; + struct samr_SamArray *sam; + + r.in.domain_handle = domain_handle; + r.in.max_size = (uint32_t)-1; + r.in.resume_handle = &resume_handle; + + r.out.sam = &sam; + r.out.num_entries = &num_entries; + r.out.resume_handle = &resume_handle; + + printf("Testing EnumDomainAliases\n"); + + do { + status = dcerpc_samr_EnumDomainAliases(p, tctx, &r); + if (NT_STATUS_IS_ERR(status)) { + torture_assert_ntstatus_ok(tctx, status, + "failed to enumerate aliases"); + } + + total_num_entries += num_entries; + } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); + + if (total_num_entries_p) { + *total_num_entries_p = total_num_entries; + } + + return true; +} + +static bool test_ManyObjects(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *domain_handle, + struct dom_sid *domain_sid, + enum torture_samr_choice which_ops) +{ + uint32_t num_total = 1500; + uint32_t num_enum = 0; + uint32_t num_disp = 0; + uint32_t num_created = 0; + uint32_t num_anounced = 0; + bool ret = true; + NTSTATUS status; + uint32_t i; + + /* query */ + + { + struct samr_QueryDomainInfo2 r; + union samr_DomainInfo *info; + r.in.domain_handle = domain_handle; + r.in.level = 2; + r.out.info = &info; + + status = dcerpc_samr_QueryDomainInfo2(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, + "failed to query domain info"); + + switch (which_ops) { + case TORTURE_SAMR_MANY_ACCOUNTS: + num_anounced = info->general.num_users; + break; + case TORTURE_SAMR_MANY_GROUPS: + num_anounced = info->general.num_groups; + break; + case TORTURE_SAMR_MANY_ALIASES: + num_anounced = info->general.num_aliases; + break; + default: + return false; + } + } + + /* create */ + + for (i=0; i < num_total; i++) { + + struct policy_handle handle; + const char *name = NULL; + + ZERO_STRUCT(handle); + + switch (which_ops) { + case TORTURE_SAMR_MANY_ACCOUNTS: + name = talloc_asprintf(tctx, "%s%04d", TEST_ACCOUNT_NAME, i); + ret &= test_CreateUser(p, tctx, domain_handle, name, &handle, domain_sid, 0, NULL, false); + break; + case TORTURE_SAMR_MANY_GROUPS: + name = talloc_asprintf(tctx, "%s%04d", TEST_GROUPNAME, i); + ret &= test_CreateDomainGroup(p, tctx, domain_handle, name, &handle, domain_sid, false); + break; + case TORTURE_SAMR_MANY_ALIASES: + name = talloc_asprintf(tctx, "%s%04d", TEST_ALIASNAME, i); + ret &= test_CreateAlias(p, tctx, domain_handle, name, &handle, domain_sid, false); + break; + default: + return false; + } + if (!policy_handle_empty(&handle)) { + ret &= test_samr_handle_Close(p, tctx, &handle); + num_created++; + } + } + + /* enum */ + + switch (which_ops) { + case TORTURE_SAMR_MANY_ACCOUNTS: + ret &= test_EnumDomainUsers(p, tctx, domain_handle, &num_enum); + break; + case TORTURE_SAMR_MANY_GROUPS: + ret &= test_EnumDomainGroups(p, tctx, domain_handle, &num_enum); + break; + case TORTURE_SAMR_MANY_ALIASES: + ret &= test_EnumDomainAliases(p, tctx, domain_handle, &num_enum); + break; + default: + return false; + } + + torture_assert_int_equal(tctx, num_enum, num_anounced + num_created, + "unexpected number of results returned in enum call"); +#if 0 + /* TODO: dispinfo */ + + switch (which_ops) { + case TORTURE_SAMR_MANY_ACCOUNTS: + break; + case TORTURE_SAMR_MANY_GROUPS: + break; + case TORTURE_SAMR_MANY_ALIASES: + break; + default: + return false; + } + + torture_assert_int_equal(tctx, num_disp, num_anounced + num_created, + "unexpected number of results returned in dispinfo call"); +#endif + return ret; +} static bool test_Connect(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *handle); @@ -6101,6 +6332,11 @@ static bool test_OpenDomain(struct dcerpc_pipe *p, struct torture_context *tctx, printf("Testing PASSWORDS PWDLASTSET on domain %s failed!\n", dom_sid_string(tctx, sid)); } break; + case TORTURE_SAMR_MANY_ACCOUNTS: + case TORTURE_SAMR_MANY_GROUPS: + case TORTURE_SAMR_MANY_ALIASES: + ret &= test_ManyObjects(p, tctx, &domain_handle, sid, which_ops); + break; case TORTURE_SAMR_OTHER: ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, which_ops, NULL, true); if (!ret) { @@ -6529,3 +6765,97 @@ struct torture_suite *torture_rpc_samr_user_privileges(TALLOC_CTX *mem_ctx) return suite; } + +static bool torture_rpc_samr_many_accounts(struct torture_context *torture, + struct dcerpc_pipe *p2, + struct cli_credentials *machine_credentials) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct policy_handle handle; + + status = torture_rpc_connection(torture, &p, &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + ret &= test_Connect(p, torture, &handle); + + ret &= test_EnumDomains(p, torture, &handle, + TORTURE_SAMR_MANY_ACCOUNTS, + machine_credentials); + + ret &= test_samr_handle_Close(p, torture, &handle); + + return ret; +} + +static bool torture_rpc_samr_many_groups(struct torture_context *torture, + struct dcerpc_pipe *p2, + struct cli_credentials *machine_credentials) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct policy_handle handle; + + status = torture_rpc_connection(torture, &p, &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + ret &= test_Connect(p, torture, &handle); + + ret &= test_EnumDomains(p, torture, &handle, + TORTURE_SAMR_MANY_GROUPS, + machine_credentials); + + ret &= test_samr_handle_Close(p, torture, &handle); + + return ret; +} + +static bool torture_rpc_samr_many_aliases(struct torture_context *torture, + struct dcerpc_pipe *p2, + struct cli_credentials *machine_credentials) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct policy_handle handle; + + status = torture_rpc_connection(torture, &p, &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + ret &= test_Connect(p, torture, &handle); + + ret &= test_EnumDomains(p, torture, &handle, + TORTURE_SAMR_MANY_ALIASES, + machine_credentials); + + ret &= test_samr_handle_Close(p, torture, &handle); + + return ret; +} + +struct torture_suite *torture_rpc_samr_large_dc(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "SAMR-LARGE-DC"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_machine_rpc_iface_tcase(suite, "samr", + &ndr_table_samr, + TEST_ACCOUNT_NAME); + + torture_rpc_tcase_add_test_creds(tcase, "many_aliases", + torture_rpc_samr_many_aliases); + torture_rpc_tcase_add_test_creds(tcase, "many_groups", + torture_rpc_samr_many_groups); + torture_rpc_tcase_add_test_creds(tcase, "many_accounts", + torture_rpc_samr_many_accounts); + + return suite; +} -- cgit From 5d2cd816459a30107b4ea011c1874b051aa29b1c Mon Sep 17 00:00:00 2001 From: Slava Semushin Date: Sat, 23 May 2009 20:51:53 +0700 Subject: source{3,4}/torture/smbiconv.c(main): fixed file descriptor leak. File descriptor leaks only when we use file instead of stdout. Found by cppcheck: [./source3/torture/smbiconv.c:219]: (error) Resource leak: out [./source4/torture/smbiconv.c:211]: (error) Resource leak: out --- source4/torture/smbiconv.c | 1 + 1 file changed, 1 insertion(+) (limited to 'source4') diff --git a/source4/torture/smbiconv.c b/source4/torture/smbiconv.c index 4eece66bdf..173f37175b 100644 --- a/source4/torture/smbiconv.c +++ b/source4/torture/smbiconv.c @@ -208,6 +208,7 @@ int main(int argc, char *argv[]) cd = smb_iconv_open_ex(tctx, to, from, lp_parm_bool(tctx->lp_ctx, NULL, "iconv", "native", true)); if((int)cd == -1) { DEBUG(0,("unable to find from or to encoding, exiting...\n")); + if (out != stdout) fclose(out); return 1; } -- cgit