diff -Nru samba-4.5.8+dfsg/auth/auth_sam_reply.h samba-4.6.5+dfsg/auth/auth_sam_reply.h --- samba-4.5.8+dfsg/auth/auth_sam_reply.h 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/auth/auth_sam_reply.h 2017-01-11 07:55:14.000000000 +0000 @@ -33,7 +33,7 @@ /* The following definitions come from auth/auth_sam_reply.c */ NTSTATUS make_user_info_SamBaseInfo(TALLOC_CTX *mem_ctx, - const const char *account_name, + const char *account_name, const struct netr_SamBaseInfo *base, bool authenticated, struct auth_user_info **_user_info); diff -Nru samba-4.5.8+dfsg/auth/credentials/credentials.c samba-4.6.5+dfsg/auth/credentials/credentials.c --- samba-4.5.8+dfsg/auth/credentials/credentials.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/auth/credentials/credentials.c 2017-01-11 07:55:14.000000000 +0000 @@ -36,84 +36,12 @@ */ _PUBLIC_ struct cli_credentials *cli_credentials_init(TALLOC_CTX *mem_ctx) { - struct cli_credentials *cred = talloc(mem_ctx, struct cli_credentials); + struct cli_credentials *cred = talloc_zero(mem_ctx, struct cli_credentials); if (cred == NULL) { return cred; } - cred->workstation_obtained = CRED_UNINITIALISED; - cred->username_obtained = CRED_UNINITIALISED; - cred->password_obtained = CRED_UNINITIALISED; - cred->domain_obtained = CRED_UNINITIALISED; - cred->realm_obtained = CRED_UNINITIALISED; - cred->ccache_obtained = CRED_UNINITIALISED; - cred->client_gss_creds_obtained = CRED_UNINITIALISED; - cred->principal_obtained = CRED_UNINITIALISED; - cred->keytab_obtained = CRED_UNINITIALISED; - cred->server_gss_creds_obtained = CRED_UNINITIALISED; - - cred->ccache_threshold = CRED_UNINITIALISED; - cred->client_gss_creds_threshold = CRED_UNINITIALISED; - - cred->workstation = NULL; - cred->username = NULL; - cred->password = NULL; - cred->old_password = NULL; - cred->domain = NULL; - cred->realm = NULL; - cred->principal = NULL; - cred->salt_principal = NULL; - cred->impersonate_principal = NULL; - cred->self_service = NULL; - cred->target_service = NULL; - - cred->bind_dn = NULL; - - cred->nt_hash = NULL; - cred->old_nt_hash = NULL; - - cred->lm_response.data = NULL; - cred->lm_response.length = 0; - cred->nt_response.data = NULL; - cred->nt_response.length = 0; - - cred->ccache = NULL; - cred->client_gss_creds = NULL; - cred->keytab = NULL; - cred->server_gss_creds = NULL; - - cred->workstation_cb = NULL; - cred->password_cb = NULL; - cred->username_cb = NULL; - cred->domain_cb = NULL; - cred->realm_cb = NULL; - cred->principal_cb = NULL; - - cred->priv_data = NULL; - - cred->netlogon_creds = NULL; - cred->secure_channel_type = SEC_CHAN_NULL; - - cred->kvno = 0; - - cred->password_last_changed_time = 0; - - cred->smb_krb5_context = NULL; - - cred->machine_account_pending = false; - cred->machine_account_pending_lp_ctx = NULL; - - cred->machine_account = false; - - cred->password_tries = 0; - - cred->callback_running = false; - - cli_credentials_set_kerberos_state(cred, CRED_AUTO_USE_KERBEROS); - cli_credentials_set_gensec_features(cred, 0); - cli_credentials_set_krb_forwardable(cred, CRED_AUTO_KRB_FORWARDABLE); - - cred->forced_sasl_mech = NULL; + cred->winbind_separator = '\\'; return cred; } @@ -285,16 +213,37 @@ if (cred->principal_obtained < cred->username_obtained || cred->principal_obtained < MAX(cred->domain_obtained, cred->realm_obtained)) { + const char *effective_username = NULL; + const char *effective_realm = NULL; + enum credentials_obtained effective_obtained; + + effective_username = cli_credentials_get_username(cred); + if (effective_username == NULL || strlen(effective_username) == 0) { + *obtained = cred->username_obtained; + return NULL; + } + if (cred->domain_obtained > cred->realm_obtained) { - *obtained = MIN(cred->domain_obtained, cred->username_obtained); - return talloc_asprintf(mem_ctx, "%s@%s", - cli_credentials_get_username(cred), - cli_credentials_get_domain(cred)); + effective_realm = cli_credentials_get_domain(cred); + effective_obtained = MIN(cred->domain_obtained, + cred->username_obtained); } else { - *obtained = MIN(cred->domain_obtained, cred->username_obtained); + effective_realm = cli_credentials_get_realm(cred); + effective_obtained = MIN(cred->realm_obtained, + cred->username_obtained); + } + + if (effective_realm == NULL || strlen(effective_realm) == 0) { + effective_realm = cli_credentials_get_domain(cred); + effective_obtained = MIN(cred->domain_obtained, + cred->username_obtained); + } + + if (effective_realm != NULL && strlen(effective_realm) != 0) { + *obtained = effective_obtained; return talloc_asprintf(mem_ctx, "%s@%s", - cli_credentials_get_username(cred), - cli_credentials_get_realm(cred)); + effective_username, + effective_realm); } } *obtained = cred->principal_obtained; @@ -319,7 +268,11 @@ { if (obtained >= cred->principal_obtained) { cred->principal = talloc_strdup(cred, val); + if (cred->principal == NULL) { + return false; + } cred->principal_obtained = obtained; + cli_credentials_invalidate_ccache(cred, cred->principal_obtained); return true; } @@ -390,7 +343,8 @@ } if (cred->password_obtained == CRED_CALLBACK && - !cred->callback_running) { + !cred->callback_running && + !cred->password_will_be_nt_hash) { cred->callback_running = true; cred->password = cred->password_cb(cred); cred->callback_running = false; @@ -411,18 +365,54 @@ enum credentials_obtained obtained) { if (obtained >= cred->password_obtained) { + + cred->lm_response = data_blob_null; + cred->nt_response = data_blob_null; + cred->nt_hash = NULL; + cred->password = NULL; + + cli_credentials_invalidate_ccache(cred, obtained); + cred->password_tries = 0; + + if (val == NULL) { + cred->password_obtained = obtained; + return true; + } + + if (cred->password_will_be_nt_hash) { + struct samr_Password *nt_hash = NULL; + size_t val_len = strlen(val); + size_t converted; + + nt_hash = talloc(cred, struct samr_Password); + if (nt_hash == NULL) { + return false; + } + + converted = strhex_to_str((char *)nt_hash->hash, + sizeof(nt_hash->hash), + val, val_len); + if (converted != sizeof(nt_hash->hash)) { + TALLOC_FREE(nt_hash); + return false; + } + + cred->nt_hash = nt_hash; + cred->password_obtained = obtained; + return true; + } + cred->password = talloc_strdup(cred, val); - if (cred->password) { - /* Don't print the actual password in talloc memory dumps */ - talloc_set_name_const(cred->password, "password set via cli_credentials_set_password"); + if (cred->password == NULL) { + return false; } + + /* Don't print the actual password in talloc memory dumps */ + talloc_set_name_const(cred->password, + "password set via cli_credentials_set_password"); cred->password_obtained = obtained; - cli_credentials_invalidate_ccache(cred, cred->password_obtained); - cred->nt_hash = NULL; - cred->lm_response = data_blob(NULL, 0); - cred->nt_response = data_blob(NULL, 0); return true; } @@ -483,32 +473,85 @@ _PUBLIC_ struct samr_Password *cli_credentials_get_nt_hash(struct cli_credentials *cred, TALLOC_CTX *mem_ctx) { + enum credentials_obtained password_obtained; + enum credentials_obtained ccache_threshold; + enum credentials_obtained client_gss_creds_threshold; + bool password_is_nt_hash; const char *password = NULL; + struct samr_Password *nt_hash = NULL; if (cred->nt_hash != NULL) { - struct samr_Password *nt_hash = talloc(mem_ctx, struct samr_Password); - if (!nt_hash) { - return NULL; - } + /* + * If we already have a hash it's easy. + */ + goto return_hash; + } - *nt_hash = *cred->nt_hash; + /* + * This is a bit tricky, with password_will_be_nt_hash + * we still need to get the value via the password_callback + * but if we did that we should not remember it's state + * in the long run so we need to undo it. + */ - return nt_hash; - } + password_obtained = cred->password_obtained; + ccache_threshold = cred->ccache_threshold; + client_gss_creds_threshold = cred->client_gss_creds_threshold; + password_is_nt_hash = cred->password_will_be_nt_hash; + cred->password_will_be_nt_hash = false; password = cli_credentials_get_password(cred); - if (password) { - struct samr_Password *nt_hash = talloc(mem_ctx, struct samr_Password); - if (!nt_hash) { - return NULL; - } + cred->password_will_be_nt_hash = password_is_nt_hash; + if (password_is_nt_hash && password_obtained == CRED_CALLBACK) { + /* + * We got the nt_hash as string via the callback, + * so we need to undo the state change. + * + * And also don't remember it as plaintext password. + */ + cred->client_gss_creds_threshold = client_gss_creds_threshold; + cred->ccache_threshold = ccache_threshold; + cred->password_obtained = password_obtained; + cred->password = NULL; + } + + if (password == NULL) { + return NULL; + } + + nt_hash = talloc(cred, struct samr_Password); + if (nt_hash == NULL) { + return NULL; + } + + if (password_is_nt_hash) { + size_t password_len = strlen(password); + size_t converted; + + converted = strhex_to_str((char *)nt_hash->hash, + sizeof(nt_hash->hash), + password, password_len); + if (converted != sizeof(nt_hash->hash)) { + TALLOC_FREE(nt_hash); + return false; + } + } else { E_md4hash(password, nt_hash->hash); + } - return nt_hash; + cred->nt_hash = nt_hash; + nt_hash = NULL; + +return_hash: + nt_hash = talloc(mem_ctx, struct samr_Password); + if (nt_hash == NULL) { + return NULL; } - return NULL; + *nt_hash = *cred->nt_hash; + + return nt_hash; } /** @@ -742,14 +785,56 @@ } if ((p = strchr_m(uname,'@'))) { + /* + * We also need to set username and domain + * in order to undo the effect of + * cli_credentials_guess(). + */ + cli_credentials_set_username(credentials, uname, obtained); + cli_credentials_set_domain(credentials, "", obtained); + cli_credentials_set_principal(credentials, uname, obtained); *p = 0; cli_credentials_set_realm(credentials, p+1, obtained); return; - } else if ((p = strchr_m(uname,'\\')) || (p = strchr_m(uname, '/'))) { + } else if ((p = strchr_m(uname,'\\')) + || (p = strchr_m(uname, '/')) + || (p = strchr_m(uname, credentials->winbind_separator))) + { + const char *domain = NULL; + + domain = uname; *p = 0; - cli_credentials_set_domain(credentials, uname, obtained); uname = p+1; + + if (obtained == credentials->realm_obtained && + !strequal_m(credentials->domain, domain)) + { + /* + * We need to undo a former set with the same level + * in order to get the expected result from + * cli_credentials_get_principal(). + * + * But we only need to do that if the domain + * actually changes. + */ + cli_credentials_set_realm(credentials, domain, obtained); + } + cli_credentials_set_domain(credentials, domain, obtained); + } + if (obtained == credentials->principal_obtained && + !strequal_m(credentials->username, uname)) + { + /* + * We need to undo a former set with the same level + * in order to get the expected result from + * cli_credentials_get_principal(). + * + * But we only need to do that if the username + * actually changes. + */ + credentials->principal_obtained = CRED_UNINITIALISED; + credentials->principal = NULL; } cli_credentials_set_username(credentials, uname, obtained); } @@ -794,6 +879,9 @@ _PUBLIC_ void cli_credentials_set_conf(struct cli_credentials *cred, struct loadparm_context *lp_ctx) { + const char *sep = NULL; + const char *realm = lpcfg_realm(lp_ctx); + cli_credentials_set_username(cred, "", CRED_UNINITIALISED); if (lpcfg_parm_is_cmdline(lp_ctx, "workgroup")) { cli_credentials_set_domain(cred, lpcfg_workgroup(lp_ctx), CRED_SPECIFIED); @@ -805,10 +893,18 @@ } else { cli_credentials_set_workstation(cred, lpcfg_netbios_name(lp_ctx), CRED_UNINITIALISED); } + if (realm != NULL && strlen(realm) == 0) { + realm = NULL; + } if (lpcfg_parm_is_cmdline(lp_ctx, "realm")) { - cli_credentials_set_realm(cred, lpcfg_realm(lp_ctx), CRED_SPECIFIED); + cli_credentials_set_realm(cred, realm, CRED_SPECIFIED); } else { - cli_credentials_set_realm(cred, lpcfg_realm(lp_ctx), CRED_UNINITIALISED); + cli_credentials_set_realm(cred, realm, CRED_UNINITIALISED); + } + + sep = lpcfg_winbind_separator(lp_ctx); + if (sep != NULL && sep[0] != '\0') { + cred->winbind_separator = *lpcfg_winbind_separator(lp_ctx); } } @@ -928,6 +1024,7 @@ cli_credentials_set_username(cred, "", CRED_SPECIFIED); cli_credentials_set_domain(cred, "", CRED_SPECIFIED); cli_credentials_set_password(cred, NULL, CRED_SPECIFIED); + cli_credentials_set_principal(cred, NULL, CRED_SPECIFIED); cli_credentials_set_realm(cred, NULL, CRED_SPECIFIED); cli_credentials_set_workstation(cred, "", CRED_UNINITIALISED); cli_credentials_set_kerberos_state(cred, CRED_DONT_USE_KERBEROS); @@ -1024,6 +1121,10 @@ char *ptr, *val, *param; char **lines; int i, numlines; + const char *realm = NULL; + const char *domain = NULL; + const char *password = NULL; + const char *username = NULL; lines = file_lines_load(file, &numlines, 0, NULL); @@ -1054,17 +1155,57 @@ val++; if (strwicmp("password", param) == 0) { - cli_credentials_set_password(cred, val, obtained); + password = val; } else if (strwicmp("username", param) == 0) { - cli_credentials_set_username(cred, val, obtained); + username = val; } else if (strwicmp("domain", param) == 0) { - cli_credentials_set_domain(cred, val, obtained); + domain = val; } else if (strwicmp("realm", param) == 0) { - cli_credentials_set_realm(cred, val, obtained); + realm = val; } - memset(lines[i], 0, len); + + /* + * We need to readd '=' in order to let + * the strlen() work in the last loop + * that clears the memory. + */ + *ptr = '='; + } + + if (realm != NULL && strlen(realm) != 0) { + /* + * only overwrite with a valid string + */ + cli_credentials_set_realm(cred, realm, obtained); } + if (domain != NULL && strlen(domain) != 0) { + /* + * only overwrite with a valid string + */ + cli_credentials_set_domain(cred, domain, obtained); + } + + if (password != NULL) { + /* + * Here we allow "". + */ + cli_credentials_set_password(cred, password, obtained); + } + + if (username != NULL) { + /* + * The last "username" line takes preference + * if the string also contains domain, realm or + * password. + */ + cli_credentials_parse_string(cred, username, obtained); + } + + for (i = 0; i < numlines; i++) { + len = strlen(lines[i]); + memset(lines[i], 0, len); + } talloc_free(lines); return true; diff -Nru samba-4.5.8+dfsg/auth/credentials/credentials.h samba-4.6.5+dfsg/auth/credentials/credentials.h --- samba-4.5.8+dfsg/auth/credentials/credentials.h 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/auth/credentials/credentials.h 2017-01-11 07:55:14.000000000 +0000 @@ -201,6 +201,8 @@ enum credentials_obtained obtained); bool cli_credentials_set_old_utf16_password(struct cli_credentials *cred, const DATA_BLOB *password_utf16); +void cli_credentials_set_password_will_be_nt_hash(struct cli_credentials *cred, + bool val); bool cli_credentials_set_nt_hash(struct cli_credentials *cred, const struct samr_Password *nt_hash, enum credentials_obtained obtained); diff -Nru samba-4.5.8+dfsg/auth/credentials/credentials_internal.h samba-4.6.5+dfsg/auth/credentials/credentials_internal.h --- samba-4.5.8+dfsg/auth/credentials/credentials_internal.h 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/auth/credentials/credentials_internal.h 2017-01-11 07:55:14.000000000 +0000 @@ -113,6 +113,10 @@ /* Whether any callback is currently running */ bool callback_running; + + char winbind_separator; + + bool password_will_be_nt_hash; }; #endif /* __CREDENTIALS_INTERNAL_H__ */ diff -Nru samba-4.5.8+dfsg/auth/credentials/credentials_krb5.c samba-4.6.5+dfsg/auth/credentials/credentials_krb5.c --- samba-4.5.8+dfsg/auth/credentials/credentials_krb5.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/auth/credentials/credentials_krb5.c 2017-05-23 08:19:23.000000000 +0000 @@ -39,6 +39,30 @@ struct cli_credentials *cred, enum credentials_obtained obtained); +/* Free a memory ccache */ +static int free_mccache(struct ccache_container *ccc) +{ + if (ccc->ccache != NULL) { + krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, + ccc->ccache); + ccc->ccache = NULL; + } + + return 0; +} + +/* Free a disk-based ccache */ +static int free_dccache(struct ccache_container *ccc) +{ + if (ccc->ccache != NULL) { + krb5_cc_close(ccc->smb_krb5_context->krb5_context, + ccc->ccache); + ccc->ccache = NULL; + } + + return 0; +} + _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred, struct loadparm_context *lp_ctx, struct smb_krb5_context **smb_krb5_context) @@ -83,7 +107,8 @@ enum credentials_obtained obtained, const char **error_string) { - + bool ok; + char *realm; krb5_principal princ; krb5_error_code ret; char *name; @@ -110,11 +135,24 @@ return ret; } - cli_credentials_set_principal(cred, name, obtained); - + ok = cli_credentials_set_principal(cred, name, obtained); + if (!ok) { + krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ); + return ENOMEM; + } free(name); + realm = smb_krb5_principal_get_realm(ccache->smb_krb5_context->krb5_context, + princ); krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ); + if (realm == NULL) { + return ENOMEM; + } + ok = cli_credentials_set_realm(cred, realm, obtained); + SAFE_FREE(realm); + if (!ok) { + return ENOMEM; + } /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */ cred->ccache_obtained = obtained; @@ -122,21 +160,6 @@ return 0; } -/* Free a memory ccache */ -static int free_mccache(struct ccache_container *ccc) -{ - krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache); - - return 0; -} - -/* Free a disk-based ccache */ -static int free_dccache(struct ccache_container *ccc) { - krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache); - - return 0; -} - _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred, struct loadparm_context *lp_ctx, const char *name, @@ -520,6 +543,7 @@ struct ccache_container *ccache; #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER; + gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X); #endif krb5_enctype *etypes = NULL; @@ -569,9 +593,14 @@ return ENOMEM; } - maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL, - &gcc->creds); - if ((maj_stat == GSS_S_FAILURE) && (min_stat == (OM_uint32)KRB5_CC_END || min_stat == (OM_uint32) KRB5_CC_NOTFOUND)) { + maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context, + ccache->ccache, NULL, NULL, + &gcc->creds); + if ((maj_stat == GSS_S_FAILURE) && + (min_stat == (OM_uint32)KRB5_CC_END || + min_stat == (OM_uint32)KRB5_CC_NOTFOUND || + min_stat == (OM_uint32)KRB5_FCC_NOFILE)) + { /* This CCACHE is no good. Ensure we don't use it again */ cli_credentials_unconditionally_invalidate_ccache(cred); @@ -583,8 +612,9 @@ return ret; } - maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL, - &gcc->creds); + maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context, + ccache->ccache, NULL, NULL, + &gcc->creds); } @@ -595,7 +625,7 @@ } else { ret = EINVAL; } - (*error_string) = talloc_asprintf(cred, "gss_krb5_import_cred failed: %s", error_message(ret)); + (*error_string) = talloc_asprintf(cred, "smb_gss_krb5_import_cred failed: %s", error_message(ret)); return ret; } @@ -611,7 +641,7 @@ * and used for the AS-REQ, so it wasn't possible to disable the usage * of AES keys. */ - min_stat = get_kerberos_allowed_etypes(ccache->smb_krb5_context->krb5_context, + min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context, &etypes); if (min_stat == 0) { OM_uint32 num_ktypes; @@ -645,7 +675,7 @@ * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938 */ maj_stat = gss_set_cred_option(&min_stat, &gcc->creds, - GSS_KRB5_CRED_NO_CI_FLAGS_X, + oid, &empty_buffer); if (maj_stat) { talloc_free(gcc); @@ -1062,12 +1092,14 @@ if (ktc->password_based || obtained < CRED_SPECIFIED) { /* This creates a GSSAPI cred_id_t for match-by-key with only the keytab set */ - maj_stat = gss_krb5_import_cred(&min_stat, NULL, NULL, ktc->keytab, - &gcc->creds); + maj_stat = smb_gss_krb5_import_cred(&min_stat, smb_krb5_context->krb5_context, + NULL, NULL, ktc->keytab, + &gcc->creds); } else { /* This creates a GSSAPI cred_id_t with the principal and keytab set, matching by name */ - maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab, - &gcc->creds); + maj_stat = smb_gss_krb5_import_cred(&min_stat, smb_krb5_context->krb5_context, + NULL, princ, ktc->keytab, + &gcc->creds); } if (maj_stat) { if (min_stat) { diff -Nru samba-4.5.8+dfsg/auth/credentials/credentials_ntlm.c samba-4.6.5+dfsg/auth/credentials/credentials_ntlm.c --- samba-4.5.8+dfsg/auth/credentials/credentials_ntlm.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/auth/credentials/credentials_ntlm.c 2017-01-11 07:55:14.000000000 +0000 @@ -36,29 +36,57 @@ DATA_BLOB *_lm_response, DATA_BLOB *_nt_response, DATA_BLOB *_lm_session_key, DATA_BLOB *_session_key) { - const char *user, *domain; - DATA_BLOB lm_response, nt_response; - DATA_BLOB lm_session_key, session_key; - const struct samr_Password *nt_hash; - lm_session_key = data_blob(NULL, 0); + TALLOC_CTX *frame = talloc_stackframe(); + const char *user = NULL; + const char *domain = NULL; + DATA_BLOB lm_response = data_blob_null; + DATA_BLOB nt_response = data_blob_null; + DATA_BLOB lm_session_key = data_blob_null; + DATA_BLOB session_key = data_blob_null; + const struct samr_Password *nt_hash = NULL; + + if (cred->use_kerberos == CRED_MUST_USE_KERBEROS) { + TALLOC_FREE(frame); + return NT_STATUS_INVALID_PARAMETER_MIX; + } /* We may already have an NTLM response we prepared earlier. * This is used for NTLM pass-though authentication */ if (cred->nt_response.data || cred->lm_response.data) { - *_nt_response = cred->nt_response; - *_lm_response = cred->lm_response; + if (cred->nt_response.length != 0) { + nt_response = data_blob_dup_talloc(frame, + cred->nt_response); + if (nt_response.data == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + } + if (cred->lm_response.length != 0) { + lm_response = data_blob_dup_talloc(frame, + cred->lm_response); + if (lm_response.data == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + } - if (!cred->lm_response.data) { + if (cred->lm_response.data == NULL) { *flags = *flags & ~CLI_CRED_LANMAN_AUTH; } - *_lm_session_key = data_blob(NULL, 0); - *_session_key = data_blob(NULL, 0); - return NT_STATUS_OK; + goto done; } - nt_hash = cli_credentials_get_nt_hash(cred, mem_ctx); + nt_hash = cli_credentials_get_nt_hash(cred, frame); - cli_credentials_get_ntlm_username_domain(cred, mem_ctx, &user, &domain); + cli_credentials_get_ntlm_username_domain(cred, frame, &user, &domain); + if (user == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + if (domain == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } /* If we are sending a username@realm login (see function * above), then we will not send LM, it will not be @@ -71,22 +99,22 @@ if (cred->machine_account) { *flags = *flags & ~CLI_CRED_LANMAN_AUTH; } - - if (cred->use_kerberos == CRED_MUST_USE_KERBEROS) { - return NT_STATUS_ACCESS_DENIED; - } if (!nt_hash) { - static const uint8_t zeros[16]; /* do nothing - blobs are zero length */ /* session key is all zeros */ - session_key = data_blob_talloc(mem_ctx, zeros, 16); - lm_session_key = data_blob_talloc(mem_ctx, zeros, 16); + session_key = data_blob_talloc_zero(frame, 16); + if (session_key.data == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + lm_session_key = data_blob_talloc_zero(frame, 16); + if (lm_session_key.data == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } - lm_response = data_blob(NULL, 0); - nt_response = data_blob(NULL, 0); - /* not doing NTLM2 without a password */ *flags &= ~CLI_CRED_NTLM2; } else if (*flags & CLI_CRED_NTLMv2_AUTH) { @@ -94,19 +122,21 @@ if (!target_info.length) { /* be lazy, match win2k - we can't do NTLMv2 without it */ DEBUG(1, ("Server did not provide 'target information', required for NTLMv2\n")); + TALLOC_FREE(frame); return NT_STATUS_INVALID_PARAMETER; } /* TODO: if the remote server is standalone, then we should replace 'domain' with the server name as supplied above */ - if (!SMBNTLMv2encrypt_hash(mem_ctx, + if (!SMBNTLMv2encrypt_hash(frame, user, domain, nt_hash->hash, &challenge, server_timestamp, &target_info, &lm_response, &nt_response, NULL, &session_key)) { + TALLOC_FREE(frame); return NT_STATUS_NO_MEMORY; } @@ -123,103 +153,139 @@ uint8_t session_nonce[16]; uint8_t session_nonce_hash[16]; uint8_t user_session_key[16]; - - lm_response = data_blob_talloc(mem_ctx, NULL, 24); + + lm_response = data_blob_talloc_zero(frame, 24); + if (lm_response.data == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } generate_random_buffer(lm_response.data, 8); - memset(lm_response.data+8, 0, 16); memcpy(session_nonce, challenge.data, 8); memcpy(&session_nonce[8], lm_response.data, 8); - + MD5Init(&md5_session_nonce_ctx); - MD5Update(&md5_session_nonce_ctx, challenge.data, 8); - MD5Update(&md5_session_nonce_ctx, lm_response.data, 8); + MD5Update(&md5_session_nonce_ctx, session_nonce, + sizeof(session_nonce)); MD5Final(session_nonce_hash, &md5_session_nonce_ctx); DEBUG(5, ("NTLMSSP challenge set by NTLM2\n")); DEBUG(5, ("challenge is: \n")); dump_data(5, session_nonce_hash, 8); - - nt_response = data_blob_talloc(mem_ctx, NULL, 24); + + nt_response = data_blob_talloc_zero(frame, 24); + if (nt_response.data == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } SMBOWFencrypt(nt_hash->hash, session_nonce_hash, nt_response.data); - - session_key = data_blob_talloc(mem_ctx, NULL, 16); + + session_key = data_blob_talloc_zero(frame, 16); + if (session_key.data == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } SMBsesskeygen_ntv1(nt_hash->hash, user_session_key); hmac_md5(user_session_key, session_nonce, sizeof(session_nonce), session_key.data); + ZERO_STRUCT(user_session_key); dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length); /* LM Key is incompatible... */ *flags &= ~CLI_CRED_LANMAN_AUTH; } else { + const char *password = cli_credentials_get_password(cred); uint8_t lm_hash[16]; - nt_response = data_blob_talloc(mem_ctx, NULL, 24); + bool do_lm = false; + + nt_response = data_blob_talloc_zero(frame, 24); + if (nt_response.data == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } SMBOWFencrypt(nt_hash->hash, challenge.data, nt_response.data); - - session_key = data_blob_talloc(mem_ctx, NULL, 16); + + session_key = data_blob_talloc_zero(frame, 16); + if (session_key.data == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } SMBsesskeygen_ntv1(nt_hash->hash, session_key.data); dump_data_pw("NT session key:\n", session_key.data, session_key.length); /* lanman auth is insecure, it may be disabled. We may also not have a password */ - if (*flags & CLI_CRED_LANMAN_AUTH) { - const char *password; - password = cli_credentials_get_password(cred); - if (!password) { - lm_response = nt_response; - } else { - lm_response = data_blob_talloc(mem_ctx, NULL, 24); - if (!SMBencrypt(password,challenge.data, - lm_response.data)) { - /* If the LM password was too long (and therefore the LM hash being - of the first 14 chars only), don't send it. - - We don't have any better options but to send the NT response - */ - data_blob_free(&lm_response); - lm_response = nt_response; - /* LM Key is incompatible with 'long' passwords */ - *flags &= ~CLI_CRED_LANMAN_AUTH; - } else if (E_deshash(password, lm_hash)) { - lm_session_key = data_blob_talloc(mem_ctx, NULL, 16); - memcpy(lm_session_key.data, lm_hash, 8); - memset(&lm_session_key.data[8], '\0', 8); - - if (!(*flags & CLI_CRED_NTLM_AUTH)) { - session_key = lm_session_key; - } - } + + if (password != NULL) { + do_lm = E_deshash(password, lm_hash); + } + + if (*flags & CLI_CRED_LANMAN_AUTH && do_lm) { + lm_response = data_blob_talloc_zero(frame, 24); + if (lm_response.data == NULL) { + ZERO_STRUCT(lm_hash); + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; } + + SMBencrypt_hash(lm_hash, + challenge.data, + lm_response.data); } else { - const char *password; + /* just copy the nt_response */ + lm_response = data_blob_dup_talloc(frame, nt_response); + if (lm_response.data == NULL) { + ZERO_STRUCT(lm_hash); + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + } + + if (do_lm) { + lm_session_key = data_blob_talloc_zero(frame, 16); + if (lm_session_key.data == NULL) { + ZERO_STRUCT(lm_hash); + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + memcpy(lm_session_key.data, lm_hash, 8); - /* LM Key is incompatible... */ - lm_response = nt_response; - *flags &= ~CLI_CRED_LANMAN_AUTH; - - password = cli_credentials_get_password(cred); - if (password && E_deshash(password, lm_hash)) { - lm_session_key = data_blob_talloc(mem_ctx, NULL, 16); - memcpy(lm_session_key.data, lm_hash, 8); - memset(&lm_session_key.data[8], '\0', 8); + if (!(*flags & CLI_CRED_NTLM_AUTH)) { + memcpy(session_key.data, lm_session_key.data, 16); } + ZERO_STRUCT(lm_hash); } } - if (_lm_response) { + +done: + if (_lm_response != NULL) { + talloc_steal(mem_ctx, lm_response.data); *_lm_response = lm_response; + } else { + data_blob_clear(&lm_response); } - if (_nt_response) { + if (_nt_response != NULL) { + talloc_steal(mem_ctx, nt_response.data); *_nt_response = nt_response; + } else { + data_blob_clear(&nt_response); } - if (_lm_session_key) { + if (_lm_session_key != NULL) { + talloc_steal(mem_ctx, lm_session_key.data); *_lm_session_key = lm_session_key; + } else { + data_blob_clear(&lm_session_key); } - if (_session_key) { + if (_session_key != NULL) { + talloc_steal(mem_ctx, session_key.data); *_session_key = session_key; + } else { + data_blob_clear(&session_key); } + TALLOC_FREE(frame); return NT_STATUS_OK; } @@ -235,6 +301,8 @@ const DATA_BLOB *password_utf16, enum credentials_obtained obtained) { + cred->password_will_be_nt_hash = false; + if (password_utf16 == NULL) { return cli_credentials_set_password(cred, NULL, obtained); } @@ -323,10 +391,27 @@ return true; } +_PUBLIC_ void cli_credentials_set_password_will_be_nt_hash(struct cli_credentials *cred, + bool val) +{ + /* + * We set this here and the next cli_credentials_set_password() + * that resets the password or password callback + * will pick this up. + * + * cli_credentials_set_nt_hash() and + * cli_credentials_set_utf16_password() will reset this + * to false. + */ + cred->password_will_be_nt_hash = val; +} + _PUBLIC_ bool cli_credentials_set_nt_hash(struct cli_credentials *cred, const struct samr_Password *nt_hash, enum credentials_obtained obtained) { + cred->password_will_be_nt_hash = false; + if (obtained >= cred->password_obtained) { cli_credentials_set_password(cred, NULL, obtained); if (nt_hash) { diff -Nru samba-4.5.8+dfsg/auth/credentials/pycredentials.c samba-4.6.5+dfsg/auth/credentials/pycredentials.c --- samba-4.5.8+dfsg/auth/credentials/pycredentials.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/auth/credentials/pycredentials.c 2017-01-11 07:55:14.000000000 +0000 @@ -59,6 +59,43 @@ return PyBool_FromLong(cli_credentials_set_username(PyCredentials_AsCliCredentials(self), newval, obt)); } +static PyObject *py_creds_get_ntlm_username_domain(PyObject *self, PyObject *unused) +{ + TALLOC_CTX *frame = talloc_stackframe(); + const char *user = NULL; + const char *domain = NULL; + PyObject *ret = NULL; + cli_credentials_get_ntlm_username_domain(PyCredentials_AsCliCredentials(self), + frame, &user, &domain); + ret = Py_BuildValue("(OO)", + PyString_FromStringOrNULL(user), + PyString_FromStringOrNULL(domain)); + TALLOC_FREE(frame); + return ret; +} + +static PyObject *py_creds_get_principal(PyObject *self, PyObject *unused) +{ + TALLOC_CTX *frame = talloc_stackframe(); + PyObject *ret = PyString_FromStringOrNULL(cli_credentials_get_principal(PyCredentials_AsCliCredentials(self), frame)); + TALLOC_FREE(frame); + return ret; +} + +static PyObject *py_creds_set_principal(PyObject *self, PyObject *args) +{ + char *newval; + enum credentials_obtained obt = CRED_SPECIFIED; + int _obt = obt; + + if (!PyArg_ParseTuple(args, "s|i", &newval, &_obt)) { + return NULL; + } + obt = _obt; + + return PyBool_FromLong(cli_credentials_set_principal(PyCredentials_AsCliCredentials(self), newval, obt)); +} + static PyObject *py_creds_get_password(PyObject *self, PyObject *unused) { return PyString_FromStringOrNULL(cli_credentials_get_password(PyCredentials_AsCliCredentials(self))); @@ -262,6 +299,36 @@ Py_RETURN_NONE; } +static PyObject *py_creds_parse_file(PyObject *self, PyObject *args) +{ + char *newval; + enum credentials_obtained obt = CRED_SPECIFIED; + int _obt = obt; + + if (!PyArg_ParseTuple(args, "s|i", &newval, &_obt)) { + return NULL; + } + obt = _obt; + + cli_credentials_parse_file(PyCredentials_AsCliCredentials(self), newval, obt); + Py_RETURN_NONE; +} + +static PyObject *py_cli_credentials_set_password_will_be_nt_hash(PyObject *self, PyObject *args) +{ + struct cli_credentials *creds = PyCredentials_AsCliCredentials(self); + PyObject *py_val = NULL; + bool val = false; + + if (!PyArg_ParseTuple(args, "O!", &PyBool_Type, &py_val)) { + return NULL; + } + val = PyObject_IsTrue(py_val); + + cli_credentials_set_password_will_be_nt_hash(creds, val); + Py_RETURN_NONE; +} + static PyObject *py_creds_get_nt_hash(PyObject *self, PyObject *unused) { PyObject *ret; @@ -461,37 +528,45 @@ { "get_username", py_creds_get_username, METH_NOARGS, "S.get_username() -> username\nObtain username." }, { "set_username", py_creds_set_username, METH_VARARGS, - "S.set_username(name, obtained=CRED_SPECIFIED) -> None\n" + "S.set_username(name[, credentials.SPECIFIED]) -> None\n" "Change username." }, + { "get_principal", py_creds_get_principal, METH_NOARGS, + "S.get_principal() -> user@realm\nObtain user principal." }, + { "set_principal", py_creds_set_principal, METH_VARARGS, + "S.set_principal(name[, credentials.SPECIFIED]) -> None\n" + "Change principal." }, { "get_password", py_creds_get_password, METH_NOARGS, "S.get_password() -> password\n" "Obtain password." }, + { "get_ntlm_username_domain", py_creds_get_ntlm_username_domain, METH_NOARGS, + "S.get_ntlm_username_domain() -> (domain, username)\n" + "Obtain NTLM username and domain, split up either as (DOMAIN, user) or (\"\", \"user@realm\")." }, { "set_password", py_creds_set_password, METH_VARARGS, - "S.set_password(password, obtained=CRED_SPECIFIED) -> None\n" + "S.set_password(password[, credentials.SPECIFIED]) -> None\n" "Change password." }, { "set_utf16_password", py_creds_set_utf16_password, METH_VARARGS, - "S.set_utf16_password(password, obtained=CRED_SPECIFIED) -> None\n" + "S.set_utf16_password(password[, credentials.SPECIFIED]) -> None\n" "Change password." }, { "get_old_password", py_creds_get_old_password, METH_NOARGS, "S.get_old_password() -> password\n" "Obtain old password." }, { "set_old_password", py_creds_set_old_password, METH_VARARGS, - "S.set_old_password(password, obtained=CRED_SPECIFIED) -> None\n" + "S.set_old_password(password[, credentials.SPECIFIED]) -> None\n" "Change old password." }, { "set_old_utf16_password", py_creds_set_old_utf16_password, METH_VARARGS, - "S.set_old_utf16_password(password, obtained=CRED_SPECIFIED) -> None\n" + "S.set_old_utf16_password(password[, credentials.SPECIFIED]) -> None\n" "Change old password." }, { "get_domain", py_creds_get_domain, METH_NOARGS, "S.get_domain() -> domain\n" "Obtain domain name." }, { "set_domain", py_creds_set_domain, METH_VARARGS, - "S.set_domain(domain, obtained=CRED_SPECIFIED) -> None\n" + "S.set_domain(domain[, credentials.SPECIFIED]) -> None\n" "Change domain name." }, { "get_realm", py_creds_get_realm, METH_NOARGS, "S.get_realm() -> realm\n" "Obtain realm name." }, { "set_realm", py_creds_set_realm, METH_VARARGS, - "S.set_realm(realm, obtained=CRED_SPECIFIED) -> None\n" + "S.set_realm(realm[, credentials.SPECIFIED]) -> None\n" "Change realm name." }, { "get_bind_dn", py_creds_get_bind_dn, METH_NOARGS, "S.get_bind_dn() -> bind dn\n" @@ -517,8 +592,16 @@ "S.set_cmdline_callbacks() -> bool\n" "Use command-line to obtain credentials not explicitly set." }, { "parse_string", py_creds_parse_string, METH_VARARGS, - "S.parse_string(text, obtained=CRED_SPECIFIED) -> None\n" + "S.parse_string(text[, credentials.SPECIFIED]) -> None\n" "Parse credentials string." }, + { "parse_file", py_creds_parse_file, METH_VARARGS, + "S.parse_file(filename[, credentials.SPECIFIED]) -> None\n" + "Parse credentials file." }, + { "set_password_will_be_nt_hash", + py_cli_credentials_set_password_will_be_nt_hash, METH_VARARGS, + "S.set_password_will_be_nt_hash(bool) -> None\n" + "Alters the behaviour of S.set_password() " + "to expect the NTHASH as hexstring." }, { "get_nt_hash", py_creds_get_nt_hash, METH_NOARGS, NULL }, { "get_kerberos_state", py_creds_get_kerberos_state, METH_NOARGS, @@ -566,6 +649,13 @@ if (m == NULL) return; + PyModule_AddObject(m, "UNINITIALISED", PyInt_FromLong(CRED_UNINITIALISED)); + PyModule_AddObject(m, "CALLBACK", PyInt_FromLong(CRED_CALLBACK)); + PyModule_AddObject(m, "GUESS_ENV", PyInt_FromLong(CRED_GUESS_ENV)); + PyModule_AddObject(m, "GUESS_FILE", PyInt_FromLong(CRED_GUESS_FILE)); + PyModule_AddObject(m, "CALLBACK_RESULT", PyInt_FromLong(CRED_CALLBACK_RESULT)); + PyModule_AddObject(m, "SPECIFIED", PyInt_FromLong(CRED_SPECIFIED)); + PyModule_AddObject(m, "AUTO_USE_KERBEROS", PyInt_FromLong(CRED_AUTO_USE_KERBEROS)); PyModule_AddObject(m, "DONT_USE_KERBEROS", PyInt_FromLong(CRED_DONT_USE_KERBEROS)); PyModule_AddObject(m, "MUST_USE_KERBEROS", PyInt_FromLong(CRED_MUST_USE_KERBEROS)); diff -Nru samba-4.5.8+dfsg/auth/gensec/gensec.c samba-4.6.5+dfsg/auth/gensec/gensec.c --- samba-4.5.8+dfsg/auth/gensec/gensec.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/auth/gensec/gensec.c 2017-01-11 07:55:14.000000000 +0000 @@ -227,45 +227,32 @@ return gensec_security->max_update_size; } -static NTSTATUS gensec_verify_dcerpc_auth_level(struct gensec_security *gensec_security) +static NTSTATUS gensec_verify_features(struct gensec_security *gensec_security) { - if (gensec_security->dcerpc_auth_level == 0) { - return NT_STATUS_OK; - } - /* - * Because callers using the - * gensec_start_mech_by_auth_type() never call - * gensec_want_feature(), it isn't sensible for them - * to have to call gensec_have_feature() manually, and - * these are not points of negotiation, but are - * asserted by the client + * gensec_want_feature(GENSEC_FEATURE_SIGN) + * and + * gensec_want_feature(GENSEC_FEATURE_SEAL) + * require these flags to be available. */ - switch (gensec_security->dcerpc_auth_level) { - case DCERPC_AUTH_LEVEL_INTEGRITY: + if (gensec_security->want_features & GENSEC_FEATURE_SIGN) { if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(0,("Did not manage to negotiate mandatory feature " - "SIGN for dcerpc auth_level %u\n", - gensec_security->dcerpc_auth_level)); + "SIGN\n")); return NT_STATUS_ACCESS_DENIED; } - break; - case DCERPC_AUTH_LEVEL_PRIVACY: - if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { + } + if (gensec_security->want_features & GENSEC_FEATURE_SEAL) { + if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(0,("Did not manage to negotiate mandatory feature " - "SIGN for dcerpc auth_level %u\n", - gensec_security->dcerpc_auth_level)); + "SEAL\n")); return NT_STATUS_ACCESS_DENIED; } - if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { + if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(0,("Did not manage to negotiate mandatory feature " - "SEAL for dcerpc auth_level %u\n", - gensec_security->dcerpc_auth_level)); + "SIGN for SEAL\n")); return NT_STATUS_ACCESS_DENIED; } - break; - default: - break; } return NT_STATUS_OK; @@ -315,7 +302,7 @@ * these are not points of negotiation, but are * asserted by the client */ - status = gensec_verify_dcerpc_auth_level(gensec_security); + status = gensec_verify_features(gensec_security); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -490,7 +477,7 @@ * these are not points of negotiation, but are * asserted by the client */ - status = gensec_verify_dcerpc_auth_level(state->gensec_security); + status = gensec_verify_features(state->gensec_security); if (tevent_req_nterror(req, status)) { return; } diff -Nru samba-4.5.8+dfsg/auth/gensec/gensec_start.c samba-4.6.5+dfsg/auth/gensec/gensec_start.c --- samba-4.5.8+dfsg/auth/gensec/gensec_start.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/auth/gensec/gensec_start.c 2017-01-11 07:55:14.000000000 +0000 @@ -742,7 +742,17 @@ gensec_want_feature(gensec_security, GENSEC_FEATURE_DCE_STYLE); gensec_want_feature(gensec_security, GENSEC_FEATURE_ASYNC_REPLIES); if (auth_level == DCERPC_AUTH_LEVEL_INTEGRITY) { - gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN); + if (gensec_security->gensec_role == GENSEC_CLIENT) { + gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN); + } + } else if (auth_level == DCERPC_AUTH_LEVEL_PACKET) { + /* + * For connection oriented DCERPC DCERPC_AUTH_LEVEL_PACKET (4) + * has the same behavior as DCERPC_AUTH_LEVEL_INTEGRITY (5). + */ + if (gensec_security->gensec_role == GENSEC_CLIENT) { + gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN); + } } else if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN); gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL); diff -Nru samba-4.5.8+dfsg/auth/gensec/spnego.c samba-4.6.5+dfsg/auth/gensec/spnego.c --- samba-4.5.8+dfsg/auth/gensec/spnego.c 2016-09-13 08:21:35.000000000 +0000 +++ samba-4.6.5+dfsg/auth/gensec/spnego.c 2017-03-07 09:09:03.000000000 +0000 @@ -511,10 +511,34 @@ NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_LOGON_SERVERS) || NT_STATUS_EQUAL(nt_status, NT_STATUS_TIME_DIFFERENCE_AT_DC) || NT_STATUS_EQUAL(nt_status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) { - /* Pretend we never started it (lets the first run find some incompatible demand) */ + const char *next = NULL; + const char *principal = NULL; + int dbg_level = DBGLVL_WARNING; + + if (all_sec[i+1].op != NULL) { + next = all_sec[i+1].op->name; + dbg_level = DBGLVL_NOTICE; + } + + if (gensec_security->target.principal != NULL) { + principal = gensec_security->target.principal; + } else if (gensec_security->target.service != NULL && + gensec_security->target.hostname != NULL) + { + principal = talloc_asprintf(spnego_state->sub_sec_security, + "%s/%s", + gensec_security->target.service, + gensec_security->target.hostname); + } else { + principal = gensec_security->target.hostname; + } + + DEBUG(dbg_level, ("SPNEGO(%s) creating NEG_TOKEN_INIT for %s failed (next[%s]): %s\n", + spnego_state->sub_sec_security->ops->name, + principal, + next, nt_errstr(nt_status))); - DEBUG(3, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n", - spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status))); + /* Pretend we never started it (lets the first run find some incompatible demand) */ talloc_free(spnego_state->sub_sec_security); spnego_state->sub_sec_security = NULL; continue; @@ -619,8 +643,32 @@ if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) { - DEBUG(1, ("SPNEGO(%s) creating NEG_TOKEN_INIT failed: %s\n", - spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status))); + const char *next = NULL; + const char *principal = NULL; + int dbg_level = DBGLVL_WARNING; + + if (all_sec[i+1].op != NULL) { + next = all_sec[i+1].op->name; + dbg_level = DBGLVL_NOTICE; + } + + if (gensec_security->target.principal != NULL) { + principal = gensec_security->target.principal; + } else if (gensec_security->target.service != NULL && + gensec_security->target.hostname != NULL) + { + principal = talloc_asprintf(spnego_state->sub_sec_security, + "%s/%s", + gensec_security->target.service, + gensec_security->target.hostname); + } else { + principal = gensec_security->target.hostname; + } + + DEBUG(dbg_level, ("SPNEGO(%s) creating NEG_TOKEN_INIT for %s failed (next[%s]): %s\n", + spnego_state->sub_sec_security->ops->name, + principal, + next, nt_errstr(nt_status))); talloc_free(spnego_state->sub_sec_security); spnego_state->sub_sec_security = NULL; /* Pretend we never started it (lets the first run find some incompatible demand) */ @@ -1571,8 +1619,8 @@ { struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data; + gensec_security->want_features |= feature; if (!spnego_state || !spnego_state->sub_sec_security) { - gensec_security->want_features |= feature; return; } diff -Nru samba-4.5.8+dfsg/auth/kerberos/gssapi_pac.c samba-4.6.5+dfsg/auth/kerberos/gssapi_pac.c --- samba-4.5.8+dfsg/auth/kerberos/gssapi_pac.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/auth/kerberos/gssapi_pac.c 2017-01-11 07:55:14.000000000 +0000 @@ -112,10 +112,11 @@ &pac_buffer, &pac_display_buffer, &more); if (gss_maj != 0) { + gss_OID oid = discard_const(gss_mech_krb5); DBG_NOTICE("obtaining PAC via GSSAPI gss_get_name_attribute " "failed: %s\n", gssapi_error_string(mem_ctx, gss_maj, gss_min, - gss_mech_krb5)); + oid)); return NT_STATUS_ACCESS_DENIED; } else if (authenticated && complete) { /* The PAC blob is returned directly */ diff -Nru samba-4.5.8+dfsg/auth/ntlmssp/gensec_ntlmssp_server.c samba-4.6.5+dfsg/auth/ntlmssp/gensec_ntlmssp_server.c --- samba-4.5.8+dfsg/auth/ntlmssp/gensec_ntlmssp_server.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/auth/ntlmssp/gensec_ntlmssp_server.c 2017-01-11 07:55:14.000000000 +0000 @@ -167,6 +167,14 @@ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY; } + /* + * We always allow NTLMSSP_NEGOTIATE_SIGN and NTLMSSP_NEGOTIATE_SEAL. + * + * These will be removed if the client doesn't want them. + */ + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL; + if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) { ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; } diff -Nru samba-4.5.8+dfsg/buildtools/wafsamba/samba_autoconf.py samba-4.6.5+dfsg/buildtools/wafsamba/samba_autoconf.py --- samba-4.5.8+dfsg/buildtools/wafsamba/samba_autoconf.py 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/buildtools/wafsamba/samba_autoconf.py 2017-01-11 07:55:14.000000000 +0000 @@ -708,6 +708,7 @@ testflags=True) conf.ADD_CFLAGS('-Wformat=2 -Wno-format-y2k', testflags=True) + conf.ADD_CFLAGS('-Werror=format-security -Wformat-security', testflags=True) # This check is because for ldb_search(), a NULL format string # is not an error, but some compilers complain about that. if CHECK_CFLAGS(conf, ["-Werror=format", "-Wformat=2"], ''' diff -Nru samba-4.5.8+dfsg/buildtools/wafsamba/samba_conftests.py samba-4.6.5+dfsg/buildtools/wafsamba/samba_conftests.py --- samba-4.5.8+dfsg/buildtools/wafsamba/samba_conftests.py 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/buildtools/wafsamba/samba_conftests.py 2017-01-11 07:55:14.000000000 +0000 @@ -286,7 +286,9 @@ os.makedirs(subdir) Utils.writef(os.path.join(subdir, 'lib1.c'), 'int lib_func(void) { return 42; }\n') - Utils.writef(os.path.join(dir, 'main.c'), 'int main(void) {return !(lib_func() == 42);}\n') + Utils.writef(os.path.join(dir, 'main.c'), + 'int lib_func(void);\n' + 'int main(void) {return !(lib_func() == 42);}\n') bld = Build.BuildContext() bld.log = conf.log @@ -436,6 +438,7 @@ ret = True for v in "sysname machine release version".split(): if not conf.CHECK_CODE(''' + int printf(const char *format, ...); struct utsname n; if (uname(&n) == -1) return -1; printf("%%s", n.%s); diff -Nru samba-4.5.8+dfsg/buildtools/wafsamba/samba_python.py samba-4.6.5+dfsg/buildtools/wafsamba/samba_python.py --- samba-4.5.8+dfsg/buildtools/wafsamba/samba_python.py 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/buildtools/wafsamba/samba_python.py 2017-05-23 08:19:23.000000000 +0000 @@ -76,6 +76,12 @@ else: conf.env['PYTHON_SO_ABI_FLAG'] = '' + for lib in conf.env['LINKFLAGS_PYEMBED']: + if lib.startswith('-L'): + conf.env.append_unique('LIBPATH_PYEMBED', lib[2:]) # strip '-L' + conf.env['LINKFLAGS_PYEMBED'].remove(lib) + + return def SAMBA_PYTHON(bld, name, source='', diff -Nru samba-4.5.8+dfsg/ctdb/client/client_control_sync.c samba-4.6.5+dfsg/ctdb/client/client_control_sync.c --- samba-4.5.8+dfsg/ctdb/client/client_control_sync.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/client/client_control_sync.c 2017-01-11 07:55:14.000000000 +0000 @@ -183,7 +183,7 @@ int ctdb_ctrl_getdebug(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct ctdb_client_context *client, int destnode, struct timeval timeout, - uint32_t *loglevel) + int *loglevel) { struct ctdb_req_control request; struct ctdb_reply_control *reply; @@ -212,7 +212,7 @@ int ctdb_ctrl_setdebug(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct ctdb_client_context *client, int destnode, struct timeval timeout, - uint32_t loglevel) + int loglevel) { struct ctdb_req_control request; struct ctdb_reply_control *reply; @@ -1437,35 +1437,6 @@ return 0; } -int ctdb_ctrl_run_eventscripts(TALLOC_CTX *mem_ctx, struct tevent_context *ev, - struct ctdb_client_context *client, - int destnode, struct timeval timeout, - const char *event) -{ - struct ctdb_req_control request; - struct ctdb_reply_control *reply; - int ret; - - ctdb_req_control_run_eventscripts(&request, event); - ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout, - &request, &reply); - if (ret != 0) { - DEBUG(DEBUG_ERR, - ("Control RUN_EVENTSCRIPTS failed to node %u, ret=%d\n", - destnode, ret)); - return ret; - } - - ret = ctdb_reply_control_run_eventscripts(reply); - if (ret != 0) { - DEBUG(DEBUG_ERR, - ("Control RUN_EVENTSCRIPTS failed, ret=%d\n", ret)); - return ret; - } - - return 0; -} - int ctdb_ctrl_get_capabilities(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct ctdb_client_context *client, int destnode, struct timeval timeout, @@ -1556,13 +1527,14 @@ int ctdb_ctrl_get_public_ips(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct ctdb_client_context *client, int destnode, struct timeval timeout, + bool available_only, struct ctdb_public_ip_list **pubip_list) { struct ctdb_req_control request; struct ctdb_reply_control *reply; int ret; - ctdb_req_control_get_public_ips(&request); + ctdb_req_control_get_public_ips(&request, available_only); ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout, &request, &reply); if (ret != 0) { @@ -1611,37 +1583,6 @@ return 0; } -int ctdb_ctrl_get_event_script_status(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct ctdb_client_context *client, - int destnode, struct timeval timeout, - enum ctdb_event event, - struct ctdb_script_list **slist) -{ - struct ctdb_req_control request; - struct ctdb_reply_control *reply; - int ret; - - ctdb_req_control_get_event_script_status(&request, event); - ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout, - &request, &reply); - if (ret != 0) { - DEBUG(DEBUG_ERR, - ("Control GET_EVENT_SCRIPT_STATUS failed to node %u, ret=%d\n", - destnode, ret)); - return ret; - } - - ret = ctdb_reply_control_get_event_script_status(reply, mem_ctx, slist); - if (ret != 0) { - DEBUG(DEBUG_ERR, - ("Control GET_EVENT_SCRIPT_STATUS failed, ret=%d\n", ret)); - return ret; - } - - return 0; -} - int ctdb_ctrl_traverse_kill(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct ctdb_client_context *client, int destnode, struct timeval timeout, @@ -1811,64 +1752,6 @@ return ret; } - return 0; -} - -int ctdb_ctrl_enable_script(TALLOC_CTX *mem_ctx, struct tevent_context *ev, - struct ctdb_client_context *client, - int destnode, struct timeval timeout, - const char *script) -{ - struct ctdb_req_control request; - struct ctdb_reply_control *reply; - int ret; - - ctdb_req_control_enable_script(&request, script); - ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout, - &request, &reply); - if (ret != 0) { - DEBUG(DEBUG_ERR, - ("Control ENABLE_SCRIPT failed to node %u, ret=%d\n", - destnode, ret)); - return ret; - } - - ret = ctdb_reply_control_enable_script(reply); - if (ret != 0) { - DEBUG(DEBUG_ERR, - ("Control ENABLE_SCRIPT failed, ret=%d\n", ret)); - return ret; - } - - return 0; -} - -int ctdb_ctrl_disable_script(TALLOC_CTX *mem_ctx, struct tevent_context *ev, - struct ctdb_client_context *client, - int destnode, struct timeval timeout, - const char *script) -{ - struct ctdb_req_control request; - struct ctdb_reply_control *reply; - int ret; - - ctdb_req_control_disable_script(&request, script); - ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout, - &request, &reply); - if (ret != 0) { - DEBUG(DEBUG_ERR, - ("Control DISABLE_SCRIPT failed to node %u, ret=%d\n", - destnode, ret)); - return ret; - } - - ret = ctdb_reply_control_disable_script(reply); - if (ret != 0) { - DEBUG(DEBUG_ERR, - ("Control DISABLE_SCRIPT failed, ret=%d\n", ret)); - return ret; - } - return 0; } diff -Nru samba-4.5.8+dfsg/ctdb/client/client_db.c samba-4.6.5+dfsg/ctdb/client/client_db.c --- samba-4.5.8+dfsg/ctdb/client/client_db.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/client/client_db.c 2017-01-11 07:55:14.000000000 +0000 @@ -1046,8 +1046,8 @@ int ctdb_store_record(struct ctdb_record_handle *h, TDB_DATA data) { - TDB_DATA rec; - size_t offset; + uint8_t header[sizeof(struct ctdb_ltdb_header)]; + TDB_DATA rec[2]; int ret; /* Cannot modify the record if it was obtained as a readonly copy */ @@ -1062,25 +1062,22 @@ return 0; } - offset = ctdb_ltdb_header_len(&h->header); - rec.dsize = offset + data.dsize; - rec.dptr = talloc_size(h, rec.dsize); - if (rec.dptr == NULL) { - return ENOMEM; - } + ctdb_ltdb_header_push(&h->header, header); - ctdb_ltdb_header_push(&h->header, rec.dptr); - memcpy(rec.dptr + offset, data.dptr, data.dsize); + rec[0].dsize = ctdb_ltdb_header_len(&h->header); + rec[0].dptr = header; - ret = tdb_store(h->db->ltdb->tdb, h->key, rec, TDB_REPLACE); + rec[1].dsize = data.dsize; + rec[1].dptr = data.dptr; + + ret = tdb_storev(h->db->ltdb->tdb, h->key, rec, 2, TDB_REPLACE); if (ret != 0) { DEBUG(DEBUG_ERR, - ("store_record: %s tdb_store failed, %s\n", + ("store_record: %s tdb_storev failed, %s\n", h->db->db_name, tdb_errorstr(h->db->ltdb->tdb))); return EIO; } - talloc_free(rec.dptr); return 0; } @@ -1098,6 +1095,7 @@ struct ctdb_delete_record_state *state; struct ctdb_key_data key; struct ctdb_req_control request; + uint8_t header[sizeof(struct ctdb_ltdb_header)]; TDB_DATA rec; int ret; @@ -1117,16 +1115,12 @@ return tevent_req_post(req, ev); } - rec.dsize = ctdb_ltdb_header_len(&h->header); - rec.dptr = talloc_size(h, rec.dsize); - if (tevent_req_nomem(rec.dptr, req)) { - return tevent_req_post(req, ev); - } + ctdb_ltdb_header_push(&h->header, header); - ctdb_ltdb_header_push(&h->header, rec.dptr); + rec.dsize = ctdb_ltdb_header_len(&h->header); + rec.dptr = header; ret = tdb_store(h->db->ltdb->tdb, h->key, rec, TDB_REPLACE); - talloc_free(rec.dptr); if (ret != 0) { DEBUG(DEBUG_ERR, ("fetch_lock delete: %s tdb_sore failed, %s\n", diff -Nru samba-4.5.8+dfsg/ctdb/client/client_event.c samba-4.6.5+dfsg/ctdb/client/client_event.c --- samba-4.5.8+dfsg/ctdb/client/client_event.c 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/client/client_event.c 2017-01-11 07:55:14.000000000 +0000 @@ -0,0 +1,597 @@ +/* + Eventd client api + + Copyright (C) Amitay Isaacs 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include "replace.h" +#include "system/filesys.h" +#include "system/network.h" + +#include +#include + +#include "lib/util/debug.h" +#include "lib/util/tevent_unix.h" + +#include "common/logging.h" +#include "common/reqid.h" +#include "common/comm.h" + +#include "protocol/protocol_api.h" + +#include "client/client.h" + +struct ctdb_event_context { + struct reqid_context *idr; + struct comm_context *comm; + int fd; + + ctdb_client_callback_func_t callback; + void *private_data; +}; + +static int ctdb_event_connect(struct ctdb_event_context *eclient, + struct tevent_context *ev, + const char *sockpath); + +static int ctdb_event_context_destructor(struct ctdb_event_context *eclient); + +int ctdb_event_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + const char *sockpath, struct ctdb_event_context **out) +{ + struct ctdb_event_context *eclient; + int ret; + + eclient = talloc_zero(mem_ctx, struct ctdb_event_context); + if (eclient == NULL) { + DEBUG(DEBUG_ERR, (__location__ " memory allocation error\n")); + return ENOMEM; + } + + ret = reqid_init(eclient, INT_MAX-200, &eclient->idr); + if (ret != 0) { + DEBUG(DEBUG_ERR, ("reqid_init() failed, ret=%d\n", ret)); + talloc_free(eclient); + return ret; + } + + eclient->fd = -1; + + ret = ctdb_event_connect(eclient, ev, sockpath); + if (ret != 0) { + talloc_free(eclient); + return ret; + } + + talloc_set_destructor(eclient, ctdb_event_context_destructor); + + *out = eclient; + return 0; +} + +static int ctdb_event_context_destructor(struct ctdb_event_context *eclient) +{ + if (eclient->fd != -1) { + close(eclient->fd); + eclient->fd = -1; + } + return 0; +} + +static void event_read_handler(uint8_t *buf, size_t buflen, + void *private_data); +static void event_dead_handler(void *private_data); + +static int ctdb_event_connect(struct ctdb_event_context *eclient, + struct tevent_context *ev, const char *sockpath) +{ + struct sockaddr_un addr; + size_t len; + int fd, ret; + + if (sockpath == NULL) { + DEBUG(DEBUG_ERR, ("socket path cannot be NULL\n")); + return EINVAL; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + len = strlcpy(addr.sun_path, sockpath, sizeof(addr.sun_path)); + if (len >= sizeof(addr.sun_path)) { + DEBUG(DEBUG_ERR, ("socket path too long, len=%zu\n", + strlen(sockpath))); + return ENAMETOOLONG; + } + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) { + ret = errno; + DEBUG(DEBUG_ERR, ("socket() failed, errno=%d\n", ret)); + return ret; + } + + ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (ret == -1) { + ret = errno; + DEBUG(DEBUG_ERR, ("connect() failed, errno=%d\n", ret)); + close(fd); + return ret; + } + eclient->fd = fd; + + ret = comm_setup(eclient, ev, fd, event_read_handler, eclient, + event_dead_handler, eclient, &eclient->comm); + if (ret != 0) { + DEBUG(DEBUG_ERR, ("comm_setup() failed, ret=%d\n", ret)); + close(fd); + eclient->fd = -1; + return ret; + } + + return 0; +} + +static void ctdb_event_msg_reply(struct ctdb_event_context *eclient, + uint8_t *buf, size_t buflen); + +static void event_read_handler(uint8_t *buf, size_t buflen, + void *private_data) +{ + struct ctdb_event_context *eclient = talloc_get_type_abort( + private_data, struct ctdb_event_context); + + ctdb_event_msg_reply(eclient, buf, buflen); +} + +static void event_dead_handler(void *private_data) +{ + struct ctdb_event_context *eclient = talloc_get_type_abort( + private_data, struct ctdb_event_context); + ctdb_client_callback_func_t callback = eclient->callback; + void *callback_data = eclient->private_data; + + talloc_free(eclient); + if (callback != NULL) { + callback(callback_data); + return; + } + + DEBUG(DEBUG_NOTICE, ("connection to daemon closed, exiting\n")); + exit(1); +} + +void ctdb_event_set_disconnect_callback(struct ctdb_event_context *eclient, + ctdb_client_callback_func_t callback, + void *private_data) +{ + eclient->callback = callback; + eclient->private_data = private_data; +} + +/* + * Handle eventd_request and eventd_reply + */ + +struct ctdb_event_msg_state { + struct ctdb_event_context *eclient; + + uint32_t reqid; + struct tevent_req *req; + struct ctdb_event_reply *reply; +}; + +static int ctdb_event_msg_state_destructor(struct ctdb_event_msg_state *state); +static void ctdb_event_msg_done(struct tevent_req *subreq); + +struct tevent_req *ctdb_event_msg_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ctdb_event_context *eclient, + struct ctdb_event_request *request) +{ + struct tevent_req *req, *subreq; + struct ctdb_event_msg_state *state; + uint8_t *buf; + size_t buflen; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct ctdb_event_msg_state); + if (req == NULL) { + return NULL; + } + + state->eclient = eclient; + + state->reqid = reqid_new(eclient->idr, state); + if (state->reqid == REQID_INVALID) { + talloc_free(req); + return NULL; + } + state->req = req; + + talloc_set_destructor(state, ctdb_event_msg_state_destructor); + + ctdb_event_header_fill(&request->header, state->reqid); + + buflen = ctdb_event_request_len(request); + buf = talloc_size(state, buflen); + if (tevent_req_nomem(buf, req)) { + return tevent_req_post(req, ev); + } + + ret = ctdb_event_request_push(request, buf, &buflen); + if (ret != 0) { + tevent_req_error(req, ret); + return tevent_req_post(req, ev); + } + + subreq = comm_write_send(state, ev, eclient->comm, buf, buflen); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, ctdb_event_msg_done, req); + + return req; +} + +static int ctdb_event_msg_state_destructor(struct ctdb_event_msg_state *state) +{ + reqid_remove(state->eclient->idr, state->reqid); + return 0; +} + +static void ctdb_event_msg_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + int ret; + bool status; + + status = comm_write_recv(subreq, &ret); + TALLOC_FREE(subreq); + if (! status) { + tevent_req_error(req, ret); + return; + } + + /* Wait for the reply or timeout */ +} + +static void ctdb_event_msg_reply(struct ctdb_event_context *eclient, + uint8_t *buf, size_t buflen) +{ + struct ctdb_event_reply *reply; + struct ctdb_event_msg_state *state; + int ret; + + reply = talloc_zero(eclient, struct ctdb_event_reply); + if (reply == NULL) { + D_WARNING("memory allocation error\n"); + return; + } + + ret = ctdb_event_reply_pull(buf, buflen, reply, reply); + if (ret != 0) { + D_WARNING("Invalid packet received, ret=%d\n", ret); + return; + } + + state = reqid_find(eclient->idr, reply->header.reqid, + struct ctdb_event_msg_state); + if (state == NULL) { + return; + } + + if (reply->header.reqid != state->reqid) { + return; + } + + state->reply = talloc_steal(state, reply); + tevent_req_done(state->req); +} + +bool ctdb_event_msg_recv(struct tevent_req *req, int *perr, + TALLOC_CTX *mem_ctx, + struct ctdb_event_reply **reply) +{ + struct ctdb_event_msg_state *state = tevent_req_data( + req, struct ctdb_event_msg_state); + int ret; + + if (tevent_req_is_unix_error(req, &ret)) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + if (reply != NULL) { + *reply = talloc_steal(mem_ctx, state->reply); + } + + return true; +} + +/* + * Run an event + */ + +struct tevent_req *ctdb_event_run_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ctdb_event_context *eclient, + enum ctdb_event event, + uint32_t timeout, const char *arg_str) +{ + struct ctdb_event_request request; + struct ctdb_event_request_run rdata; + + rdata.event = event; + rdata.timeout = timeout; + rdata.arg_str = arg_str; + + request.rdata.command = CTDB_EVENT_COMMAND_RUN; + request.rdata.data.run = &rdata; + + return ctdb_event_msg_send(mem_ctx, ev, eclient, &request); +} + +bool ctdb_event_run_recv(struct tevent_req *req, int *perr, int *result) +{ + struct ctdb_event_reply *reply; + int ret; + bool status; + + status = ctdb_event_msg_recv(req, &ret, req, &reply); + if (! status) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + if (reply->rdata.command != CTDB_EVENT_COMMAND_RUN) { + if (perr != NULL) { + *perr = EPROTO; + } + talloc_free(reply); + return false; + } + + if (result != NULL) { + *result = reply->rdata.result; + } + + talloc_free(reply); + return true; +} + +/* + * Get event status + */ + +struct tevent_req *ctdb_event_status_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ctdb_event_context *eclient, + enum ctdb_event event, + enum ctdb_event_status_state state) +{ + struct ctdb_event_request request; + struct ctdb_event_request_status rdata; + + rdata.event = event; + rdata.state = state; + + request.rdata.command = CTDB_EVENT_COMMAND_STATUS; + request.rdata.data.status = &rdata; + + return ctdb_event_msg_send(mem_ctx, ev, eclient, &request); +} + +bool ctdb_event_status_recv(struct tevent_req *req, int *perr, + int32_t *result, int *event_status, + TALLOC_CTX *mem_ctx, + struct ctdb_script_list **script_list) +{ + struct ctdb_event_reply *reply; + int ret; + bool status; + + status = ctdb_event_msg_recv(req, &ret, req, &reply); + if (! status) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + if (reply->rdata.command != CTDB_EVENT_COMMAND_STATUS) { + if (perr != NULL) { + *perr = EPROTO; + } + talloc_free(reply); + return false; + } + + if (result != NULL) { + *result = reply->rdata.result; + } + if (event_status != NULL) { + *event_status = reply->rdata.data.status->status; + } + if (script_list != NULL) { + *script_list = talloc_steal(mem_ctx, + reply->rdata.data.status->script_list); + } + + talloc_free(reply); + return true; +} + +/* + * Get script list + */ + +struct tevent_req *ctdb_event_script_list_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ctdb_event_context *eclient) +{ + struct ctdb_event_request request; + + request.rdata.command = CTDB_EVENT_COMMAND_SCRIPT_LIST; + + return ctdb_event_msg_send(mem_ctx, ev, eclient, &request); +} + +bool ctdb_event_script_list_recv(struct tevent_req *req, int *perr, + int32_t *result, TALLOC_CTX *mem_ctx, + struct ctdb_script_list **script_list) +{ + struct ctdb_event_reply *reply; + int ret; + bool status; + + status = ctdb_event_msg_recv(req, &ret, req, &reply); + if (! status) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + if (reply->rdata.command != CTDB_EVENT_COMMAND_SCRIPT_LIST) { + if (perr != NULL) { + *perr = EPROTO; + } + talloc_free(reply); + return false; + } + + if (result != NULL) { + *result = reply->rdata.result; + } + if (script_list != NULL) { + *script_list = talloc_steal(mem_ctx, + reply->rdata.data.script_list->script_list); + } + + talloc_free(reply); + return true; +} + +/* + * Enable a script + */ + +struct tevent_req *ctdb_event_script_enable_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ctdb_event_context *eclient, + const char *script_name) +{ + struct ctdb_event_request request; + struct ctdb_event_request_script_enable rdata; + + rdata.script_name = script_name; + + request.rdata.command = CTDB_EVENT_COMMAND_SCRIPT_ENABLE; + request.rdata.data.script_enable = &rdata; + + return ctdb_event_msg_send(mem_ctx, ev, eclient, &request); +} + +bool ctdb_event_script_enable_recv(struct tevent_req *req, int *perr, + int *result) +{ + struct ctdb_event_reply *reply; + int ret; + bool status; + + status = ctdb_event_msg_recv(req, &ret, req, &reply); + if (! status) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + if (reply->rdata.command != CTDB_EVENT_COMMAND_SCRIPT_ENABLE) { + if (perr != NULL) { + *perr = EPROTO; + } + talloc_free(reply); + return false; + } + + if (result != NULL) { + *result = reply->rdata.result; + } + + talloc_free(reply); + return true; +} + +/* + * Disable a script + */ + +struct tevent_req *ctdb_event_script_disable_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ctdb_event_context *eclient, + const char *script_name) +{ + struct ctdb_event_request request; + struct ctdb_event_request_script_disable rdata; + + rdata.script_name = script_name; + + request.rdata.command = CTDB_EVENT_COMMAND_SCRIPT_DISABLE; + request.rdata.data.script_disable = &rdata; + + return ctdb_event_msg_send(mem_ctx, ev, eclient, &request); +} + +bool ctdb_event_script_disable_recv(struct tevent_req *req, int *perr, + int *result) +{ + struct ctdb_event_reply *reply; + int ret; + bool status; + + status = ctdb_event_msg_recv(req, &ret, req, &reply); + if (! status) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + if (reply->rdata.command != CTDB_EVENT_COMMAND_SCRIPT_DISABLE) { + if (perr != NULL) { + *perr = EPROTO; + } + talloc_free(reply); + return false; + } + + if (result != NULL) { + *result = reply->rdata.result; + } + + talloc_free(reply); + return true; +} diff -Nru samba-4.5.8+dfsg/ctdb/client/client.h samba-4.6.5+dfsg/ctdb/client/client.h --- samba-4.5.8+dfsg/ctdb/client/client.h 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/client/client.h 2017-01-11 07:55:14.000000000 +0000 @@ -240,12 +240,12 @@ int ctdb_ctrl_getdebug(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct ctdb_client_context *client, int destnode, struct timeval timeout, - uint32_t *loglevel); + int *loglevel); int ctdb_ctrl_setdebug(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct ctdb_client_context *client, int destnode, struct timeval timeout, - uint32_t loglevel); + int loglevel); int ctdb_ctrl_get_dbmap(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct ctdb_client_context *client, @@ -452,11 +452,6 @@ int destnode, struct timeval timeout, struct ctdb_addr_info *addr_info); -int ctdb_ctrl_run_eventscripts(TALLOC_CTX *mem_ctx, struct tevent_context *ev, - struct ctdb_client_context *client, - int destnode, struct timeval timeout, - const char *event); - int ctdb_ctrl_get_capabilities(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct ctdb_client_context *client, int destnode, struct timeval timeout, @@ -475,6 +470,7 @@ int ctdb_ctrl_get_public_ips(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct ctdb_client_context *client, int destnode, struct timeval timeout, + bool available_only, struct ctdb_public_ip_list **pubip_list); int ctdb_ctrl_get_nodemap(TALLOC_CTX *mem_ctx, struct tevent_context *ev, @@ -482,13 +478,6 @@ int destnode, struct timeval timeout, struct ctdb_node_map **nodemap); -int ctdb_ctrl_get_event_script_status(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct ctdb_client_context *client, - int destnode, struct timeval timeout, - enum ctdb_event event, - struct ctdb_script_list **slist); - int ctdb_ctrl_traverse_kill(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct ctdb_client_context *client, int destnode, struct timeval timeout, @@ -517,16 +506,6 @@ int destnode, struct timeval timeout, uint32_t recmaster_role); -int ctdb_ctrl_enable_script(TALLOC_CTX *mem_ctx, struct tevent_context *ev, - struct ctdb_client_context *client, - int destnode, struct timeval timeout, - const char *script); - -int ctdb_ctrl_disable_script(TALLOC_CTX *mem_ctx, struct tevent_context *ev, - struct ctdb_client_context *client, - int destnode, struct timeval timeout, - const char *script); - int ctdb_ctrl_set_ban_state(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct ctdb_client_context *client, int destnode, struct timeval timeout, @@ -846,4 +825,70 @@ struct ctdb_client_context *client, struct ctdb_server_id *sid, bool *exists); +/* from client/client_event.c */ + +struct ctdb_event_context; + +int ctdb_event_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + const char *sockpath, struct ctdb_event_context **out); + +void ctdb_event_set_disconnect_callback(struct ctdb_event_context *eclient, + ctdb_client_callback_func_t callback, + void *private_data); + +struct tevent_req *ctdb_event_msg_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ctdb_event_context *eclient, + struct ctdb_event_request *request); + +bool ctdb_event_msg_recv(struct tevent_req *req, int *perr, + TALLOC_CTX *mem_ctx, + struct ctdb_event_reply **reply); + +struct tevent_req *ctdb_event_run_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ctdb_event_context *eclient, + enum ctdb_event event, + uint32_t timeout, const char *arg_str); + +bool ctdb_event_run_recv(struct tevent_req *req, int *perr, int32_t *result); + +struct tevent_req *ctdb_event_status_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ctdb_event_context *eclient, + enum ctdb_event event, + enum ctdb_event_status_state state); + +bool ctdb_event_status_recv(struct tevent_req *req, int *perr, + int32_t *result, int *event_result, + TALLOC_CTX *mem_ctx, + struct ctdb_script_list **script_list); + +struct tevent_req *ctdb_event_script_list_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ctdb_event_context *eclient); + +bool ctdb_event_script_list_recv(struct tevent_req *req, int *perr, + int32_t *result, TALLOC_CTX *mem_ctx, + struct ctdb_script_list **script_list); + +struct tevent_req *ctdb_event_script_enable_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ctdb_event_context *eclient, + const char *script_name); + +bool ctdb_event_script_enable_recv(struct tevent_req *req, int *perr, + int32_t *result); + +struct tevent_req *ctdb_event_script_disable_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ctdb_event_context *eclient, + const char *script_name); + +bool ctdb_event_script_disable_recv(struct tevent_req *req, int *perr, + int32_t *result); + #endif /* __CTDB_CLIENT_H__ */ diff -Nru samba-4.5.8+dfsg/ctdb/client/ctdb_client.c samba-4.6.5+dfsg/ctdb/client/ctdb_client.c --- samba-4.5.8+dfsg/ctdb/client/ctdb_client.c 2016-09-13 08:21:35.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/client/ctdb_client.c 2017-01-11 07:55:14.000000000 +0000 @@ -4178,41 +4178,6 @@ } /* - get the status of running the monitor eventscripts: NULL means never run. - */ -int ctdb_ctrl_getscriptstatus(struct ctdb_context *ctdb, - struct timeval timeout, uint32_t destnode, - TALLOC_CTX *mem_ctx, - enum ctdb_event type, - struct ctdb_script_list_old **scripts) -{ - int ret; - TDB_DATA outdata, indata; - int32_t res; - uint32_t uinttype = type; - - indata.dptr = (uint8_t *)&uinttype; - indata.dsize = sizeof(uinttype); - - ret = ctdb_control(ctdb, destnode, 0, - CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS, 0, indata, - mem_ctx, &outdata, &res, &timeout, NULL); - if (ret != 0 || res != 0) { - DEBUG(DEBUG_ERR,(__location__ " ctdb_control for getscriptstatus failed ret:%d res:%d\n", ret, res)); - return -1; - } - - if (outdata.dsize == 0) { - *scripts = NULL; - } else { - *scripts = (struct ctdb_script_list_old *)talloc_memdup(mem_ctx, outdata.dptr, outdata.dsize); - talloc_free(outdata.dptr); - } - - return 0; -} - -/* tell the main daemon how long it took to lock the reclock file */ int ctdb_ctrl_report_recd_lock_latency(struct ctdb_context *ctdb, struct timeval timeout, double latency) @@ -4343,51 +4308,6 @@ return 0; } -/* enable an eventscript - */ -int ctdb_ctrl_enablescript(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode, const char *script) -{ - int ret; - TDB_DATA data; - int32_t res; - - data.dsize = strlen(script) + 1; - data.dptr = discard_const(script); - - ret = ctdb_control(ctdb, destnode, 0, - CTDB_CONTROL_ENABLE_SCRIPT, 0, data, - NULL, NULL, &res, &timeout, NULL); - if (ret != 0 || res != 0) { - DEBUG(DEBUG_ERR,(__location__ " ctdb_control for enablescript failed\n")); - return -1; - } - - return 0; -} - -/* disable an eventscript - */ -int ctdb_ctrl_disablescript(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode, const char *script) -{ - int ret; - TDB_DATA data; - int32_t res; - - data.dsize = strlen(script) + 1; - data.dptr = discard_const(script); - - ret = ctdb_control(ctdb, destnode, 0, - CTDB_CONTROL_DISABLE_SCRIPT, 0, data, - NULL, NULL, &res, &timeout, NULL); - if (ret != 0 || res != 0) { - DEBUG(DEBUG_ERR,(__location__ " ctdb_control for disablescript failed\n")); - return -1; - } - - return 0; -} - - int ctdb_ctrl_set_ban(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode, struct ctdb_ban_state *bantime) { diff -Nru samba-4.5.8+dfsg/ctdb/common/cmdline.c samba-4.6.5+dfsg/ctdb/common/cmdline.c --- samba-4.5.8+dfsg/ctdb/common/cmdline.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/common/cmdline.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,181 +0,0 @@ -/* - common commandline code to ctdb test tools - - Copyright (C) Andrew Tridgell 2007 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . -*/ - -#include "replace.h" -#include "system/filesys.h" -#include "system/network.h" - -#include -#include -#include -#include - -#include "lib/util/debug.h" -#include "ctdb_private.h" -#include "ctdb_client.h" - -#include "common/rb_tree.h" -#include "common/common.h" -#include "common/logging.h" -#include "common/cmdline.h" - - - -/* Handle common command line options for ctdb test progs - */ - -static struct { - const char *socketname; - const char *debuglevel; - int torture; - const char *events; -} ctdb_cmdline = { - .torture = 0, - .debuglevel = "NOTICE", -}; - -enum {OPT_EVENTSYSTEM=1}; - -static void ctdb_cmdline_callback(poptContext con, - enum poptCallbackReason reason, - const struct poptOption *opt, - const char *arg, const void *data) -{ - switch (opt->val) { - case OPT_EVENTSYSTEM: - tevent_set_default_backend(arg); - break; - } -} - - -struct poptOption popt_ctdb_cmdline[] = { - { NULL, 0, POPT_ARG_CALLBACK, (void *)ctdb_cmdline_callback }, - { "socket", 0, POPT_ARG_STRING, &ctdb_cmdline.socketname, 0, "local socket name", "filename" }, - { "debug", 'd', POPT_ARG_STRING, &ctdb_cmdline.debuglevel, 0, "debug level"}, - { "torture", 0, POPT_ARG_NONE, &ctdb_cmdline.torture, 0, "enable nastiness in library", NULL }, - { "events", 0, POPT_ARG_STRING, NULL, OPT_EVENTSYSTEM, "event system", NULL }, - { NULL } -}; - - -/* - startup daemon side of ctdb according to command line options - */ -struct ctdb_context *ctdb_cmdline_init(struct tevent_context *ev) -{ - struct ctdb_context *ctdb; - enum debug_level log_level; - int ret; - - /* initialise ctdb */ - ctdb = ctdb_init(ev); - if (ctdb == NULL) { - printf("Failed to init ctdb\n"); - exit(1); - } - - if (ctdb_cmdline.torture) { - ctdb_set_flags(ctdb, CTDB_FLAG_TORTURE); - } - - /* command line specified a socket name */ - if (ctdb_cmdline.socketname != NULL) { - setenv("CTDB_SOCKET", ctdb_cmdline.socketname, 1); - ret = ctdb_set_socketname(ctdb, ctdb_cmdline.socketname); - if (ret == -1) { - printf("ctdb_set_socketname failed - %s\n", - ctdb_errstr(ctdb)); - exit(1); - } - } - - /* Set the debug level */ - if (debug_level_parse(ctdb_cmdline.debuglevel, &log_level)) { - DEBUGLEVEL = debug_level_to_int(log_level); - } else { - DEBUGLEVEL = debug_level_to_int(DEBUG_NOTICE); - } - - return ctdb; -} - - -/* - startup a client only ctdb context - */ -struct ctdb_context *ctdb_cmdline_client(struct tevent_context *ev, - struct timeval req_timeout) -{ - struct ctdb_context *ctdb; - enum debug_level log_level; - char *socket_name; - int ret; - - /* initialise ctdb */ - ctdb = ctdb_init(ev); - if (ctdb == NULL) { - fprintf(stderr, "Failed to init ctdb\n"); - exit(1); - } - - /* tell ctdb the socket address */ - socket_name = getenv("CTDB_SOCKET"); - if (socket_name != NULL) { - ret = ctdb_set_socketname(ctdb, socket_name); - if (ret == -1) { - printf("ctdb_set_socketname failed - %s\n", - ctdb_errstr(ctdb)); - exit(1); - } - } - - if (ctdb_cmdline.socketname != NULL) { - ret = ctdb_set_socketname(ctdb, ctdb_cmdline.socketname); - if (ret == -1) { - fprintf(stderr, "ctdb_set_socketname failed - %s\n", - ctdb_errstr(ctdb)); - exit(1); - } - } - - /* Set the debug level */ - if (debug_level_parse(ctdb_cmdline.debuglevel, &log_level)) { - DEBUGLEVEL = debug_level_to_int(log_level); - } else { - DEBUGLEVEL = debug_level_to_int(DEBUG_NOTICE); - } - - ret = ctdb_socket_connect(ctdb); - if (ret != 0) { - fprintf(stderr, __location__ " Failed to connect to daemon\n"); - talloc_free(ctdb); - return NULL; - } - - /* get our pnn */ - ctdb->pnn = ctdb_ctrl_getpnn(ctdb, req_timeout, CTDB_CURRENT_NODE); - if (ctdb->pnn == (uint32_t)-1) { - DEBUG(DEBUG_CRIT,(__location__ " Failed to get ctdb pnn\n")); - talloc_free(ctdb); - return NULL; - } - - return ctdb; -} diff -Nru samba-4.5.8+dfsg/ctdb/common/cmdline.h samba-4.6.5+dfsg/ctdb/common/cmdline.h --- samba-4.5.8+dfsg/ctdb/common/cmdline.h 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/common/cmdline.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -#ifndef CTDB_CMDLINE_H -#define CTDB_CMDLINE_H - -extern struct poptOption popt_ctdb_cmdline[]; - -#define POPT_CTDB_CMDLINE { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_ctdb_cmdline, 0, "Common ctdb options:", NULL }, - -struct ctdb_context *ctdb_cmdline_init(struct tevent_context *ev); - -struct ctdb_context *ctdb_cmdline_client(struct tevent_context *ev, - struct timeval req_timeout); - -#endif /* CTDB_CMDLINE_H */ diff -Nru samba-4.5.8+dfsg/ctdb/common/common.h samba-4.6.5+dfsg/ctdb/common/common.h --- samba-4.5.8+dfsg/ctdb/common/common.h 2016-09-13 08:21:35.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/common/common.h 2017-01-11 07:55:14.000000000 +0000 @@ -22,6 +22,9 @@ /* From common/ctdb_io.c */ +typedef void (*ctdb_queue_cb_fn_t)(uint8_t *data, size_t length, + void *private_data); + int ctdb_queue_length(struct ctdb_queue *queue); int ctdb_queue_send(struct ctdb_queue *queue, uint8_t *data, uint32_t length); @@ -104,7 +107,7 @@ struct ctdb_marshall_buffer *ctdb_marshall_add(TALLOC_CTX *mem_ctx, struct ctdb_marshall_buffer *m, - uint64_t db_id, + uint32_t db_id, uint32_t reqid, TDB_DATA key, struct ctdb_ltdb_header *header, diff -Nru samba-4.5.8+dfsg/ctdb/common/ctdb_io.c samba-4.6.5+dfsg/ctdb/common/ctdb_io.c --- samba-4.5.8+dfsg/ctdb/common/ctdb_io.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/common/ctdb_io.c 2017-01-11 07:55:14.000000000 +0000 @@ -30,11 +30,11 @@ #include "lib/util/dlinklist.h" #include "lib/util/debug.h" +#include "lib/util/sys_rw.h" #include "ctdb_private.h" #include "ctdb_client.h" -#include "common/system.h" #include "common/logging.h" #include "common/common.h" diff -Nru samba-4.5.8+dfsg/ctdb/common/ctdb_ltdb.c samba-4.6.5+dfsg/ctdb/common/ctdb_ltdb.c --- samba-4.5.8+dfsg/ctdb/common/ctdb_ltdb.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/common/ctdb_ltdb.c 2017-01-11 07:55:14.000000000 +0000 @@ -170,7 +170,8 @@ struct ctdb_ltdb_header *header, TDB_DATA data) { struct ctdb_context *ctdb = ctdb_db->ctdb; - TDB_DATA rec; + TDB_DATA rec[2]; + uint32_t hsize = sizeof(struct ctdb_ltdb_header); int ret; bool seqnum_suppressed = false; @@ -179,22 +180,27 @@ } if (ctdb->flags & CTDB_FLAG_TORTURE) { + TDB_DATA old; struct ctdb_ltdb_header *h2; - rec = tdb_fetch(ctdb_db->ltdb->tdb, key); - h2 = (struct ctdb_ltdb_header *)rec.dptr; - if (rec.dptr && rec.dsize >= sizeof(h2) && h2->rsn > header->rsn) { - DEBUG(DEBUG_CRIT,("RSN regression! %llu %llu\n", - (unsigned long long)h2->rsn, (unsigned long long)header->rsn)); + + old = tdb_fetch(ctdb_db->ltdb->tdb, key); + h2 = (struct ctdb_ltdb_header *)old.dptr; + if (old.dptr != NULL && old.dsize >= hsize && + h2->rsn > header->rsn) { + DEBUG(DEBUG_ERR, + ("RSN regression! %"PRIu64" %"PRIu64"\n", + h2->rsn, header->rsn)); + } + if (old.dptr != NULL) { + free(old.dptr); } - if (rec.dptr) free(rec.dptr); } - rec.dsize = sizeof(*header) + data.dsize; - rec.dptr = talloc_size(ctdb, rec.dsize); - CTDB_NO_MEMORY(ctdb, rec.dptr); + rec[0].dsize = hsize; + rec[0].dptr = (uint8_t *)header; - memcpy(rec.dptr, header, sizeof(*header)); - memcpy(rec.dptr + sizeof(*header), data.dptr, data.dsize); + rec[1].dsize = data.dsize; + rec[1].dptr = data.dptr; /* Databases with seqnum updates enabled only get their seqnum changes when/if we modify the data */ @@ -202,16 +208,16 @@ TDB_DATA old; old = tdb_fetch(ctdb_db->ltdb->tdb, key); - if ( (old.dsize == rec.dsize) - && !memcmp(old.dptr+sizeof(struct ctdb_ltdb_header), - rec.dptr+sizeof(struct ctdb_ltdb_header), - rec.dsize-sizeof(struct ctdb_ltdb_header)) ) { + if ((old.dsize == hsize + data.dsize) && + memcmp(old.dptr+hsize, data.dptr, data.dsize) == 0) { tdb_remove_flags(ctdb_db->ltdb->tdb, TDB_SEQNUM); seqnum_suppressed = true; } - if (old.dptr) free(old.dptr); + if (old.dptr != NULL) { + free(old.dptr); + } } - ret = tdb_store(ctdb_db->ltdb->tdb, key, rec, TDB_REPLACE); + ret = tdb_storev(ctdb_db->ltdb->tdb, key, rec, 2, TDB_REPLACE); if (ret != 0) { DEBUG(DEBUG_ERR, (__location__ " Failed to store dynamic data\n")); } @@ -219,8 +225,6 @@ tdb_add_flags(ctdb_db->ltdb->tdb, TDB_SEQNUM); } - talloc_free(rec.dptr); - return ret; } diff -Nru samba-4.5.8+dfsg/ctdb/common/ctdb_util.c samba-4.6.5+dfsg/ctdb/common/ctdb_util.c --- samba-4.5.8+dfsg/ctdb/common/ctdb_util.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/common/ctdb_util.c 2017-01-11 07:55:14.000000000 +0000 @@ -273,7 +273,7 @@ /* helper function for marshalling multiple records */ struct ctdb_marshall_buffer *ctdb_marshall_add(TALLOC_CTX *mem_ctx, struct ctdb_marshall_buffer *m, - uint64_t db_id, + uint32_t db_id, uint32_t reqid, TDB_DATA key, struct ctdb_ltdb_header *header, diff -Nru samba-4.5.8+dfsg/ctdb/common/logging.c samba-4.6.5+dfsg/ctdb/common/logging.c --- samba-4.5.8+dfsg/ctdb/common/logging.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/common/logging.c 2017-06-06 07:47:19.000000000 +0000 @@ -1,6 +1,8 @@ /* Logging utilities + Copyright (C) Andrew Tridgell 2008 + Copyright (C) Martin Schwenke 2014 Copyright (C) Amitay Isaacs 2015 This program is free software; you can redistribute it and/or modify @@ -17,23 +19,39 @@ along with this program; if not, see . */ -#include -#include +#include "replace.h" +#include "system/network.h" +#include "system/locale.h" +#include "system/time.h" +#include "system/filesys.h" +#include "system/syslog.h" + +#include "lib/util/time_basic.h" +#include "lib/util/sys_rw.h" +#include "lib/util/debug.h" +#include "lib/util/blocking.h" +#include "lib/util/samba_util.h" /* get_myname() */ #include "common/logging.h" struct { - enum debug_level log_level; + int log_level; const char *log_string; } log_string_map[] = { { DEBUG_ERR, "ERROR" }, { DEBUG_WARNING, "WARNING" }, + { 2, "WARNING" }, { DEBUG_NOTICE, "NOTICE" }, + { 4, "NOTICE" }, { DEBUG_INFO, "INFO" }, + { 6, "INFO" }, + { 7, "INFO" }, + { 8, "INFO" }, + { 9, "INFO" }, { DEBUG_DEBUG, "DEBUG" }, }; -bool debug_level_parse(const char *log_string, enum debug_level *log_level) +bool debug_level_parse(const char *log_string, int *log_level) { int i; @@ -45,7 +63,7 @@ int level = atoi(log_string); if (level >= 0 && level < ARRAY_SIZE(log_string_map)) { - *log_level = debug_level_from_int(level); + *log_level = level; return true; } return false; @@ -62,7 +80,7 @@ return false; } -const char *debug_level_to_string(enum debug_level log_level) +const char *debug_level_to_string(int log_level) { int i; @@ -74,10 +92,10 @@ return "UNKNOWN"; } -enum debug_level debug_level_from_string(const char *log_string) +int debug_level_from_string(const char *log_string) { bool found; - enum debug_level log_level; + int log_level; found = debug_level_parse(log_string, &log_level); if (found) { @@ -88,20 +106,447 @@ return DEBUG_ERR; } -int debug_level_to_int(enum debug_level log_level) +/* + * file logging backend + */ + +struct file_log_state { + const char *app_name; + int fd; + char buffer[1024]; +}; + +static void file_log(void *private_data, int level, const char *msg) +{ + struct file_log_state *state = talloc_get_type_abort( + private_data, struct file_log_state); + struct timeval tv; + struct timeval_buf tvbuf; + int ret; + + if (state->fd == STDERR_FILENO) { + ret = snprintf(state->buffer, sizeof(state->buffer), + "%s[%u]: %s\n", + state->app_name, (unsigned)getpid(), msg); + } else { + GetTimeOfDay(&tv); + timeval_str_buf(&tv, false, true, &tvbuf); + + ret = snprintf(state->buffer, sizeof(state->buffer), + "%s %s[%u]: %s\n", tvbuf.buf, + state->app_name, (unsigned)getpid(), msg); + } + if (ret < 0) { + return; + } + + state->buffer[sizeof(state->buffer)-1] = '\0'; + + sys_write_v(state->fd, state->buffer, strlen(state->buffer)); +} + +static int file_log_state_destructor(struct file_log_state *state) +{ + if (state->fd != -1 && state->fd != STDERR_FILENO) { + close(state->fd); + state->fd = -1; + } + return 0; +} + +static int file_log_setup(TALLOC_CTX *mem_ctx, const char *option, + const char *app_name) +{ + struct file_log_state *state; + + state = talloc_zero(mem_ctx, struct file_log_state); + if (state == NULL) { + return ENOMEM; + } + + state->app_name = app_name; + + if (option == NULL || strcmp(option, "-") == 0) { + int ret; + + state->fd = STDERR_FILENO; + ret = dup2(STDERR_FILENO, STDOUT_FILENO); + if (ret == -1) { + int save_errno = errno; + talloc_free(state); + return save_errno; + } + + } else { + state->fd = open(option, O_WRONLY|O_APPEND|O_CREAT, 0644); + if (state->fd == -1) { + int save_errno = errno; + talloc_free(state); + return save_errno; + } + + if (! set_close_on_exec(state->fd)) { + int save_errno = errno; + talloc_free(state); + return save_errno; + } + } + + talloc_set_destructor(state, file_log_state_destructor); + debug_set_callback(state, file_log); + + return 0; +} + +/* + * syslog logging backend + */ + +/* Copied from lib/util/debug.c */ +static int debug_level_to_priority(int level) +{ + /* + * map debug levels to syslog() priorities + */ + static const int priority_map[] = { + LOG_ERR, /* 0 */ + LOG_WARNING, /* 1 */ + LOG_NOTICE, /* 2 */ + LOG_NOTICE, /* 3 */ + LOG_NOTICE, /* 4 */ + LOG_NOTICE, /* 5 */ + LOG_INFO, /* 6 */ + LOG_INFO, /* 7 */ + LOG_INFO, /* 8 */ + LOG_INFO, /* 9 */ + }; + int priority; + + if( level >= ARRAY_SIZE(priority_map) || level < 0) + priority = LOG_DEBUG; + else + priority = priority_map[level]; + + return priority; +} + +struct syslog_log_state { + int fd; + const char *app_name; + const char *hostname; + int (*format)(int dbglevel, struct syslog_log_state *state, + const char *str, char *buf, int bsize); + /* RFC3164 says: The total length of the packet MUST be 1024 + bytes or less. */ + char buffer[1024]; +}; + +/* Format messages as per RFC3164 + * + * It appears that some syslog daemon implementations do not allow a + * hostname when messages are sent via a Unix domain socket, so omit + * it. Similarly, syslogd on FreeBSD does not understand the hostname + * part of the header, even when logging via UDP. Note that most + * implementations will log messages against "localhost" when logging + * via UDP. A timestamp could be sent but rsyslogd on Linux limits + * the timestamp logged to the precision that was received on + * /dev/log. It seems sane to send degenerate RFC3164 messages + * without a header at all, so that the daemon will generate high + * resolution timestamps if configured. + */ +static int format_rfc3164(int dbglevel, struct syslog_log_state *state, + const char *str, char *buf, int bsize) +{ + int pri; + int len; + + pri = LOG_DAEMON | debug_level_to_priority(dbglevel); + len = snprintf(buf, bsize, "<%d>%s[%u]: %s", + pri, state->app_name, getpid(), str); + buf[bsize-1] = '\0'; + len = MIN(len, bsize - 1); + + return len; +} + +/* Format messages as per RFC5424 + * + * <165>1 2003-08-24T05:14:15.000003-07:00 192.0.2.1 + * myproc 8710 - - %% It's time to make the do-nuts. + */ +static int format_rfc5424(int dbglevel, struct syslog_log_state *state, + const char *str, char *buf, int bsize) +{ + int pri; + struct timeval tv; + struct timeval_buf tvbuf; + int len, s; + + /* Header */ + pri = LOG_DAEMON | debug_level_to_priority(dbglevel); + GetTimeOfDay(&tv); + len = snprintf(buf, bsize, + "<%d>1 %s %s %s %u - - ", + pri, timeval_str_buf(&tv, true, true, &tvbuf), + state->hostname, state->app_name, getpid()); + /* A truncated header is not useful... */ + if (len >= bsize) { + return -1; + } + + /* Message */ + s = snprintf(&buf[len], bsize - len, "%s", str); + buf[bsize-1] = '\0'; + len = MIN(len + s, bsize - 1); + + return len; +} + +static void syslog_log(void *private_data, int level, const char *msg) +{ + syslog(debug_level_to_priority(level), "%s", msg); +} + +static void syslog_log_sock(void *private_data, int level, const char *msg) +{ + struct syslog_log_state *state = talloc_get_type_abort( + private_data, struct syslog_log_state); + int n; + + n = state->format(level, state, msg, state->buffer, + sizeof(state->buffer)); + if (n == -1) { + return; + } + + sys_write_v(state->fd, state->buffer, n); +} + +static int syslog_log_setup_syslog(TALLOC_CTX *mem_ctx, const char *app_name) { - return (int)log_level; + openlog(app_name, LOG_PID, LOG_DAEMON); + + debug_set_callback(NULL, syslog_log); + + return 0; +} + +static int syslog_log_state_destructor(struct syslog_log_state *state) +{ + if (state->fd != -1) { + close(state->fd); + state->fd = -1; + } + return 0; +} + +static int syslog_log_setup_common(TALLOC_CTX *mem_ctx, const char *app_name, + struct syslog_log_state **result) +{ + struct syslog_log_state *state; + + state = talloc_zero(mem_ctx, struct syslog_log_state); + if (state == NULL) { + return ENOMEM; + } + + state->fd = -1; + state->app_name = app_name; + talloc_set_destructor(state, syslog_log_state_destructor); + + *result = state; + return 0; } -enum debug_level debug_level_from_int(int level) +#ifdef _PATH_LOG +static int syslog_log_setup_nonblocking(TALLOC_CTX *mem_ctx, + const char *app_name) { - enum debug_level log_level; + struct syslog_log_state *state = NULL; + struct sockaddr_un dest; + int ret; + + ret = syslog_log_setup_common(mem_ctx, app_name, &state); + if (ret != 0) { + return ret; + } + + state->fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (state->fd == -1) { + int save_errno = errno; + talloc_free(state); + return save_errno; + } + + dest.sun_family = AF_UNIX; + strncpy(dest.sun_path, _PATH_LOG, sizeof(dest.sun_path)-1); + ret = connect(state->fd, + (struct sockaddr *)&dest, sizeof(dest)); + if (ret == -1) { + int save_errno = errno; + talloc_free(state); + return save_errno; + } + + ret = set_blocking(state->fd, false); + if (ret != 0) { + int save_errno = errno; + talloc_free(state); + return save_errno; + } + + if (! set_close_on_exec(state->fd)) { + int save_errno = errno; + talloc_free(state); + return save_errno; + } + + state->hostname = NULL; /* Make this explicit */ + state->format = format_rfc3164; + + debug_set_callback(state, syslog_log_sock); + + return 0; +} +#endif /* _PATH_LOG */ + +static int syslog_log_setup_udp(TALLOC_CTX *mem_ctx, const char *app_name, + bool rfc5424) +{ + struct syslog_log_state *state = NULL; + struct sockaddr_in dest; + int ret; + + ret = syslog_log_setup_common(mem_ctx, app_name, &state); + if (ret != 0) { + return ret; + } + + state->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (state->fd == -1) { + int save_errno = errno; + talloc_free(state); + return save_errno; + } - if (level >= 0 && level < ARRAY_SIZE(log_string_map)) { - log_level = log_string_map[level].log_level; + dest.sin_family = AF_INET; + dest.sin_port = htons(514); + dest.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + ret = connect(state->fd, + (struct sockaddr *)&dest, sizeof(dest)); + if (ret == -1) { + int save_errno = errno; + talloc_free(state); + return save_errno; + } + + if (! set_close_on_exec(state->fd)) { + int save_errno = errno; + talloc_free(state); + return save_errno; + } + + state->hostname = get_myname(state); + if (state->hostname == NULL) { + /* Use a fallback instead of failing initialisation */ + state->hostname = "localhost"; + } + if (rfc5424) { + state->format = format_rfc5424; } else { - log_level = DEBUG_ERR; + state->format = format_rfc3164; + } + + debug_set_callback(state, syslog_log_sock); + + return 0; +} + +static int syslog_log_setup(TALLOC_CTX *mem_ctx, const char *option, + const char *app_name) +{ + if (option == NULL) { + return syslog_log_setup_syslog(mem_ctx, app_name); +#ifdef _PATH_LOG + } else if (strcmp(option, "nonblocking") == 0) { + return syslog_log_setup_nonblocking(mem_ctx, app_name); +#endif + } else if (strcmp(option, "udp") == 0) { + return syslog_log_setup_udp(mem_ctx, app_name, false); + } else if (strcmp(option, "udp-rfc5424") == 0) { + return syslog_log_setup_udp(mem_ctx, app_name, true); + } + + return EINVAL; +} + +/* Initialise logging */ +int logging_init(TALLOC_CTX *mem_ctx, const char *logging, + const char *debug_level, const char *app_name) +{ + struct { + const char *name; + int (*setup)(TALLOC_CTX *mem_ctx, const char *option, + const char *app_name); + } log_backend[] = { + { + .name = "file", + .setup = file_log_setup, + }, + { + .name = "syslog", + .setup = syslog_log_setup, + }, + }; + int (*setup)(TALLOC_CTX *, const char *, const char *) = NULL; + char *str, *name, *option; + int ret, i; + + setup_logging(app_name, DEBUG_STDERR); + + if (debug_level == NULL) { + debug_level = getenv("CTDB_DEBUGLEVEL"); + } + if (! debug_level_parse(debug_level, &DEBUGLEVEL)) { + return EINVAL; + } + + if (logging == NULL) { + logging = getenv("CTDB_LOGGING"); + } + if (logging == NULL || logging[0] == '\0') { + return EINVAL; + } + + str = talloc_strdup(mem_ctx, logging); + if (str == NULL) { + return ENOMEM; + } + + name = strtok(str, ":"); + if (name == NULL) { + talloc_free(str); + return EINVAL; + } + option = strtok(NULL, ":"); + /* + * option can be NULL here, both setup() + * backends handle this. + */ + + for (i=0; i +#include "lib/util/debug.h" + +#define DEBUG_ERR DBGLVL_ERR +#define DEBUG_WARNING DBGLVL_WARNING +#define DEBUG_NOTICE DBGLVL_NOTICE +#define DEBUG_INFO DBGLVL_INFO +#define DEBUG_DEBUG DBGLVL_DEBUG /* These are used in many places, so define them here to avoid churn */ #define DEBUG_ALERT DEBUG_ERR #define DEBUG_CRIT DEBUG_ERR -bool debug_level_parse(const char *log_string, enum debug_level *log_level); -const char *debug_level_to_string(enum debug_level log_level); -enum debug_level debug_level_from_string(const char *log_string); -int debug_level_to_int(enum debug_level log_level); -enum debug_level debug_level_from_int(int log_int); +bool debug_level_parse(const char *log_string, int *log_level); +const char *debug_level_to_string(int log_level); +int debug_level_from_string(const char *log_string); + +int logging_init(TALLOC_CTX *mem_ctx, const char *logging, + const char *debuglevel, const char *app_name); #endif /* __CTDB_LOGGING_H__ */ diff -Nru samba-4.5.8+dfsg/ctdb/common/run_proc.c samba-4.6.5+dfsg/ctdb/common/run_proc.c --- samba-4.5.8+dfsg/ctdb/common/run_proc.c 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/common/run_proc.c 2017-02-14 12:29:05.000000000 +0000 @@ -0,0 +1,528 @@ +/* + Run a child process and collect the output + + Copyright (C) Amitay Isaacs 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include "replace.h" +#include "system/filesys.h" +#include "system/wait.h" + +#include +#include + +#include "lib/util/tevent_unix.h" +#include "lib/util/sys_rw.h" +#include "lib/util/blocking.h" + +#include "common/db_hash.h" +#include "common/run_proc.h" + +/* + * Process abstraction + */ + +struct proc_context { + pid_t pid; + + int fd; + struct tevent_fd *fde; + + char *output; + struct run_proc_result result; + + struct tevent_req *req; +}; + +static struct proc_context *proc_new(TALLOC_CTX *mem_ctx) +{ + struct proc_context *proc; + + proc = talloc_zero(mem_ctx, struct proc_context); + if (proc == NULL) { + return NULL; + } + + proc->pid = -1; + proc->fd = -1; + + return proc; +} + +static void proc_read_handler(struct tevent_context *ev, + struct tevent_fd *fde, uint16_t flags, + void *private_data); + +static int proc_start(struct proc_context *proc, struct tevent_context *ev, + const char *path, const char **argv) +{ + int fd[2]; + int ret; + + ret = pipe(fd); + if (ret != 0) { + return ret; + } + + proc->pid = fork(); + if (proc->pid == -1) { + ret = errno; + close(fd[0]); + close(fd[1]); + return ret; + } + + if (proc->pid == 0) { + close(fd[0]); + + ret = dup2(fd[1], STDOUT_FILENO); + if (ret == -1) { + exit(64 + errno); + } + ret = dup2(fd[1], STDERR_FILENO); + if (ret == -1) { + exit(64 + errno); + } + + close(fd[1]); + + ret = setpgid(0, 0); + if (ret != 0) { + exit(64 + errno); + } + + ret = execv(path, discard_const(argv)); + if (ret != 0) { + exit(64 + errno); + } + + exit(64 + ENOEXEC); + } + + close(fd[1]); + + proc->fd = fd[0]; + proc->fde = tevent_add_fd(ev, proc, fd[0], TEVENT_FD_READ, + proc_read_handler, proc); + if (proc->fde == NULL) { + return ENOMEM; + } + + tevent_fd_set_auto_close(proc->fde); + + return 0; +} + +static void proc_read_handler(struct tevent_context *ev, + struct tevent_fd *fde, uint16_t flags, + void *private_data) +{ + struct proc_context *proc = talloc_get_type_abort( + private_data, struct proc_context); + size_t offset; + ssize_t nread; + int len = 0; + int ret; + + ret = ioctl(proc->fd, FIONREAD, &len); + if (ret != 0) { + goto fail; + } + + if (len == 0) { + /* pipe closed */ + goto close; + } + + offset = (proc->output == NULL) ? 0 : strlen(proc->output); + + proc->output = talloc_realloc(proc, proc->output, char, offset+len+1); + if (proc->output == NULL) { + goto fail; + } + + nread = sys_read(proc->fd, proc->output + offset, len); + if (nread == -1) { + goto fail; + } + proc->output[offset+nread] = '\0'; + return; + +fail: + kill(-proc->pid, SIGKILL); +close: + TALLOC_FREE(proc->fde); + proc->fd = -1; +} + +/* + * Processes database + */ + +static int proc_db_init(TALLOC_CTX *mem_ctx, struct db_hash_context **result) +{ + struct db_hash_context *pdb = NULL; + int ret; + + ret = db_hash_init(pdb, "proc_db", 1001, DB_HASH_COMPLEX, &pdb); + if (ret != 0) { + return ret; + } + + *result = pdb; + return 0; +} + +static int proc_db_add(struct db_hash_context *pdb, pid_t pid, + struct proc_context *proc) +{ + return db_hash_insert(pdb, (uint8_t *)&pid, sizeof(pid_t), + (uint8_t *)&proc, sizeof(struct proc_context *)); +} + +static int proc_db_remove(struct db_hash_context *pdb, pid_t pid) +{ + return db_hash_delete(pdb, (uint8_t *)&pid, sizeof(pid_t)); +} + +static int proc_db_fetch_parser(uint8_t *keybuf, size_t keylen, + uint8_t *databuf, size_t datalen, + void *private_data) +{ + struct proc_context **result = (struct proc_context **)private_data; + + if (datalen != sizeof(struct proc_context *)) { + return EINVAL; + } + + *result = *(struct proc_context **)databuf; + return 0; +} + +static int proc_db_fetch(struct db_hash_context *pdb, pid_t pid, + struct proc_context **result) +{ + return db_hash_fetch(pdb, (uint8_t *)&pid, sizeof(pid_t), + proc_db_fetch_parser, result); +} + +static int proc_db_killall_parser(uint8_t *keybuf, size_t keylen, + uint8_t *databuf, size_t datalen, + void *private_data) +{ + struct db_hash_context *pdb = talloc_get_type_abort( + private_data, struct db_hash_context); + struct proc_context *proc; + pid_t pid; + + if (keylen != sizeof(pid_t) || + datalen != sizeof(struct proc_context *)) { + /* skip */ + return 0; + } + + pid = *(pid_t *)keybuf; + proc = talloc_steal(pdb, *(struct proc_context **)databuf); + + TALLOC_FREE(proc->req); + TALLOC_FREE(proc->fde); + + kill(-pid, SIGKILL); + return 0; +} + +static void proc_db_killall(struct db_hash_context *pdb) +{ + (void) db_hash_traverse(pdb, proc_db_killall_parser, pdb, NULL); +} + + +/* + * Run proc abstraction + */ + +struct run_proc_context { + struct tevent_context *ev; + struct tevent_signal *se; + struct db_hash_context *pdb; +}; + +static void run_proc_signal_handler(struct tevent_context *ev, + struct tevent_signal *se, + int signum, int count, void *siginfo, + void *private_data); +static int run_proc_context_destructor(struct run_proc_context *run_ctx); +static void run_proc_done(struct tevent_req *req); + +int run_proc_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct run_proc_context **result) +{ + struct run_proc_context *run_ctx; + int ret; + + run_ctx = talloc_zero(mem_ctx, struct run_proc_context); + if (run_ctx == NULL) { + return ENOMEM; + } + + run_ctx->ev = ev; + run_ctx->se = tevent_add_signal(ev, run_ctx, SIGCHLD, 0, + run_proc_signal_handler, run_ctx); + if (run_ctx->se == NULL) { + talloc_free(run_ctx); + return ENOMEM; + } + + ret = proc_db_init(run_ctx, &run_ctx->pdb); + if (ret != 0) { + talloc_free(run_ctx); + return ret; + } + + talloc_set_destructor(run_ctx, run_proc_context_destructor); + + *result = run_ctx; + return 0; +} + +static void run_proc_signal_handler(struct tevent_context *ev, + struct tevent_signal *se, + int signum, int count, void *siginfo, + void *private_data) +{ + struct run_proc_context *run_ctx = talloc_get_type_abort( + private_data, struct run_proc_context); + struct proc_context *proc; + pid_t pid = -1; + int ret, status; + +again: + pid = waitpid(-1, &status, WNOHANG); + if (pid == -1) { + return; + } + + if (pid == 0) { + return; + } + + ret = proc_db_fetch(run_ctx->pdb, pid, &proc); + if (ret != 0) { + /* unknown process */ + return; + } + + /* Mark the process as terminated */ + proc->pid = -1; + + /* Update process status */ + if (WIFEXITED(status)) { + int pstatus = WEXITSTATUS(status); + if (WIFSIGNALED(status)) { + proc->result.sig = WTERMSIG(status); + } else if (pstatus >= 64 && pstatus < 255) { + proc->result.err = pstatus-64; + } else { + proc->result.status = pstatus; + } + } else if (WIFSIGNALED(status)) { + proc->result.sig = WTERMSIG(status); + } + + /* Active run_proc request */ + if (proc->req != NULL) { + run_proc_done(proc->req); + } + + proc_db_remove(run_ctx->pdb, pid); + talloc_free(proc); + + goto again; + +} + +static int run_proc_context_destructor(struct run_proc_context *run_ctx) +{ + /* Get rid of signal handler */ + TALLOC_FREE(run_ctx->se); + + /* Kill any pending processes */ + proc_db_killall(run_ctx->pdb); + TALLOC_FREE(run_ctx->pdb); + + return 0; +} + +struct run_proc_state { + struct tevent_context *ev; + struct proc_context *proc; + + struct run_proc_result result; + char *output; + pid_t pid; +}; + +static int run_proc_state_destructor(struct run_proc_state *state); +static void run_proc_timedout(struct tevent_req *subreq); + +struct tevent_req *run_proc_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct run_proc_context *run_ctx, + const char *path, const char **argv, + struct timeval timeout) +{ + struct tevent_req *req; + struct run_proc_state *state; + struct stat st; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct run_proc_state); + if (req == NULL) { + return NULL; + } + + state->ev = ev; + state->pid = -1; + + ret = stat(path, &st); + if (ret != 0) { + state->result.err = errno; + tevent_req_done(req); + return tevent_req_post(req, ev); + } + + if (! (st.st_mode & S_IXUSR)) { + state->result.err = EACCES; + tevent_req_done(req); + return tevent_req_post(req, ev); + } + + state->proc = proc_new(run_ctx); + if (tevent_req_nomem(state->proc, req)) { + return tevent_req_post(req, ev); + } + + ret = proc_start(state->proc, ev, path, argv); + if (ret != 0) { + tevent_req_error(req, ret); + return tevent_req_post(req, ev); + } + + state->proc->req = req; + talloc_set_destructor(state, run_proc_state_destructor); + + ret = proc_db_add(run_ctx->pdb, state->proc->pid, state->proc); + if (ret != 0) { + tevent_req_error(req, ret); + return tevent_req_post(req, ev); + } + + if (! tevent_timeval_is_zero(&timeout)) { + struct tevent_req *subreq; + + subreq = tevent_wakeup_send(state, ev, timeout); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, run_proc_timedout, req); + } + + return req; +} + +static int run_proc_state_destructor(struct run_proc_state *state) +{ + /* Do not get rid of the child process if timeout has occurred */ + if (state->proc->req != NULL) { + state->proc->req = NULL; + if (state->proc->pid != -1) { + kill(-state->proc->pid, SIGTERM); + } + } + + return 0; +} + +static void run_proc_done(struct tevent_req *req) +{ + struct run_proc_state *state = tevent_req_data( + req, struct run_proc_state); + + state->proc->req = NULL; + + state->result = state->proc->result; + if (state->proc->output != NULL) { + state->output = talloc_steal(state, state->proc->output); + } + + tevent_req_done(req); +} + +static void run_proc_timedout(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct run_proc_state *state = tevent_req_data( + req, struct run_proc_state); + bool status; + + state->proc->req = NULL; + + status = tevent_wakeup_recv(subreq); + TALLOC_FREE(subreq); + if (! status) { + tevent_req_error(req, EIO); + return; + } + + state->result.err = ETIME; + if (state->proc->output != NULL) { + state->output = talloc_steal(state, state->proc->output); + } + state->pid = state->proc->pid; + + tevent_req_done(req); +} + +bool run_proc_recv(struct tevent_req *req, int *perr, + struct run_proc_result *result, pid_t *pid, + TALLOC_CTX *mem_ctx, char **output) +{ + struct run_proc_state *state = tevent_req_data( + req, struct run_proc_state); + int ret; + + if (tevent_req_is_unix_error(req, &ret)) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + if (result != NULL) { + *result = state->result; + } + + if (pid != NULL) { + *pid = state->pid; + } + + if (output != NULL) { + *output = talloc_steal(mem_ctx, state->output); + } + + return true; +} diff -Nru samba-4.5.8+dfsg/ctdb/common/run_proc.h samba-4.6.5+dfsg/ctdb/common/run_proc.h --- samba-4.5.8+dfsg/ctdb/common/run_proc.h 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/common/run_proc.h 2017-01-11 07:55:14.000000000 +0000 @@ -0,0 +1,99 @@ +/* + Run a child process and collect the output + + Copyright (C) Amitay Isaacs 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#ifndef __CTDB_RUN_PROC_H__ +#define __CTDB_RUN_PROC_H__ + +#include +#include + +/** + * @file run_proc.h + * + * @brief Run a process and capture the output + * + * This abstraction allows to execute scripts with argumunts. + */ + +/** + * @brief The run process context + */ +struct run_proc_context; + +/** + * @brief The exit status structure + * + * If the process is terminated due to a signal, sig is set. + * If the process is terminated due to an error, err is set. + * If the process terminates normally, status is set. + */ +struct run_proc_result { + int sig; + int err; + int status; +}; + +/** + * @brief Initialize the context for running processes + * + * @param[in] mem_ctx Talloc memory context + * @param[in] ev Tevent context + * @param[out] result New run_proc context + * @return 0 on success, errno on error + */ +int run_proc_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct run_proc_context **result); + +/** + * @brief Async computation start to run an executable + * + * @param[in] mem_ctx Talloc memory context + * @param[in] ev Tevent context + * @param[in] run_ctx Run_proc context + * @param[in] prog The path to the executable + * @param[in] argv Arguments to the executable + * @param[in] timeout How long to wait for execution + * @return new tevent request, or NULL on failure + * + * argv must include program name as argv[0] and must be null terminated. + */ +struct tevent_req *run_proc_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct run_proc_context *run_ctx, + const char *prog, const char **argv, + struct timeval timeout); + +/** + * @brief Async computation end to run an executable + * + * @param[in] req Tevent request + * @param[out] perr errno in case of failure + * @param[out] result The exit status of the executable + * @param[out] pid The pid of the child process (still running) + * @param[in] mem_ctx Talloc memory context + * @param[out] output The output from the executable (stdio + stderr) + * @return true on success, false on failure + * + * The returned pid is -1 if the process has terminated. + */ +bool run_proc_recv(struct tevent_req *req, int *perr, + struct run_proc_result *result, pid_t *pid, + TALLOC_CTX *mem_ctx, char **output); + +#endif /* __CTDB_RUN_PROC_H__ */ diff -Nru samba-4.5.8+dfsg/ctdb/common/sock_daemon.c samba-4.6.5+dfsg/ctdb/common/sock_daemon.c --- samba-4.5.8+dfsg/ctdb/common/sock_daemon.c 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/common/sock_daemon.c 2017-01-26 12:19:08.000000000 +0000 @@ -0,0 +1,803 @@ +/* + A server based on unix domain socket + + Copyright (C) Amitay Isaacs 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include "replace.h" +#include "system/filesys.h" +#include "system/network.h" +#include "system/wait.h" + +#include +#include + +#include "lib/async_req/async_sock.h" +#include "lib/util/debug.h" +#include "lib/util/blocking.h" +#include "lib/util/dlinklist.h" +#include "lib/util/tevent_unix.h" + +#include "common/logging.h" +#include "common/reqid.h" +#include "common/comm.h" +#include "common/pidfile.h" +#include "common/sock_daemon.h" + +struct sock_socket { + struct sock_socket *prev, *next; + + const char *sockpath; + struct sock_socket_funcs *funcs; + void *private_data; + + int fd; + struct tevent_req *req; +}; + +struct sock_client { + struct sock_client *prev, *next; + + struct tevent_req *req; + struct sock_client_context *client_ctx; +}; + +struct sock_client_context { + struct tevent_context *ev; + struct sock_socket *sock; + int fd; + struct comm_context *comm; + + struct sock_client *client; +}; + +struct sock_daemon_context { + struct sock_daemon_funcs *funcs; + void *private_data; + + struct pidfile_context *pid_ctx; + struct sock_socket *socket_list; +}; + +/* + * Process a single client + */ + +static void sock_client_read_handler(uint8_t *buf, size_t buflen, + void *private_data); +static void sock_client_read_done(struct tevent_req *subreq); +static void sock_client_dead_handler(void *private_data); +static int sock_client_context_destructor( + struct sock_client_context *client_ctx); + +static int sock_client_context_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sock_socket *sock, + int client_fd, + struct sock_client *client, + struct sock_client_context **result) +{ + struct sock_client_context *client_ctx; + int ret; + + client_ctx = talloc_zero(mem_ctx, struct sock_client_context); + if (client_ctx == NULL) { + return ENOMEM; + } + + client_ctx->ev = ev; + client_ctx->sock = sock; + client_ctx->fd = client_fd; + client_ctx->client = client; + + ret = comm_setup(client_ctx, ev, client_fd, + sock_client_read_handler, client_ctx, + sock_client_dead_handler, client_ctx, + &client_ctx->comm); + if (ret != 0) { + talloc_free(client_ctx); + return ret; + } + + if (sock->funcs->connect != NULL) { + bool status; + + status = sock->funcs->connect(client_ctx, sock->private_data); + if (! status) { + talloc_free(client_ctx); + close(client_fd); + return 0; + } + } + + talloc_set_destructor(client_ctx, sock_client_context_destructor); + + *result = client_ctx; + return 0; +} + +static void sock_client_read_handler(uint8_t *buf, size_t buflen, + void *private_data) +{ + struct sock_client_context *client_ctx = talloc_get_type_abort( + private_data, struct sock_client_context); + struct sock_socket *sock = client_ctx->sock; + struct tevent_req *subreq; + + subreq = sock->funcs->read_send(client_ctx, client_ctx->ev, + client_ctx, buf, buflen, + sock->private_data); + if (subreq == NULL) { + talloc_free(client_ctx); + return; + } + tevent_req_set_callback(subreq, sock_client_read_done, client_ctx); +} + +static void sock_client_read_done(struct tevent_req *subreq) +{ + struct sock_client_context *client_ctx = tevent_req_callback_data( + subreq, struct sock_client_context); + struct sock_socket *sock = client_ctx->sock; + int ret; + bool status; + + status = sock->funcs->read_recv(subreq, &ret); + if (! status) { + D_ERR("client read failed with ret=%d\n", ret); + talloc_free(client_ctx); + } +} + +static void sock_client_dead_handler(void *private_data) +{ + struct sock_client_context *client_ctx = talloc_get_type_abort( + private_data, struct sock_client_context); + struct sock_socket *sock = client_ctx->sock; + + if (sock->funcs->disconnect != NULL) { + sock->funcs->disconnect(client_ctx, sock->private_data); + } + + talloc_free(client_ctx); +} + +static int sock_client_context_destructor( + struct sock_client_context *client_ctx) +{ + TALLOC_FREE(client_ctx->client); + TALLOC_FREE(client_ctx->comm); + if (client_ctx->fd != -1) { + close(client_ctx->fd); + client_ctx->fd = -1; + } + + return 0; +} + +/* + * Process a single listening socket + */ + +static int socket_setup(const char *sockpath, bool remove_before_use) +{ + struct sockaddr_un addr; + size_t len; + int ret, fd; + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + + len = strlcpy(addr.sun_path, sockpath, sizeof(addr.sun_path)); + if (len >= sizeof(addr.sun_path)) { + D_ERR("socket path too long: %s\n", sockpath); + return -1; + } + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) { + D_ERR("socket create failed - %s\n", sockpath); + return -1; + } + + ret = set_blocking(fd, false); + if (ret != 0) { + D_ERR("socket set nonblocking failed - %s\n", sockpath); + close(fd); + return -1; + } + + if (remove_before_use) { + unlink(sockpath); + } + + ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (ret != 0) { + D_ERR("socket bind failed - %s\n", sockpath); + close(fd); + return -1; + } + + ret = listen(fd, 10); + if (ret != 0) { + D_ERR("socket listen failed - %s\n", sockpath); + close(fd); + return -1; + } + + return fd; +} + +static int sock_socket_destructor(struct sock_socket *sock); + +static int sock_socket_init(TALLOC_CTX *mem_ctx, const char *sockpath, + struct sock_socket_funcs *funcs, + void *private_data, + bool remove_before_use, + struct sock_socket **result) +{ + struct sock_socket *sock; + + if (funcs == NULL) { + return EINVAL; + } + if (funcs->read_send == NULL || funcs->read_recv == NULL) { + return EINVAL; + } + + sock = talloc_zero(mem_ctx, struct sock_socket); + if (sock == NULL) { + return ENOMEM; + } + + sock->sockpath = sockpath; + sock->funcs = funcs; + sock->private_data = private_data; + + sock->fd = socket_setup(sockpath, remove_before_use); + if (sock->fd == -1) { + talloc_free(sock); + return EIO; + } + + talloc_set_destructor(sock, sock_socket_destructor); + + *result = sock; + return 0; +} + +static int sock_socket_destructor(struct sock_socket *sock) +{ + if (sock->fd != -1) { + close(sock->fd); + sock->fd = -1; + } + + unlink(sock->sockpath); + return 0; +} + + +struct sock_socket_start_state { + struct tevent_context *ev; + struct sock_socket *sock; + + struct sock_client *client_list; +}; + +static int sock_socket_start_state_destructor( + struct sock_socket_start_state *state); +static void sock_socket_start_new_client(struct tevent_req *subreq); +static int sock_socket_start_client_destructor(struct sock_client *client); + +static struct tevent_req *sock_socket_start_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sock_socket *sock) +{ + struct tevent_req *req, *subreq; + struct sock_socket_start_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct sock_socket_start_state); + if (req == NULL) { + return NULL; + } + + state->ev = ev; + state->sock = sock; + + talloc_set_destructor(state, sock_socket_start_state_destructor); + + subreq = accept_send(state, ev, sock->fd); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, sock_socket_start_new_client, req); + + return req; +} + +static int sock_socket_start_state_destructor( + struct sock_socket_start_state *state) +{ + struct sock_client *client; + + while ((client = state->client_list) != NULL) { + talloc_free(client); + } + + return 0; +} + +static void sock_socket_start_new_client(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct sock_socket_start_state *state = tevent_req_data( + req, struct sock_socket_start_state); + struct sock_client *client; + int client_fd, ret; + + client_fd = accept_recv(subreq, NULL, NULL, &ret); + TALLOC_FREE(subreq); + if (client_fd == -1) { + D_ERR("failed to accept new connection\n"); + } + + subreq = accept_send(state, state->ev, state->sock->fd); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, sock_socket_start_new_client, req); + + if (client_fd == -1) { + return; + } + + client = talloc_zero(state, struct sock_client); + if (tevent_req_nomem(client, req)) { + close(client_fd); + return; + } + + client->req = req; + + ret = sock_client_context_init(client, state->ev, state->sock, + client_fd, client, &client->client_ctx); + if (ret != 0) { + talloc_free(client); + return; + } + + talloc_set_destructor(client, sock_socket_start_client_destructor); + DLIST_ADD(state->client_list, client); +} + +static int sock_socket_start_client_destructor(struct sock_client *client) +{ + struct sock_socket_start_state *state = tevent_req_data( + client->req, struct sock_socket_start_state); + + DLIST_REMOVE(state->client_list, client); + TALLOC_FREE(client->client_ctx); + + return 0; +} + +static bool sock_socket_start_recv(struct tevent_req *req, int *perr) +{ + struct sock_socket_start_state *state = tevent_req_data( + req, struct sock_socket_start_state); + int ret; + + state->sock->req = NULL; + + if (tevent_req_is_unix_error(req, &ret)) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + return true; +} + +/* + * Send message to a client + */ + +struct tevent_req *sock_socket_write_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sock_client_context *client_ctx, + uint8_t *buf, size_t buflen) +{ + struct tevent_req *req; + + req = comm_write_send(mem_ctx, ev, client_ctx->comm, buf, buflen); + + return req; +} + +bool sock_socket_write_recv(struct tevent_req *req, int *perr) +{ + int ret; + bool status; + + status = comm_write_recv(req, &ret); + if (! status) { + if (perr != NULL) { + *perr = ret; + } + } + + return status; +} + +/* + * Socket daemon + */ + +int sock_daemon_setup(TALLOC_CTX *mem_ctx, const char *daemon_name, + const char *logging, const char *debug_level, + const char *pidfile, + struct sock_daemon_funcs *funcs, + void *private_data, + struct sock_daemon_context **out) +{ + struct sock_daemon_context *sockd; + int ret; + + sockd = talloc_zero(mem_ctx, struct sock_daemon_context); + if (sockd == NULL) { + return ENOMEM; + } + + sockd->funcs = funcs; + sockd->private_data = private_data; + + ret = logging_init(sockd, logging, debug_level, daemon_name); + if (ret != 0) { + fprintf(stderr, + "Failed to initialize logging, logging=%s, debug=%s\n", + logging, debug_level); + return ret; + } + + if (pidfile != NULL) { + ret = pidfile_create(sockd, pidfile, &sockd->pid_ctx); + if (ret != 0) { + talloc_free(sockd); + return EEXIST; + } + } + + *out = sockd; + return 0; +} + +int sock_daemon_add_unix(struct sock_daemon_context *sockd, + const char *sockpath, + struct sock_socket_funcs *funcs, + void *private_data) +{ + struct sock_socket *sock; + int ret; + bool remove_before_use = false; + + remove_before_use = (sockd->pid_ctx != NULL) ? true : false; + + ret = sock_socket_init(sockd, sockpath, funcs, private_data, + remove_before_use, &sock); + if (ret != 0) { + return ret; + } + + D_NOTICE("listening on %s\n", sockpath); + + DLIST_ADD(sockd->socket_list, sock); + return 0; +} + +/* + * Run socket daemon + */ + +struct sock_daemon_run_state { + struct tevent_context *ev; + struct sock_daemon_context *sockd; + pid_t pid_watch; + + int fd; +}; + +static void sock_daemon_run_started(struct tevent_req *subreq); +static void sock_daemon_run_signal_handler(struct tevent_context *ev, + struct tevent_signal *se, + int signum, int count, void *siginfo, + void *private_data); +static void sock_daemon_run_reconfigure(struct tevent_req *req); +static void sock_daemon_run_shutdown(struct tevent_req *req); +static void sock_daemon_run_socket_fail(struct tevent_req *subreq); +static void sock_daemon_run_watch_pid(struct tevent_req *subreq); +static void sock_daemon_run_wait_done(struct tevent_req *subreq); + +struct tevent_req *sock_daemon_run_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sock_daemon_context *sockd, + pid_t pid_watch) +{ + struct tevent_req *req, *subreq; + struct sock_daemon_run_state *state; + struct tevent_signal *se; + struct sock_socket *sock; + + req = tevent_req_create(mem_ctx, &state, + struct sock_daemon_run_state); + if (req == NULL) { + return NULL; + } + + state->ev = ev; + state->sockd = sockd; + state->pid_watch = pid_watch; + state->fd = -1; + + subreq = tevent_wakeup_send(state, ev, + tevent_timeval_current_ofs(0, 0)); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, sock_daemon_run_started, req); + + se = tevent_add_signal(ev, state, SIGHUP, 0, + sock_daemon_run_signal_handler, req); + if (tevent_req_nomem(se, req)) { + return tevent_req_post(req, ev); + } + + se = tevent_add_signal(ev, state, SIGUSR1, 0, + sock_daemon_run_signal_handler, req); + if (tevent_req_nomem(se, req)) { + return tevent_req_post(req, ev); + } + + se = tevent_add_signal(ev, state, SIGINT, 0, + sock_daemon_run_signal_handler, req); + if (tevent_req_nomem(se, req)) { + return tevent_req_post(req, ev); + } + + se = tevent_add_signal(ev, state, SIGTERM, 0, + sock_daemon_run_signal_handler, req); + if (tevent_req_nomem(se, req)) { + return tevent_req_post(req, ev); + } + + for (sock = sockd->socket_list; sock != NULL; sock = sock->next) { + subreq = sock_socket_start_send(state, ev, sock); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, sock_daemon_run_socket_fail, + req); + + sock->req = subreq; + } + + if (pid_watch > 1) { + subreq = tevent_wakeup_send(state, ev, + tevent_timeval_current_ofs(1,0)); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, sock_daemon_run_watch_pid, + req); + } + + if (sockd->funcs != NULL && sockd->funcs->wait_send != NULL && + sockd->funcs->wait_recv != NULL) { + subreq = sockd->funcs->wait_send(state, ev, + sockd->private_data); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, sock_daemon_run_wait_done, + req); + } + + return req; +} + +static void sock_daemon_run_started(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct sock_daemon_run_state *state = tevent_req_data( + req, struct sock_daemon_run_state); + struct sock_daemon_context *sockd = state->sockd; + + D_NOTICE("daemon started, pid=%u\n", getpid()); + + if (sockd->funcs != NULL && sockd->funcs->startup != NULL) { + sockd->funcs->startup(sockd->private_data); + } +} + +static void sock_daemon_run_signal_handler(struct tevent_context *ev, + struct tevent_signal *se, + int signum, int count, void *siginfo, + void *private_data) +{ + struct tevent_req *req = talloc_get_type_abort( + private_data, struct tevent_req); + + D_NOTICE("Received signal %d\n", signum); + + if (signum == SIGHUP || signum == SIGUSR1) { + sock_daemon_run_reconfigure(req); + return; + } + + if (signum == SIGINT || signum == SIGTERM) { + sock_daemon_run_shutdown(req); + tevent_req_error(req, EINTR); + } +} + +static void sock_daemon_run_reconfigure(struct tevent_req *req) +{ + struct sock_daemon_run_state *state = tevent_req_data( + req, struct sock_daemon_run_state); + struct sock_daemon_context *sockd = state->sockd; + + if (sockd->funcs != NULL && sockd->funcs->reconfigure != NULL) { + sockd->funcs->reconfigure(sockd->private_data); + } +} + +static void sock_daemon_run_shutdown(struct tevent_req *req) +{ + struct sock_daemon_run_state *state = tevent_req_data( + req, struct sock_daemon_run_state); + struct sock_daemon_context *sockd = state->sockd; + struct sock_socket *sock; + + D_NOTICE("Shutting down\n"); + + while ((sock = sockd->socket_list) != NULL) { + DLIST_REMOVE(sockd->socket_list, sock); + TALLOC_FREE(sock->req); + TALLOC_FREE(sock); + } + + if (sockd->funcs != NULL && sockd->funcs->shutdown != NULL) { + sockd->funcs->shutdown(sockd->private_data); + } + + TALLOC_FREE(sockd->pid_ctx); +} + +static void sock_daemon_run_socket_fail(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + int ret = 0; + bool status; + + status = sock_socket_start_recv(subreq, &ret); + TALLOC_FREE(subreq); + sock_daemon_run_shutdown(req); + if (! status) { + tevent_req_error(req, ret); + } else { + tevent_req_done(req); + } +} + +static void sock_daemon_run_watch_pid(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct sock_daemon_run_state *state = tevent_req_data( + req, struct sock_daemon_run_state); + int ret; + bool status; + + status = tevent_wakeup_recv(subreq); + TALLOC_FREE(subreq); + if (! status) { + tevent_req_error(req, EIO); + return; + } + + ret = kill(state->pid_watch, 0); + if (ret == -1) { + if (errno == ESRCH) { + D_ERR("PID %d gone away, exiting\n", state->pid_watch); + sock_daemon_run_shutdown(req); + tevent_req_error(req, ESRCH); + return; + } else { + D_ERR("Failed to check PID status %d, ret=%d\n", + state->pid_watch, errno); + } + } + + subreq = tevent_wakeup_send(state, state->ev, + tevent_timeval_current_ofs(5,0)); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, sock_daemon_run_watch_pid, req); +} + +static void sock_daemon_run_wait_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct sock_daemon_run_state *state = tevent_req_data( + req, struct sock_daemon_run_state); + struct sock_daemon_context *sockd = state->sockd; + int ret; + bool status; + + status = sockd->funcs->wait_recv(subreq, &ret); + TALLOC_FREE(subreq); + sock_daemon_run_shutdown(req); + if (! status) { + tevent_req_error(req, ret); + } else { + tevent_req_done(req); + } +} + +bool sock_daemon_run_recv(struct tevent_req *req, int *perr) +{ + int ret; + + if (tevent_req_is_unix_error(req, &ret)) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + return true; +} + +int sock_daemon_run(struct tevent_context *ev, + struct sock_daemon_context *sockd, + pid_t pid_watch) +{ + struct tevent_req *req; + int ret; + bool status; + + req = sock_daemon_run_send(ev, ev, sockd, pid_watch); + if (req == NULL) { + return ENOMEM; + } + + tevent_req_poll(req, ev); + + status = sock_daemon_run_recv(req, &ret); + TALLOC_FREE(req); + if (! status) { + return ret; + } + + return 0; +} diff -Nru samba-4.5.8+dfsg/ctdb/common/sock_daemon.h samba-4.6.5+dfsg/ctdb/common/sock_daemon.h --- samba-4.5.8+dfsg/ctdb/common/sock_daemon.h 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/common/sock_daemon.h 2017-01-26 12:19:08.000000000 +0000 @@ -0,0 +1,216 @@ +/* + A server based on unix domain socket + + Copyright (C) Amitay Isaacs 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#ifndef __CTDB_SOCK_DAEMON_H__ +#define __CTDB_SOCK_DAEMON_H__ + +#include +#include + +#include "common/logging.h" + +/** + * @file sock_daemon.h + * + * @brief A framework for a server based on unix-domain sockets. + * + * This abstraction allows to build simple servers that communicate using + * unix-domain sockets. It takes care of the common boilerplate. + */ + +/** + * @brief The abstract socket daemon context + */ +struct sock_daemon_context; + +/** + * @brief The abstract socket client context + */ +struct sock_client_context; + +/** + * @brief The callback routines called during daemon life cycle + * + * startup() is called when the daemon starts running + * either via sock_daemon_run() or via sock_daemon_run_send() + * reconfigure() is called when process receives SIGUSR1 or SIGHUP + * shutdown() is called when process receives SIGINT or SIGTERM or + * when wait computation has finished + * + * wait_send() starts the async computation to keep running the daemon + * wait_recv() ends the async computation to keep running the daemon + * + * If wait_send()/wait_recv() is NULL, then daemon will keep running forever. + * If wait_send() returns req, then when req is over, daemon will shutdown. + */ +struct sock_daemon_funcs { + void (*startup)(void *private_data); + void (*reconfigure)(void *private_data); + void (*shutdown)(void *private_data); + + struct tevent_req * (*wait_send)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + void *private_data); + bool (*wait_recv)(struct tevent_req *req, int *perr); +}; + +/** + * @brief The callback routines called for an unix-domain socket + * + * connect() is called when there is a new connection + * + * @param[in] client The new socket client context + * @param[in] private_data Private data set with the socket + * @retun true if connection should be accepted, false otherwise + * + * + * disconnect() is called when client closes connection + * + * @param[in] client The socket client context + * @param[in] private_data Private data associated with the socket + * + * + * read_send() starts the async computation to process data on the socket + * + * @param[in] mem_ctx Talloc memory context + * @param[in] ev Tevent context + * @param[in] client The socket client context + * @param[in] buf Data received from the client + * @param[in] buflen Length of the data + * @param[i] private_data Private data associatedwith the socket + * @return new tevent reques, or NULL on failure + * + * + * read_recv() ends the async computation to process data on the socket + * + * @param[in] req Tevent request + * @param[out] perr errno in case of failure + * @return true on success, false on failure + * + */ +struct sock_socket_funcs { + bool (*connect)(struct sock_client_context *client, + void *private_data); + void (*disconnect)(struct sock_client_context *client, + void *private_data); + + struct tevent_req * (*read_send)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sock_client_context *client, + uint8_t *buf, size_t buflen, + void *private_data); + bool (*read_recv)(struct tevent_req *req, int *perr); +}; + +/** + * @brief Async computation to send data to the client + * + * @param[in] mem_ctx Talloc memory context + * @param[in] ev Tevent context + * @param[in] client The socket client context + * @param[in] buf Data to be sent to the client + * @param[in] buflen Length of the data + * @return new tevent request, or NULL on failure + */ +struct tevent_req *sock_socket_write_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sock_client_context *client, + uint8_t *buf, size_t buflen); + +/** + * @brief Async computation end to send data to client + * + * @param[in] req Tevent request + * @param[out] perr errno in case of failure + * @return true on success, false on failure + */ +bool sock_socket_write_recv(struct tevent_req *req, int *perr); + +/** + * @brief Create a new socket daemon + * + * @param[in] mem_ctx Talloc memory context + * @param[in] daemon_name Name of the daemon, used for logging + * @param[in] logging Logging setup string + * @param[in] debug_level Debug level to log at + * @param[in] pidfile PID file to create, NULL if no PID file required + * @param[in] funcs Socket daemon callback routines + * @param[in] private_data Private data associated with callback routines + * @param[out] result New socket daemon context + * @return 0 on success, errno on failure + */ +int sock_daemon_setup(TALLOC_CTX *mem_ctx, const char *daemon_name, + const char *logging, const char *debug_level, + const char *pidfile, + struct sock_daemon_funcs *funcs, + void *private_data, + struct sock_daemon_context **result); + +/** + * @brief Create and listen to the unix domain socket + * + * @param[in] sockd Socket daemon context + * @param[in] sockpath Unix domain socket path + * @param[in] funcs socket callback routines + * @param[in] private_data Private data associated with callback routines + * @return 0 on success, errno on failure + */ +int sock_daemon_add_unix(struct sock_daemon_context *sockd, + const char *sockpath, + struct sock_socket_funcs *funcs, + void *private_data); + +/** + * @brief Async computation start to run a socket daemon + * + * @param[in] mem_ctx Talloc memory context + * @param[in] ev Tevent context + * @param[in] sockd The socket daemon context + * @param[in] pid_watch PID to watch. If PID goes away, shutdown. + * @return new tevent request, NULL on failure + */ +struct tevent_req *sock_daemon_run_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sock_daemon_context *sockd, + pid_t pid_watch); + +/** + * @brief Async computation end to run a socket daemon + * + * @param[in] req Tevent request + * @param[out] perr errno in case of failure + * @return true on success, false on failure + */ +bool sock_daemon_run_recv(struct tevent_req *req, int *perr); + +/** + * @brief Sync way to start a daemon + * + * @param[in] ev Tevent context + * @param[in] sockd The socket daemon context + * @param[in] pid_watch PID to watch. If PID goes away, shutdown. + * @return 0 on success, errno on failure + * + * This call will return only on shutdown of the daemon + */ +int sock_daemon_run(struct tevent_context *ev, + struct sock_daemon_context *sockd, + pid_t pid_watch); + +#endif /* __CTDB_SOCK_DAEMON_H__ */ diff -Nru samba-4.5.8+dfsg/ctdb/common/sock_io.c samba-4.6.5+dfsg/ctdb/common/sock_io.c --- samba-4.5.8+dfsg/ctdb/common/sock_io.c 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/common/sock_io.c 2017-01-26 12:18:16.000000000 +0000 @@ -0,0 +1,310 @@ +/* + Generic Unix-domain Socket I/O + + Copyright (C) Amitay Isaacs 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include "replace.h" +#include "system/filesys.h" +#include "system/network.h" + +#include +#include + +#include "lib/util/sys_rw.h" +#include "lib/util/debug.h" +#include "lib/util/blocking.h" + +#include "common/logging.h" +#include "common/sock_io.h" + +int sock_connect(const char *sockpath) +{ + struct sockaddr_un addr; + size_t len; + int fd, ret; + + if (sockpath == NULL) { + D_ERR("Invalid socket path\n"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + len = strlcpy(addr.sun_path, sockpath, sizeof(addr.sun_path)); + if (len >= sizeof(addr.sun_path)) { + D_ERR("Socket path too long, len=%zu\n", strlen(sockpath)); + return -1; + } + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) { + D_ERR("socket() failed, errno=%d\n", errno); + return -1; + } + + ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (ret == -1) { + D_ERR("connect() failed, errno=%d\n", errno); + close(fd); + return -1; + } + + return fd; +} + +struct sock_queue { + struct tevent_context *ev; + sock_queue_callback_fn_t callback; + void *private_data; + int fd; + + struct tevent_immediate *im; + struct tevent_queue *queue; + struct tevent_fd *fde; + uint8_t *buf; + size_t buflen, begin, end; +}; + +static bool sock_queue_set_fd(struct sock_queue *queue, int fd); +static int sock_queue_destructor(struct sock_queue *queue); +static void sock_queue_handler(struct tevent_context *ev, + struct tevent_fd *fde, uint16_t flags, + void *private_data); +static void sock_queue_process(struct sock_queue *queue); +static void sock_queue_process_event(struct tevent_context *ev, + struct tevent_immediate *im, + void *private_data); + +struct sock_queue *sock_queue_setup(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + int fd, + sock_queue_callback_fn_t callback, + void *private_data) +{ + struct sock_queue *queue; + + queue = talloc_zero(mem_ctx, struct sock_queue); + if (queue == NULL) { + return NULL; + } + + queue->ev = ev; + queue->callback = callback; + queue->private_data = private_data; + + queue->im = tevent_create_immediate(queue); + if (queue->im == NULL) { + talloc_free(queue); + return NULL; + } + + queue->queue = tevent_queue_create(queue, "out-queue"); + if (queue->queue == NULL) { + talloc_free(queue); + return NULL; + } + + if (! sock_queue_set_fd(queue, fd)) { + talloc_free(queue); + return NULL; + } + + talloc_set_destructor(queue, sock_queue_destructor); + + return queue; +} + +static bool sock_queue_set_fd(struct sock_queue *queue, int fd) +{ + TALLOC_FREE(queue->fde); + queue->fd = fd; + + if (fd != -1) { + int ret; + + ret = set_blocking(fd, false); + if (ret != 0) { + return false; + } + + queue->fde = tevent_add_fd(queue->ev, queue, fd, + TEVENT_FD_READ, + sock_queue_handler, queue); + if (queue->fde == NULL) { + return false; + } + tevent_fd_set_auto_close(queue->fde); + } + + return true; +} + +static int sock_queue_destructor(struct sock_queue *queue) +{ + TALLOC_FREE(queue->fde); + queue->fd = -1; + + return 0; +} + +static void sock_queue_handler(struct tevent_context *ev, + struct tevent_fd *fde, uint16_t flags, + void *private_data) +{ + struct sock_queue *queue = talloc_get_type_abort( + private_data, struct sock_queue); + int ret, num_ready; + ssize_t nread; + + ret = ioctl(queue->fd, FIONREAD, &num_ready); + if (ret != 0) { + /* Ignore */ + return; + } + + if (num_ready == 0) { + /* descriptor has been closed */ + goto fail; + } + + if (num_ready > queue->buflen - queue->end) { + queue->buf = talloc_realloc_size(queue, queue->buf, + queue->end + num_ready); + if (queue->buf == NULL) { + goto fail; + } + queue->buflen = queue->end + num_ready; + } + + nread = sys_read(queue->fd, queue->buf + queue->end, num_ready); + if (nread < 0) { + goto fail; + } + queue->end += nread; + + sock_queue_process(queue); + return; + +fail: + queue->callback(NULL, 0, queue->private_data); +} + +static void sock_queue_process(struct sock_queue *queue) +{ + uint32_t pkt_size; + + if ((queue->end - queue->begin) < sizeof(uint32_t)) { + /* not enough data */ + return; + } + + pkt_size = *(uint32_t *)(queue->buf + queue->begin); + if (pkt_size == 0) { + D_ERR("Invalid packet of length 0\n"); + queue->callback(NULL, 0, queue->private_data); + } + + if ((queue->end - queue->begin) < pkt_size) { + /* not enough data */ + return; + } + + queue->callback(queue->buf + queue->begin, pkt_size, + queue->private_data); + queue->begin += pkt_size; + + if (queue->begin < queue->end) { + /* more data to be processed */ + tevent_schedule_immediate(queue->im, queue->ev, + sock_queue_process_event, queue); + } else { + TALLOC_FREE(queue->buf); + queue->buflen = 0; + queue->begin = 0; + queue->end = 0; + } +} + +static void sock_queue_process_event(struct tevent_context *ev, + struct tevent_immediate *im, + void *private_data) +{ + struct sock_queue *queue = talloc_get_type_abort( + private_data, struct sock_queue); + + sock_queue_process(queue); +} + +struct sock_queue_write_state { + uint8_t *pkt; + uint32_t pkt_size; +}; + +static void sock_queue_trigger(struct tevent_req *req, void *private_data); + +int sock_queue_write(struct sock_queue *queue, uint8_t *buf, size_t buflen) +{ + struct tevent_req *req; + struct sock_queue_write_state *state; + bool status; + + if (buflen >= INT32_MAX) { + return -1; + } + + req = tevent_req_create(queue, &state, struct sock_queue_write_state); + if (req == NULL) { + return -1; + } + + state->pkt = buf; + state->pkt_size = (uint32_t)buflen; + + status = tevent_queue_add_entry(queue->queue, queue->ev, req, + sock_queue_trigger, queue); + if (! status) { + talloc_free(req); + return -1; + } + + return 0; +} + +static void sock_queue_trigger(struct tevent_req *req, void *private_data) +{ + struct sock_queue *queue = talloc_get_type_abort( + private_data, struct sock_queue); + struct sock_queue_write_state *state = tevent_req_data( + req, struct sock_queue_write_state); + size_t offset = 0; + + do { + ssize_t nwritten; + + nwritten = sys_write(queue->fd, state->pkt + offset, + state->pkt_size - offset); + if (nwritten < 0) { + queue->callback(NULL, 0, queue->private_data); + return; + } + offset += nwritten; + + } while (offset < state->pkt_size); + + tevent_req_done(req); + talloc_free(req); +} diff -Nru samba-4.5.8+dfsg/ctdb/common/sock_io.h samba-4.6.5+dfsg/ctdb/common/sock_io.h --- samba-4.5.8+dfsg/ctdb/common/sock_io.h 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/common/sock_io.h 2017-01-11 07:55:14.000000000 +0000 @@ -0,0 +1,38 @@ +/* + Generic Socket I/O + + Copyright (C) Amitay Isaacs 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#ifndef __CTDB_SOCK_IO_H__ +#define __CTDB_SOCK_IO_H__ + +typedef void (*sock_queue_callback_fn_t)(uint8_t *buf, size_t buflen, + void *private_data); + +struct sock_queue; + +int sock_connect(const char *sockpath); + +struct sock_queue *sock_queue_setup(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + int fd, + sock_queue_callback_fn_t callback, + void *private_data); + +int sock_queue_write(struct sock_queue *queue, uint8_t *buf, size_t buflen); + +#endif /* __CTDB_SOCK_IO_H__ */ diff -Nru samba-4.5.8+dfsg/ctdb/common/system.h samba-4.6.5+dfsg/ctdb/common/system.h --- samba-4.5.8+dfsg/ctdb/common/system.h 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/common/system.h 2017-01-11 07:55:14.000000000 +0000 @@ -60,9 +60,6 @@ int mkdir_p(const char *dir, int mode); void mkdir_p_or_die(const char *dir, int mode); -ssize_t sys_read(int fd, void *buf, size_t count); -ssize_t sys_write(int fd, const void *buf, size_t count); - void ctdb_wait_for_process_to_exit(pid_t pid); int ctdb_parse_connections(FILE *fp, TALLOC_CTX *mem_ctx, diff -Nru samba-4.5.8+dfsg/ctdb/common/system_util.c samba-4.6.5+dfsg/ctdb/common/system_util.c --- samba-4.5.8+dfsg/ctdb/common/system_util.c 2016-12-05 08:18:44.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/common/system_util.c 2017-01-11 07:55:14.000000000 +0000 @@ -349,40 +349,6 @@ } } -/* A read wrapper that will deal with EINTR. For now, copied from - * source3/lib/system.c - */ -ssize_t sys_read(int fd, void *buf, size_t count) -{ - ssize_t ret; - - do { - ret = read(fd, buf, count); -#if defined(EWOULDBLOCK) - } while (ret == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)); -#else - } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); -#endif - return ret; -} - -/* A write wrapper that will deal with EINTR. For now, copied from - * source3/lib/system.c - */ -ssize_t sys_write(int fd, const void *buf, size_t count) -{ - ssize_t ret; - - do { - ret = write(fd, buf, count); -#if defined(EWOULDBLOCK) - } while (ret == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)); -#else - } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); -#endif - return ret; -} - void ctdb_wait_for_process_to_exit(pid_t pid) { while (kill(pid, 0) == 0 || errno != ESRCH) { diff -Nru samba-4.5.8+dfsg/ctdb/common/tunable.c samba-4.6.5+dfsg/ctdb/common/tunable.c --- samba-4.5.8+dfsg/ctdb/common/tunable.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/common/tunable.c 2017-01-11 07:55:14.000000000 +0000 @@ -75,9 +75,9 @@ offsetof(struct ctdb_tunable_list, rerecovery_timeout) }, { "EnableBans", 1, false, offsetof(struct ctdb_tunable_list, enable_bans) }, - { "DeterministicIPs", 0, false, + { "DeterministicIPs", 0, true, offsetof(struct ctdb_tunable_list, deterministic_public_ips) }, - { "LCP2PublicIPs", 1, false, + { "LCP2PublicIPs", 1, true, offsetof(struct ctdb_tunable_list, lcp2_public_ip_assignment) }, { "ReclockPingPeriod", 60, true, offsetof(struct ctdb_tunable_list, reclock_ping_period) }, @@ -119,7 +119,7 @@ offsetof(struct ctdb_tunable_list, deferred_attach_timeout) }, { "AllowClientDBAttach", 1, false, offsetof(struct ctdb_tunable_list, allow_client_db_attach) }, - { "RecoverPDBBySeqNum", 1, false, + { "RecoverPDBBySeqNum", 1, true, offsetof(struct ctdb_tunable_list, recover_pdb_by_seqnum) }, { "DeferredRebalanceOnNodeAdd", 300, true, offsetof(struct ctdb_tunable_list, deferred_rebalance_on_node_add) }, @@ -153,6 +153,8 @@ offsetof(struct ctdb_tunable_list, rec_buffer_size_limit) }, { "QueueBufferSize", 1024, false, offsetof(struct ctdb_tunable_list, queue_buffer_size) }, + { "IPAllocAlgorithm", 2, false, + offsetof(struct ctdb_tunable_list, ip_alloc_algorithm) }, { NULL, 0, true, } }; diff -Nru samba-4.5.8+dfsg/ctdb/config/ctdbd_wrapper samba-4.6.5+dfsg/ctdb/config/ctdbd_wrapper --- samba-4.5.8+dfsg/ctdb/config/ctdbd_wrapper 2016-10-24 19:37:30.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/config/ctdbd_wrapper 2017-01-11 07:55:14.000000000 +0000 @@ -30,53 +30,26 @@ # ctdbd_is_running() -# 1. Check if ctdbd is running. -# - If the PID file is being used then, if the PID file is present, -# ctdbd is only considered to running if the PID in the file is -# active. -# - If the PID file is not being used (i.e. we're upgrading from a -# version that doesn't support it) then the presence of any ctdbd -# processes is enough proof. - -# 2. Print a comma-separated list of PIDs that can be -# used with "pkill -s". -# - If the PID file is being used then this is just the PID in that -# file. This also happens to be the session ID, so can be used -# to kill all CTDB processes. -# - If the PID file is not being used (i.e. upgrading) then this is -# just any ctdbd processes that are running. Hopefully one of -# them is the session ID so that it can be used to kill all CTDB -# processes. - -# Combining these 2 checks is an optimisation to avoid potentially -# running too many pgrep/pkill processes on an already loaded system. -# Trawling through /proc/ can be very expensive. +# Check if ctdbd is running. ctdbd is only considered to running if +# the PID in the PID file is active and is called "ctdbd". Return +# true if this is the case. Print the PID regardless, since it can be +# used to kill stale processes in the session. ctdbd_is_running () { - # If the directory for the PID file exists then respect the - # existence of a PID file. - _pidfile_dir=$(dirname "$pidfile") - if [ -d "$_pidfile_dir" ] ; then if read _pid 2>/dev/null <"$pidfile" ; then - echo "$_pid" + echo "$_pid" - # Return value of kill is used - kill -0 "$_pid" 2>/dev/null - else - # Missing/empty PID file - return 1 - fi - else - if _pid=$(pgrep -f "${ctdbd}\>") ; then - # Use word splitting to squash whitespace - # shellcheck disable=SC2086 - echo $_pid | sed -e 's@ @,@g' - return 0 + # This could be optimised with ps options -q and -h. + # However, -q is not generally available because it is + # fairly new and -h is not in some older distros. The + # options below are portable. + _cmd=$(ps -p "$_pid" -o comm | tail -n +2) + [ "$_cmd" = "ctdbd" ] else - return 1 + # Missing/empty PID file + return 1 fi - fi } ############################################################ diff -Nru samba-4.5.8+dfsg/ctdb/config/events.d/10.interface samba-4.6.5+dfsg/ctdb/config/events.d/10.interface --- samba-4.5.8+dfsg/ctdb/config/events.d/10.interface 2017-01-30 09:56:26.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/config/events.d/10.interface 2017-01-26 12:19:08.000000000 +0000 @@ -225,6 +225,13 @@ get_iface_ip_maskbits "$_oiface" "$_ip" "$_maskbits" oiface="$iface" + # Could check maskbits too. However, that should never change + # so we want to notice if it does. + if [ "$oiface" = "$niface" ] ; then + echo "Redundant \"updateip\" - ${ip} already on ${niface}" + exit 0 + fi + ip_block "$ip" "$oiface" delete_ip_from_iface "$oiface" "$ip" "$maskbits" 2>/dev/null diff -Nru samba-4.5.8+dfsg/ctdb/config/events.d/13.per_ip_routing samba-4.6.5+dfsg/ctdb/config/events.d/13.per_ip_routing --- samba-4.5.8+dfsg/ctdb/config/events.d/13.per_ip_routing 2017-01-30 09:56:26.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/config/events.d/13.per_ip_routing 2017-01-26 12:19:08.000000000 +0000 @@ -367,8 +367,6 @@ ctdb_check_args "$@" -ctdb_service_check_reconfigure - case "$1" in startup) flush_rules_and_routes @@ -435,6 +433,10 @@ add_missing_routes remove_bogus_routes ;; + +reconfigure) + ctdb_service_reconfigure + ;; esac exit 0 diff -Nru samba-4.5.8+dfsg/ctdb/config/events.d/40.vsftpd samba-4.6.5+dfsg/ctdb/config/events.d/40.vsftpd --- samba-4.5.8+dfsg/ctdb/config/events.d/40.vsftpd 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/config/events.d/40.vsftpd 2017-01-11 07:55:14.000000000 +0000 @@ -29,8 +29,6 @@ is_ctdb_managed_service || exit 0 -ctdb_service_check_reconfigure - case "$1" in startup) ctdb_service_start @@ -44,6 +42,12 @@ ctdb_service_set_reconfigure ;; +ipreallocated) + if ctdb_service_needs_reconfigure ; then + ctdb_service_reconfigure + fi + ;; + monitor) if ctdb_check_tcp_ports 21 ; then ctdb_counter_init diff -Nru samba-4.5.8+dfsg/ctdb/config/events.d/50.samba samba-4.6.5+dfsg/ctdb/config/events.d/50.samba --- samba-4.5.8+dfsg/ctdb/config/events.d/50.samba 2016-12-05 08:18:44.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/config/events.d/50.samba 2017-01-11 07:55:14.000000000 +0000 @@ -65,6 +65,7 @@ service_stop () { service "$CTDB_SERVICE_SMB" stop + program_stack_traces "smbd" 5 if [ -n "$CTDB_SERVICE_NMB" ] ; then service "$CTDB_SERVICE_NMB" stop fi diff -Nru samba-4.5.8+dfsg/ctdb/config/events.d/60.nfs samba-4.6.5+dfsg/ctdb/config/events.d/60.nfs --- samba-4.5.8+dfsg/ctdb/config/events.d/60.nfs 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/config/events.d/60.nfs 2017-01-11 07:55:14.000000000 +0000 @@ -254,8 +254,6 @@ is_ctdb_managed_service || exit 0 -ctdb_service_check_reconfigure - case "$1" in startup) nfs_callout "$@" @@ -275,6 +273,12 @@ ctdb_service_set_reconfigure ;; +ipreallocated) + if ctdb_service_needs_reconfigure ; then + ctdb_service_reconfigure + fi + ;; + monitor) nfs_callout "monitor-pre" || exit $? diff -Nru samba-4.5.8+dfsg/ctdb/config/functions samba-4.6.5+dfsg/ctdb/config/functions --- samba-4.5.8+dfsg/ctdb/config/functions 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/config/functions 2017-01-11 07:55:14.000000000 +0000 @@ -101,7 +101,7 @@ # configuration file. debug () { - if [ "${CTDB_SCRIPT_DEBUGLEVEL:-2}" -ge 4 ] ; then + if [ "${CTDB_SCRIPT_DEBUGLEVEL:-NOTICE}" = "DEBUG" ] ; then # If there are arguments then echo them. Otherwise expect to # use stdin, which allows us to pass lots of debug using a # here document. @@ -520,21 +520,12 @@ { _ip="$1" - get_tcp_connections_for_ip "$_ip" | - { - _failed=false - - while read dest src; do - echo "Tickle TCP connection $src $dest" - $CTDB tickle "$src" "$dest" >/dev/null 2>&1 || _failed=true - echo "Tickle TCP connection $dest $src" - $CTDB tickle "$dest" "$src" >/dev/null 2>&1 || _failed=true - done + # Get connections, both directions + _conns=$(get_tcp_connections_for_ip "$_ip" | \ + awk '{ print $1, $2 ; print $2, $1 }') - if $_failed ; then - echo "Failed to send tickle control" - fi - } + echo "$_conns" | awk '{ print "Tickle TCP connection", $1, $2 }' + echo "$_conns" | ctdb tickle } get_tcp_connections_for_ip () @@ -862,139 +853,6 @@ : } -ctdb_reconfigure_take_lock () -{ - _ctdb_service_reconfigure_common - _lock="${_d}/reconfigure_lock" - mkdir -p "${_lock%/*}" # dirname - touch "$_lock" - - ( - flock 9 - # This is overkill but will work if we need to extend - # this to allow certain events to run multiple times - # in parallel (e.g. takeip) and write multiple PIDs to - # the file. - { - read _locker_event - if [ -n "$_locker_event" ] ; then - while read _pid ; do - if [ -n "$_pid" -a "$_pid" != $$ ] && \ - kill -0 "$_pid" 2>/dev/null ; then - exit 1 - fi - done - fi - } <"$_lock" - - printf "%s\n%s\n" "$event_name" $$ >"$_lock" - exit 0 - ) 9>"${_lock}.flock" -} - -ctdb_reconfigure_release_lock () -{ - _ctdb_service_reconfigure_common - _lock="${_d}/reconfigure_lock" - - rm -f "$_lock" -} - -ctdb_replay_monitor_status () -{ - echo "Replaying previous status for this script due to reconfigure..." - # Leading separator ('|') is missing in some versions... - _out=$($CTDB scriptstatus -X | grep -E "^\|?monitor\|${script_name}\|") - # Output looks like this: - # |monitor|60.nfs|1|ERROR|1314764004.030861|1314764004.035514|foo bar| - # This is the cheapest way of getting fields in the middle. - # Intentional word splitting here - # shellcheck disable=SC2046,2086 - set -- $(IFS="|" ; echo $_out) - _code="$3" - _status="$4" - # The error output field can include colons so we'll try to - # preserve them. The weak checking at the beginning tries to make - # this work for both broken (no leading '|') and fixed output. - _out="${_out%|}" - _err_out="${_out#*monitor|${script_name}|*|*|*|*|}" - case "$_status" in - OK) : ;; # Do nothing special. - TIMEDOUT) - # Recast this as an error, since we can't exit with the - # correct negative number. - _code=1 - _err_out="[Replay of TIMEDOUT scriptstatus - note incorrect return code.] ${_err_out}" - ;; - DISABLED) - # Recast this as an OK, since we can't exit with the - # correct negative number. - _code=0 - _err_out="[Replay of DISABLED scriptstatus - note incorrect return code.] ${_err_out}" - ;; - *) : ;; # Must be ERROR, do nothing special. - esac - if [ -n "$_err_out" ] ; then - echo "$_err_out" - fi - exit $_code -} - -ctdb_service_check_reconfigure () -{ - assert_service_name - - # We only care about some events in this function. For others we - # return now. - case "$event_name" in - monitor|ipreallocated|reconfigure) : ;; - *) return 0 ;; - esac - - if ctdb_reconfigure_take_lock ; then - # No events covered by this function are running, so proceed - # with gay abandon. - case "$event_name" in - reconfigure) - (ctdb_service_reconfigure) - exit $? - ;; - ipreallocated) - if ctdb_service_needs_reconfigure ; then - ctdb_service_reconfigure - fi - ;; - esac - - ctdb_reconfigure_release_lock - else - # Somebody else is running an event we don't want to collide - # with. We proceed with caution. - case "$event_name" in - reconfigure) - # Tell whoever called us to retry. - exit 2 - ;; - ipreallocated) - # Defer any scheduled reconfigure and just run the - # rest of the ipreallocated event, as per the - # eventscript. There's an assumption here that the - # event doesn't depend on any scheduled reconfigure. - # This is true in the current code. - return 0 - ;; - monitor) - # There is most likely a reconfigure in progress so - # the service is possibly unstable. As above, we - # defer any scheduled reconfigured. We also replay - # the previous monitor status since that's the best - # information we have. - ctdb_replay_monitor_status - ;; - esac - fi -} - ################################################################## # Does CTDB manage this service? - and associated auto-start/stop @@ -1276,16 +1134,12 @@ sort >"$_my_tickles" # Add tickles for connections that we haven't already got tickles for - comm -23 "$_my_connections" "$_my_tickles" | - while read _src _dst ; do - $CTDB addtickle "$_src" "$_dst" - done + comm -23 "$_my_connections" "$_my_tickles" | \ + $CTDB addtickle # Remove tickles for connections that are no longer there - comm -13 "$_my_connections" "$_my_tickles" | - while read _src _dst ; do - $CTDB deltickle "$_src" "$_dst" - done + comm -13 "$_my_connections" "$_my_tickles" | \ + $CTDB deltickle rm -f "$_my_connections" "$_my_tickles" diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdb.1 samba-4.6.5+dfsg/ctdb/doc/ctdb.1 --- samba-4.5.8+dfsg/ctdb/doc/ctdb.1 2017-01-30 10:15:39.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdb.1 2017-06-06 07:49:56.000000000 +0000 @@ -2,12 +2,12 @@ .\" Title: ctdb .\" Author: .\" Generator: DocBook XSL Stylesheets v1.78.1 -.\" Date: 01/30/2017 +.\" Date: 06/06/2017 .\" Manual: CTDB - clustered TDB database .\" Source: ctdb .\" Language: English .\" -.TH "CTDB" "1" "01/30/2017" "ctdb" "CTDB \- clustered TDB database" +.TH "CTDB" "1" "06/06/2017" "ctdb" "CTDB \- clustered TDB database" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -64,9 +64,9 @@ .RE .SH "OPTIONS" .PP -\-n \fIPNN\-LIST\fR +\-n \fIPNN\fR .RS 4 -The nodes specified by PNN\-LIST should be queried for the requested information\&. Default is to query the daemon running on the local host\&. +The node specified by PNN should be queried for the requested information\&. Default is to query the daemon running on the local host\&. .RE .PP \-Y @@ -108,7 +108,7 @@ .PP \-d \-\-debug=\fIDEBUGLEVEL\fR .RS 4 -Change the debug level for the command\&. Default is NOTICE (2)\&. +Change the debug level for the command\&. Default is NOTICE\&. .RE .PP \-\-socket=\fIFILENAME\fR @@ -549,9 +549,109 @@ .RE .\} .RE +.SS "event run|status|script list|script enable|script disable" +.PP +This command is used to control event daemon and to inspect status of various events\&. +.PP +run \fIEVENT\fR \fITIMEOUT\fR [\fIARGUMENTS\fR] +.RS 4 +This command can be used to manually run specified EVENT with optional ARGUMENTS\&. The event will be allowed to run a maximum of TIMEOUT seconds\&. If TIMEOUT is 0, then there is no time limit for running the event\&. +.RE +.PP +status [\fIEVENT\fR] [lastrun|lastpass|lastfail] +.RS 4 +This command displays the last execution status of the specified EVENT\&. If no event is specified, then the status of last executed monitor event will be displayed\&. +.sp +To see the last successful execution of the event, lastpass can be specified\&. Similarly lastfail can be specified to see the last unsuccessful execution of the event\&. The optional lastrun can be specified to query the last execution of the event\&. +.sp +The command will terminate with the exit status corresponding to the overall status of event that is displayed\&. If lastpass is specified, then the command will always terminate with 0\&. If lastfail is specified then the command will always terminate with non\-zero exit status\&. If lastrun is specified, then the command will terminate with 0 or not depending on if the last execution of the event was successful or not\&. +.sp +The output is the list of event scripts executed\&. Each line shows the name, status, duration and start time for each script\&. +.sp +Example output: +.sp +.if n \{\ +.RS 4 +.\} +.nf +00\&.ctdb OK 0\&.014 Sat Dec 17 19:39:11 2016 +01\&.reclock OK 0\&.013 Sat Dec 17 19:39:11 2016 +05\&.system OK 0\&.029 Sat Dec 17 19:39:11 2016 +06\&.nfs OK 0\&.014 Sat Dec 17 19:39:11 2016 +10\&.external DISABLED +10\&.interface OK 0\&.037 Sat Dec 17 19:39:11 2016 +11\&.natgw OK 0\&.011 Sat Dec 17 19:39:11 2016 +11\&.routing OK 0\&.007 Sat Dec 17 19:39:11 2016 +13\&.per_ip_routing OK 0\&.007 Sat Dec 17 19:39:11 2016 +20\&.multipathd OK 0\&.007 Sat Dec 17 19:39:11 2016 +31\&.clamd OK 0\&.007 Sat Dec 17 19:39:11 2016 +40\&.vsftpd OK 0\&.013 Sat Dec 17 19:39:11 2016 +41\&.httpd OK 0\&.018 Sat Dec 17 19:39:11 2016 +49\&.winbind OK 0\&.023 Sat Dec 17 19:39:11 2016 +50\&.samba OK 0\&.100 Sat Dec 17 19:39:12 2016 +60\&.nfs OK 0\&.376 Sat Dec 17 19:39:12 2016 +70\&.iscsi OK 0\&.009 Sat Dec 17 19:39:12 2016 +91\&.lvs OK 0\&.007 Sat Dec 17 19:39:12 2016 +99\&.timeout OK 0\&.007 Sat Dec 17 19:39:12 2016 + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +script list +.RS 4 +List the available event scripts\&. +.sp +Example output: +.sp +.if n \{\ +.RS 4 +.\} +.nf +00\&.ctdb +01\&.reclock +05\&.system +06\&.nfs +10\&.external DISABLED +10\&.interface +11\&.natgw +11\&.routing +13\&.per_ip_routing +20\&.multipathd +31\&.clamd +40\&.vsftpd +41\&.httpd +49\&.winbind +50\&.samba +60\&.nfs +70\&.iscsi +91\&.lvs +99\&.timeout + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +script enable \fISCRIPT\fR +.RS 4 +Enable the specified event SCRIPT\&. Only enabled scripts will be executed when running any event\&. +.RE +.PP +script disable \fISCRIPT\fR +.RS 4 +Disable the specified event SCRIPT\&. This will prevent the script from executing when running any event\&. +.RE .SS "scriptstatus" .PP -This command displays which scripts where run in the previous monitoring cycle and the result of each script\&. If a script failed with an error, causing the node to become unhealthy, the output from that script is also shown\&. +This command displays which event scripts where run in the previous monitoring cycle and the result of each script\&. If a script failed with an error, causing the node to become unhealthy, the output from that script is also shown\&. +.PP +This command is deprecated\&. It\*(Aqs provided for backward compatibility\&. In place of +\fBctdb scriptstatus\fR, use +\fBctdb event status\fR\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -566,32 +666,28 @@ .\} .nf # ctdb scriptstatus -7 scripts were executed last monitoring cycle -00\&.ctdb Status:OK Duration:0\&.056 Tue Mar 24 18:56:57 2009 -10\&.interface Status:OK Duration:0\&.077 Tue Mar 24 18:56:57 2009 -11\&.natgw Status:OK Duration:0\&.039 Tue Mar 24 18:56:57 2009 -20\&.multipathd Status:OK Duration:0\&.038 Tue Mar 24 18:56:57 2009 -31\&.clamd Status:DISABLED -40\&.vsftpd Status:OK Duration:0\&.045 Tue Mar 24 18:56:57 2009 -41\&.httpd Status:OK Duration:0\&.039 Tue Mar 24 18:56:57 2009 -50\&.samba Status:ERROR Duration:0\&.082 Tue Mar 24 18:56:57 2009 -OUTPUT:ERROR: Samba tcp port 445 is not responding +00\&.ctdb OK 0\&.011 Sat Dec 17 19:40:46 2016 +01\&.reclock OK 0\&.010 Sat Dec 17 19:40:46 2016 +05\&.system OK 0\&.030 Sat Dec 17 19:40:46 2016 +06\&.nfs OK 0\&.014 Sat Dec 17 19:40:46 2016 +10\&.external DISABLED +10\&.interface OK 0\&.041 Sat Dec 17 19:40:46 2016 +11\&.natgw OK 0\&.008 Sat Dec 17 19:40:46 2016 +11\&.routing OK 0\&.007 Sat Dec 17 19:40:46 2016 +13\&.per_ip_routing OK 0\&.007 Sat Dec 17 19:40:46 2016 +20\&.multipathd OK 0\&.007 Sat Dec 17 19:40:46 2016 +31\&.clamd OK 0\&.007 Sat Dec 17 19:40:46 2016 +40\&.vsftpd OK 0\&.013 Sat Dec 17 19:40:46 2016 +41\&.httpd OK 0\&.015 Sat Dec 17 19:40:46 2016 +49\&.winbind OK 0\&.022 Sat Dec 17 19:40:46 2016 +50\&.samba ERROR 0\&.077 Sat Dec 17 19:40:46 2016 + OUTPUT: ERROR: samba tcp port 445 is not responding .fi .if n \{\ .RE .\} .RE -.SS "disablescript \fISCRIPT\fR" -.PP -This command is used to disable an eventscript\&. -.PP -This will take effect the next time the eventscripts are being executed so it can take a short while until this is reflected in \*(Aqscriptstatus\*(Aq\&. -.SS "enablescript \fISCRIPT\fR" -.PP -This command is used to enable an eventscript\&. -.PP -This will take effect the next time the eventscripts are being executed so it can take a short while until this is reflected in \*(Aqscriptstatus\*(Aq\&. .SS "listvars" .PP List all tuneable variables, except the values of the obsolete tunables like VacuumMinInterval\&. The obsolete tunables can be retrieved only explicitly with the "ctdb getvar" command\&. @@ -628,8 +724,6 @@ DatabaseMaxDead = 5 RerecoveryTimeout = 10 EnableBans = 1 -DeterministicIPs = 0 -LCP2PublicIPs = 1 NoIPFailback = 0 DisableIPFailover = 0 VerboseMemoryNames = 0 @@ -660,9 +754,11 @@ DBSizeWarn = 100000000 PullDBPreallocation = 10485760 NoIPHostOnAllDisabled = 0 -Samba3AvoidDeadlocks = 0 TDBMutexEnabled = 0 LockProcessesPerDB = 200 +RecBufferSizeLimit = 1000000 +QueueBufferSize = 1024 +IPAllocAlgorithm = 2 .fi .if n \{\ @@ -981,7 +1077,7 @@ .PP In order to manually override the "automatic" distribution of public ip addresses that ctdb normally provides, this command only works when you have changed the tunables for the daemon to: .PP -DeterministicIPs = 0 +IPAllocAlgorithm != 0 .PP NoIPFailback = 1 .SS "shutdown" @@ -1403,9 +1499,6 @@ .SS "rddumpmemory" .PP This is a debugging command\&. This command will dump the talloc memory allocation tree for the recovery daemon to standard output\&. -.SS "eventscript \fIARGUMENTS\fR" -.PP -This is a debugging command\&. This command can be used to manually invoke and run the eventscritps with arbitrary arguments\&. .SS "ban \fIBANTIME\fR" .PP Administratively ban a node for BANTIME seconds\&. The node will be unbanned after BANTIME seconds have elapsed\&. diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdb.1.html samba-4.6.5+dfsg/ctdb/doc/ctdb.1.html --- samba-4.5.8+dfsg/ctdb/doc/ctdb.1.html 2017-01-30 10:15:40.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdb.1.html 2017-06-06 07:49:56.000000000 +0000 @@ -1,4 +1,4 @@ -ctdb

Name

ctdb — CTDB management utility

Synopsis

ctdb [OPTION...] {COMMAND} [COMMAND-ARGS]

DESCRIPTION

+ctdb

Name

ctdb — CTDB management utility

Synopsis

ctdb [OPTION...] {COMMAND} [COMMAND-ARGS]

DESCRIPTION

ctdb is a utility to view and manage a CTDB cluster.

The following terms are used when referring to nodes in a @@ -21,8 +21,8 @@ A space separated list of at least one DB.

-

OPTIONS

-n PNN-LIST

- The nodes specified by PNN-LIST should be queried for the +

OPTIONS

-n PNN

+ The node specified by PNN should be queried for the requested information. Default is to query the daemon running on the local host.

-Y

@@ -53,27 +53,27 @@

--usage

Print useage information to the screen.

-d --debug=DEBUGLEVEL

- Change the debug level for the command. Default is NOTICE (2). + Change the debug level for the command. Default is NOTICE.

--socket=FILENAME

Specify that FILENAME is the name of the Unix domain socket to use when connecting to the local CTDB daemon. The default is /usr/local/var/run/ctdb/ctdbd.socket. -

ADMINISTRATIVE COMMANDS

+

ADMINISTRATIVE COMMANDS

These are commands used to monitor and administer a CTDB cluster. -

pnn

+

pnn

This command displays the PNN of the current node. -

status

+

status

This command shows the current status of all CTDB nodes based on information from the queried node.

Note: If the the queried node is INACTIVE then the status might not be current. -

Node status

+

Node status

This includes the number of physical nodes and the status of each node. See ctdb(7) for information about node states. -

Generation

+

Generation

The generation id is a number that indicates the current generation of a cluster instance. Each time a cluster goes through a reconfiguration or a recovery its generation id will be changed. @@ -94,13 +94,13 @@ All nodes start with generation "INVALID" and are not assigned a real generation id until they have successfully been merged with a cluster through a recovery. -

Virtual Node Number (VNN) map

+

Virtual Node Number (VNN) map

Consists of the number of virtual nodes and mapping from virtual node numbers to physical node numbers. Virtual nodes host CTDB databases. Only nodes that are participating in the VNN map can become lmaster or dmaster for database records. -

Recovery mode

+

Recovery mode

This is the current recovery mode of the cluster. There are two possible modes:

NORMAL - The cluster is fully operational. @@ -120,13 +120,13 @@ databases have been recovered, the node mode will change into NORMAL mode and the databases will be "thawed", allowing samba to access the databases again. -

Recovery master

+

Recovery master

This is the cluster node that is currently designated as the recovery master. This node is responsible of monitoring the consistency of the cluster and to perform the actual recovery process when reqired.

Only one node at a time can be the designated recovery master. Which node is designated the recovery master is decided by an election process in the recovery daemons running on each node. -

Example

+	

Example

 # ctdb status
 Number of nodes:4
 pnn:0 192.168.2.200       OK (THIS NODE)
@@ -141,7 +141,7 @@
 hash:3 lmaster:3
 Recovery mode:NORMAL (0)
 Recovery master:0
-	

nodestatus [PNN-LIST]

+

nodestatus [PNN-LIST]

This command is similar to the status command. It displays the "node status" subset of output. The main differences are: @@ -159,7 +159,7 @@ A common invocation in scripts is ctdb nodestatus all to check whether all nodes in a cluster are healthy. -

Example

+      

Example

 # ctdb nodestatus
 pnn:0 10.0.0.30        OK (THIS NODE)
 
@@ -167,28 +167,28 @@
 Number of nodes:2
 pnn:0 10.0.0.30        OK (THIS NODE)
 pnn:1 10.0.0.31        OK
-	

recmaster

+

recmaster

This command shows the pnn of the node which is currently the recmaster.

Note: If the the queried node is INACTIVE then the status might not be current. -

uptime

+

uptime

This command shows the uptime for the ctdb daemon. When the last recovery or ip-failover completed and how long it took. If the "duration" is shown as a negative number, this indicates that there is a recovery/failover in progress and it started that many seconds ago. -

Example

+      

Example

 # ctdb uptime
 Current time of node          :                Thu Oct 29 10:38:54 2009
 Ctdbd start time              : (000 16:54:28) Wed Oct 28 17:44:26 2009
 Time of last recovery/failover: (000 16:53:31) Wed Oct 28 17:45:23 2009
 Duration of last recovery/failover: 2.248552 seconds
-	

listnodes

+

listnodes

This command shows lists the ip addresses of all the nodes in the cluster. -

Example

+      

Example

 # ctdb listnodes
 192.168.2.200
 192.168.2.201
 192.168.2.202
 192.168.2.203
-	

natgw {master|list|status}

+

natgw {master|list|status}

This command shows different aspects of NAT gateway status. For an overview of CTDB's NAT gateway functionality please see the NAT GATEWAY section in @@ -220,16 +220,16 @@ pnn:1 192.168.2.201 OK pnn:2 192.168.2.202 OK pnn:3 192.168.2.203 OK -

ping

+

ping

This command will "ping" specified CTDB nodes in the cluster to verify that they are running. -

Example

+      

Example

 # ctdb ping
 response from 0 time=0.000054 sec  (3 clients)
-	

ifaces

+

ifaces

This command will display the list of network interfaces, which could host public addresses, along with their status. -

Example

+      

Example

 # ctdb ifaces
 Interfaces on node 0
 name:eth5 link:up references:2
@@ -243,9 +243,9 @@
 |eth4|0|0|
 |eth3|1|1|
 |eth2|1|1|
-	

ip

+

ip

This command will display the list of public addresses that are provided by the cluster and which physical node is currently serving this ip. By default this command will ONLY show those public addresses that are known to the node itself. To see the full list of all public ips across the cluster you must use "ctdb ip all". -

Example

+      

Example

 # ctdb ip -v
 Public IPs on node 0
 172.31.91.82 node[1] active[] available[eth2,eth3] configured[eth2,eth3]
@@ -267,9 +267,9 @@
 |172.31.92.83|0|eth5|eth5|eth4,eth5|
 |172.31.92.84|1||eth5|eth4,eth5|
 |172.31.92.85|0|eth5|eth5|eth4,eth5|
-	

ipinfo IP

+

ipinfo IP

This command will display details about the specified public addresses. -

Example

+      

Example

 # ctdb ipinfo 172.31.92.85
 Public IP[172.31.92.85] info on node 0
 IP:172.31.92.85
@@ -277,33 +277,121 @@
 NumInterfaces:2
 Interface[1]: Name:eth4 Link:down References:0
 Interface[2]: Name:eth5 Link:up References:2 (active)
-	

scriptstatus

- This command displays which scripts where run in the previous monitoring cycle and the result of each script. If a script failed with an error, causing the node to become unhealthy, the output from that script is also shown. -

Example

+	

event run|status|script list|script enable|script disable

+ This command is used to control event daemon and to inspect + status of various events. +

run EVENT TIMEOUT [ARGUMENTS]

+ This command can be used to manually run specified EVENT + with optional ARGUMENTS. The event will be allowed to run + a maximum of TIMEOUT seconds. If TIMEOUT is 0, then there + is no time limit for running the event. +

status [EVENT] [lastrun|lastpass|lastfail]

+ This command displays the last execution status of the + specified EVENT. If no event is specified, then the status + of last executed monitor event will be displayed. +

+ To see the last successful execution of the event, lastpass + can be specified. Similarly lastfail can be specified + to see the last unsuccessful execution of the event. + The optional lastrun can be specified to query the last + execution of the event. +

+ The command will terminate with the exit status + corresponding to the overall status of event that is + displayed. If lastpass is specified, then the command will + always terminate with 0. If lastfail is specified then the + command will always terminate with non-zero exit status. + If lastrun is specified, then the command will terminate + with 0 or not depending on if the last execution of the + event was successful or not. +

+ The output is the list of event scripts executed. + Each line shows the name, status, duration and start time + for each script. +

+ Example output: +

+00.ctdb              OK         0.014 Sat Dec 17 19:39:11 2016
+01.reclock           OK         0.013 Sat Dec 17 19:39:11 2016
+05.system            OK         0.029 Sat Dec 17 19:39:11 2016
+06.nfs               OK         0.014 Sat Dec 17 19:39:11 2016
+10.external          DISABLED  
+10.interface         OK         0.037 Sat Dec 17 19:39:11 2016
+11.natgw             OK         0.011 Sat Dec 17 19:39:11 2016
+11.routing           OK         0.007 Sat Dec 17 19:39:11 2016
+13.per_ip_routing    OK         0.007 Sat Dec 17 19:39:11 2016
+20.multipathd        OK         0.007 Sat Dec 17 19:39:11 2016
+31.clamd             OK         0.007 Sat Dec 17 19:39:11 2016
+40.vsftpd            OK         0.013 Sat Dec 17 19:39:11 2016
+41.httpd             OK         0.018 Sat Dec 17 19:39:11 2016
+49.winbind           OK         0.023 Sat Dec 17 19:39:11 2016
+50.samba             OK         0.100 Sat Dec 17 19:39:12 2016
+60.nfs               OK         0.376 Sat Dec 17 19:39:12 2016
+70.iscsi             OK         0.009 Sat Dec 17 19:39:12 2016
+91.lvs               OK         0.007 Sat Dec 17 19:39:12 2016
+99.timeout           OK         0.007 Sat Dec 17 19:39:12 2016
+	    
script list

+ List the available event scripts. +

+ Example output: +

+00.ctdb             
+01.reclock          
+05.system           
+06.nfs              
+10.external          DISABLED
+10.interface        
+11.natgw            
+11.routing          
+13.per_ip_routing   
+20.multipathd       
+31.clamd            
+40.vsftpd           
+41.httpd            
+49.winbind          
+50.samba            
+60.nfs              
+70.iscsi            
+91.lvs              
+99.timeout          
+	    
script enable SCRIPT

+ Enable the specified event SCRIPT. Only enabled scripts will be + executed when running any event. +

script disable SCRIPT

+ Disable the specified event SCRIPT. This will prevent the script + from executing when running any event. +

scriptstatus

+ This command displays which event scripts where run in the previous + monitoring cycle and the result of each script. If a script + failed with an error, causing the node to become unhealthy, + the output from that script is also shown. +

+ This command is deprecated. It's provided for backward + compatibility. In place of ctdb scriptstatus, + use ctdb event status. +

Example

 # ctdb scriptstatus
-7 scripts were executed last monitoring cycle
-00.ctdb              Status:OK    Duration:0.056 Tue Mar 24 18:56:57 2009
-10.interface         Status:OK    Duration:0.077 Tue Mar 24 18:56:57 2009
-11.natgw             Status:OK    Duration:0.039 Tue Mar 24 18:56:57 2009
-20.multipathd        Status:OK    Duration:0.038 Tue Mar 24 18:56:57 2009
-31.clamd             Status:DISABLED
-40.vsftpd            Status:OK    Duration:0.045 Tue Mar 24 18:56:57 2009
-41.httpd             Status:OK    Duration:0.039 Tue Mar 24 18:56:57 2009
-50.samba             Status:ERROR    Duration:0.082 Tue Mar 24 18:56:57 2009
-OUTPUT:ERROR: Samba tcp port 445 is not responding
-      

disablescript SCRIPT

- This command is used to disable an eventscript. -

- This will take effect the next time the eventscripts are being executed so it can take a short while until this is reflected in 'scriptstatus'. -

enablescript SCRIPT

- This command is used to enable an eventscript. -

- This will take effect the next time the eventscripts are being executed so it can take a short while until this is reflected in 'scriptstatus'. -

listvars

+00.ctdb OK 0.011 Sat Dec 17 19:40:46 2016 +01.reclock OK 0.010 Sat Dec 17 19:40:46 2016 +05.system OK 0.030 Sat Dec 17 19:40:46 2016 +06.nfs OK 0.014 Sat Dec 17 19:40:46 2016 +10.external DISABLED +10.interface OK 0.041 Sat Dec 17 19:40:46 2016 +11.natgw OK 0.008 Sat Dec 17 19:40:46 2016 +11.routing OK 0.007 Sat Dec 17 19:40:46 2016 +13.per_ip_routing OK 0.007 Sat Dec 17 19:40:46 2016 +20.multipathd OK 0.007 Sat Dec 17 19:40:46 2016 +31.clamd OK 0.007 Sat Dec 17 19:40:46 2016 +40.vsftpd OK 0.013 Sat Dec 17 19:40:46 2016 +41.httpd OK 0.015 Sat Dec 17 19:40:46 2016 +49.winbind OK 0.022 Sat Dec 17 19:40:46 2016 +50.samba ERROR 0.077 Sat Dec 17 19:40:46 2016 + OUTPUT: ERROR: samba tcp port 445 is not responding +

listvars

List all tuneable variables, except the values of the obsolete tunables like VacuumMinInterval. The obsolete tunables can be retrieved only explicitly with the "ctdb getvar" command. -

Example

+      

Example

 # ctdb listvars
 SeqnumInterval          = 1000
 ControlTimeout          = 60
@@ -324,8 +412,6 @@
 DatabaseMaxDead         = 5
 RerecoveryTimeout       = 10
 EnableBans              = 1
-DeterministicIPs        = 0
-LCP2PublicIPs           = 1
 NoIPFailback            = 0
 DisableIPFailover       = 0
 VerboseMemoryNames      = 0
@@ -356,19 +442,21 @@
 DBSizeWarn              = 100000000
 PullDBPreallocation     = 10485760
 NoIPHostOnAllDisabled   = 0
-Samba3AvoidDeadlocks    = 0
 TDBMutexEnabled         = 0
 LockProcessesPerDB      = 200
-	

getvar NAME

+RecBufferSizeLimit = 1000000 +QueueBufferSize = 1024 +IPAllocAlgorithm = 2 +

getvar NAME

Get the runtime value of a tuneable variable. -

Example

+      

Example

 # ctdb getvar MonitorInterval
 MonitorInterval         = 15
-	

setvar NAME VALUE

+

setvar NAME VALUE

Set the runtime value of a tuneable variable. -

Example

+      

Example

 # ctdb setvar MonitorInterval 20
-	

lvs {master|list|status}

+

lvs {master|list|status}

This command shows different aspects of LVS status. For an overview of CTDB's LVS functionality please see the LVS section in @@ -395,7 +483,7 @@ pnn:1 10.0.0.12 UNHEALTHY pnn:2 10.0.0.13 OK pnn:3 10.0.0.14 OK -

getcapabilities

+

getcapabilities

This command shows the capabilities of the current node. See the CAPABILITIES section in ctdb(7) for more details. @@ -404,12 +492,12 @@

 RECMASTER: YES
 LMASTER: YES
-      

statistics

+

statistics

Collect statistics from the CTDB daemon about how many calls it has served. Information about various fields in statistics can be found in ctdb-statistics(7). -

Example

+      

Example

 # ctdb statistics
 CTDB version 1
 Current time of statistics  :                Tue Mar  8 15:18:51 2016
@@ -461,15 +549,15 @@
  reclock_recd       MIN/AVG/MAX     0.000000/0.000000/0.000000 sec out of 0
  call_latency       MIN/AVG/MAX     0.000044/0.002142/0.011702 sec out of 15
  childwrite_latency MIN/AVG/MAX     0.000000/0.000000/0.000000 sec out of 0
-	

statisticsreset

+

statisticsreset

This command is used to clear all statistics counters in a node.

Example: ctdb statisticsreset -

dbstatistics DB

+

dbstatistics DB

Display statistics about the database DB. Information about various fields in dbstatistics can be found in ctdb-statistics(7). -

Example

+      

Example

 # ctdb dbstatistics locking.tdb
 DB Statistics: locking.tdb
  ro_delegations                     0
@@ -485,13 +573,13 @@
  vacuum_latency     MIN/AVG/MAX     0.000472/0.002207/15.243570 sec out of 224530
  Num Hot Keys:     1
      Count:8 Key:ff5bd7cb3ee3822edc1f0000000000000000000000000000
-	

getreclock

+

getreclock

Show details of the recovery lock, if any.

Example output:

 	/clusterfs/.ctdb/recovery.lock
-      

getdebug

+

getdebug

Get the current debug level for the node. the debug level controls what information is written to the log file.

The debug levels are mapped to the corresponding syslog levels. @@ -501,29 +589,29 @@ The list of debug levels from highest to lowest are :

ERROR WARNING NOTICE INFO DEBUG -

setdebug DEBUGLEVEL

+

setdebug DEBUGLEVEL

Set the debug level of a node. This controls what information will be logged.

The debuglevel is one of ERROR WARNING NOTICE INFO DEBUG -

getpid

+

getpid

This command will return the process id of the ctdb daemon. -

disable

+

disable

This command is used to administratively disable a node in the cluster. A disabled node will still participate in the cluster and host clustered TDB records but its public ip address has been taken over by a different node and it no longer hosts any services. -

enable

+

enable

Re-enable a node that has been administratively disabled. -

stop

+

stop

This command is used to administratively STOP a node in the cluster. A STOPPED node is connected to the cluster but will not host any public ip addresse, nor does it participate in the VNNMAP. The difference between a DISABLED node and a STOPPED node is that a STOPPED node does not host any parts of the database which means that a recovery is required to stop/continue nodes. -

continue

+

continue

Re-start a node that has been administratively stopped. -

addip IPADDR/mask IFACE

+

addip IPADDR/mask IFACE

This command is used to add a new public ip to a node during runtime. It should be followed by a ctdb ipreallocate. This allows public addresses to be @@ -533,7 +621,7 @@ changes will be lost next time ctdb is restarted and the public addresses file is re-read. If you want this change to be permanent you must also update the public addresses file manually. -

delip IPADDR

+

delip IPADDR

This command flags IPADDR for deletion from a node at runtime. It should be followed by a ctdb ipreallocate. If IPADDR is currently hosted by the @@ -546,7 +634,7 @@ public addresses file is re-read. If you want this change to be permanent you must also update the public addresses file manually. -

moveip IPADDR PNN

+

moveip IPADDR PNN

This command can be used to manually fail a public ip address to a specific node.

@@ -554,12 +642,12 @@ ip addresses that ctdb normally provides, this command only works when you have changed the tunables for the daemon to:

- DeterministicIPs = 0 + IPAllocAlgorithm != 0

NoIPFailback = 1 -

shutdown

+

shutdown

This command will shutdown a specific CTDB daemon. -

setlmasterrole on|off

+

setlmasterrole on|off

This command is used ot enable/disable the LMASTER capability for a node at runtime. This capability determines whether or not a node can be used as an LMASTER for records in the database. A node that does not have the LMASTER capability will not show up in the vnnmap.

Nodes will by default have this capability, but it can be stripped off nodes by the setting in the sysconfig file or by using this command. @@ -567,13 +655,13 @@ Once this setting has been enabled/disabled, you need to perform a recovery for it to take effect.

See also "ctdb getcapabilities" -

setrecmasterrole on|off

+

setrecmasterrole on|off

This command is used ot enable/disable the RECMASTER capability for a node at runtime. This capability determines whether or not a node can be used as an RECMASTER for the cluster. A node that does not have the RECMASTER capability can not win a recmaster election. A node that already is the recmaster for the cluster when the capability is stripped off the node will remain the recmaster until the next cluster election.

Nodes will by default have this capability, but it can be stripped off nodes by the setting in the sysconfig file or by using this command.

See also "ctdb getcapabilities" -

reloadnodes

+

reloadnodes

This command is used when adding new nodes, or removing existing nodes from an existing cluster.

@@ -622,7 +710,7 @@

  • Use ctdb status on all nodes and verify that the deleted nodes are no longer listed. -

  • +

    reloadips [PNN-LIST]

    @@ -635,7 +723,7 @@ Such changes must be made in 2 steps by deleting addresses in question and re-adding then. Unfortunately this will disrupt connections to the changed addresses. -

    getdbmap

    +

    getdbmap

    This command lists all clustered TDB databases that the CTDB daemon has attached to. Some databases are flagged as PERSISTENT, this means that the database stores data persistently and the data will remain across reboots. One example of such a database is secrets.tdb where information about how the cluster was joined to the domain is stored.

    If a PERSISTENT database is not in a healthy state the database is @@ -649,7 +737,7 @@ and (if samba or tdb-utils are installed) "tdbtool check".

    Most databases are not persistent and only store the state information that the currently running samba daemons need. These databases are always wiped when ctdb/samba starts and when a node is rebooted. -

    Example

    +      

    Example

     # ctdb getdbmap
     Number of databases:10
     dbid:0x435d3410 name:notify.tdb path:/usr/local/var/lib/ctdb/notify.tdb.0
    @@ -670,7 +758,7 @@
     # ctdb -X getdbmap
     |ID|Name|Path|Persistent|Unhealthy|
     |0x7bbbd26c|passdb.tdb|/usr/local/var/lib/ctdb/persistent/passdb.tdb.0|1|0|
    -	

    +

    backupdb DB FILE @@ -679,7 +767,7 @@ read back using restoredb. This is mainly useful for backing up persistent databases such as secrets.tdb and similar. -

    +

    restoredb FILE [DB] @@ -689,45 +777,45 @@ be restored back into the same database as it was created from. By specifying dbname you can restore the data into a different database. -

    setdbreadonly DB

    +

    setdbreadonly DB

    This command will enable the read-only record support for a database. This is an experimental feature to improve performance for contended records primarily in locking.tdb and brlock.tdb. When enabling this feature you must set it on all nodes in the cluster. -

    setdbsticky DB

    +

    setdbsticky DB

    This command will enable the sticky record support for the specified database. This is an experimental feature to improve performance for contended records primarily in locking.tdb and brlock.tdb. When enabling this feature you must set it on all nodes in the cluster. -

    INTERNAL COMMANDS

    +

    INTERNAL COMMANDS

    Internal commands are used by CTDB's scripts and are not required for managing a CTDB cluster. Their parameters and behaviour are subject to change. -

    gettickles IPADDR

    +

    gettickles IPADDR

    Show TCP connections that are registered with CTDB to be "tickled" if there is a failover. -

    gratarp IPADDR INTERFACE

    +

    gratarp IPADDR INTERFACE

    Send out a gratuitous ARP for the specified interface through the specified interface. This command is mainly used by the ctdb eventscripts. -

    +

    pdelete DB KEY

    Delete KEY from DB. -

    +

    pfetch DB KEY

    Print the value associated with KEY in DB. -

    +

    pstore DB KEY FILE

    Store KEY in DB with contents of FILE as the associated value. -

    +

    ptrans DB [FILE] @@ -739,7 +827,7 @@ The key and value should be separated by spaces or tabs. Each key/value should be a printable string enclosed in double-quotes. -

    runstate [setup|first_recovery|startup|running]

    +

    runstate [setup|first_recovery|startup|running]

    Print the runstate of the specified node. Runstates are used to serialise important state transitions in CTDB, particularly during startup. @@ -747,16 +835,16 @@ If one or more optional runstate arguments are specified then the node must be in one of these runstates for the command to succeed. -

    Example

    +      

    Example

     # ctdb runstate
     RUNNING
    -	

    setifacelink IFACE up|down

    +

    setifacelink IFACE up|down

    Set the internal state of network interface IFACE. This is typically used in the 10.interface script in the "monitor" event.

    Example: ctdb setifacelink eth0 up -

    tickle

    +

    tickle

    Read a list of TCP connections, one per line, from standard input and send a TCP tickle to the source host for each connection. A connection is specified as: @@ -776,12 +864,12 @@ TCP connection has been disrupted and that the client will need to reestablish. This greatly speeds up the time it takes for a client to detect and reestablish after an IP failover in the ctdb cluster. -

    version

    +

    version

    Display the CTDB version. -

    DEBUGGING COMMANDS

    +

    DEBUGGING COMMANDS

    These commands are primarily used for CTDB development and testing and should not be used for normal administration. -

    OPTIONS

    --print-emptyrecords

    +

    OPTIONS

    --print-emptyrecords

    This enables printing of empty records when dumping databases with the catdb, cattbd and dumpdbbackup commands. Records with empty data segment are considered deleted by ctdb and cleaned @@ -799,11 +887,11 @@ This lets catdb and dumpdbbackup print the record flags for each record. Note that cattdb always prints the flags. -

    process-exists PID

    +

    process-exists PID

    This command checks if a specific process exists on the CTDB host. This is mainly used by Samba to check if remote instances of samba are still running or not. -

    getdbstatus DB

    +

    getdbstatus DB

    This command displays more details about a database. -

    Example

    +      

    Example

     # ctdb getdbstatus test.tdb.0
     dbid: 0x122224da
     name: test.tdb
    @@ -817,28 +905,28 @@
     path: /usr/local/var/lib/ctdb/persistent/registry.tdb.0
     PERSISTENT: yes
     HEALTH: NO-HEALTHY-NODES - ERROR - Backup of corrupted TDB in '/usr/local/var/lib/ctdb/persistent/registry.tdb.0.corrupted.20091208091949.0Z'
    -	

    catdb DB

    +

    catdb DB

    Print a dump of the clustered TDB database DB. -

    cattdb DB

    +

    cattdb DB

    Print a dump of the contents of the local TDB database DB. -

    dumpdbbackup FILE

    +

    dumpdbbackup FILE

    Print a dump of the contents from database backup FILE, similar to catdb. -

    wipedb DB

    +

    wipedb DB

    Remove all contents of database DB. -

    recover

    +

    recover

    This command will trigger the recovery daemon to do a cluster recovery. -

    ipreallocate, sync

    +

    ipreallocate, sync

    This command will force the recovery master to perform a full ip reallocation process and redistribute all ip addresses. This is useful to "reset" the allocations back to its default state if they have been changed using the "moveip" command. While a "recover" will also perform this reallocation, a recovery is much more hevyweight since it will also rebuild all the databases. -

    getmonmode

    +

    getmonmode

    This command prints the monitoring mode of a node. This indicates when CTDB is monitoring services on the node. The monitoring mode is either ENABLED or DISABLED. -

    attach DBNAME [persistent]

    +

    attach DBNAME [persistent]

    Create a new CTDB database called DBNAME and attach to it on all nodes. -

    detach DB-LIST

    +

    detach DB-LIST

    Detach specified non-persistent database(s) from the cluster. This command will disconnect specified database(s) on all nodes in the cluster. This command should only be used when none of the @@ -846,16 +934,13 @@

    All nodes should be active and tunable AllowClientDBAccess should be disabled on all nodes before detaching databases. -

    dumpmemory

    +

    dumpmemory

    This is a debugging command. This command will make the ctdb daemon to write a fill memory allocation map to standard output. -

    rddumpmemory

    +

    rddumpmemory

    This is a debugging command. This command will dump the talloc memory allocation tree for the recovery daemon to standard output. -

    eventscript ARGUMENTS

    - This is a debugging command. This command can be used to manually - invoke and run the eventscritps with arbitrary arguments. -

    ban BANTIME

    +

    ban BANTIME

    Administratively ban a node for BANTIME seconds. The node will be unbanned after BANTIME seconds have elapsed.

    @@ -869,21 +954,21 @@

    To administratively exclude a node from a cluster use the stop command. -

    unban

    +

    unban

    This command is used to unban a node that has either been administratively banned using the ban command or has been automatically banned. -

    check_srvids SRVID ...

    +

    check_srvids SRVID ...

    This command checks whether a set of srvid message ports are registered on the node or not. The command takes a list of values to check. -

    Example

    +      

    Example

     # ctdb check_srvids 1 2 3 14765
     Server id 0:1 does not exist
     Server id 0:2 does not exist
     Server id 0:3 does not exist
     Server id 0:14765 exists
    -	

    SEE ALSO

    +

    SEE ALSO

    ctdbd(1), onnode(1), diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdb.1.xml samba-4.6.5+dfsg/ctdb/doc/ctdb.1.xml --- samba-4.5.8+dfsg/ctdb/doc/ctdb.1.xml 2017-01-30 09:56:26.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdb.1.xml 2017-05-23 08:19:23.000000000 +0000 @@ -123,10 +123,10 @@ OPTIONS - -n PNN-LIST + -n PNN - The nodes specified by PNN-LIST should be queried for the + The node specified by PNN should be queried for the requested information. Default is to query the daemon running on the local host. @@ -206,7 +206,7 @@ -d --debug=DEBUGLEVEL - Change the debug level for the command. Default is NOTICE (2). + Change the debug level for the command. Default is NOTICE. @@ -615,46 +615,172 @@ - scriptstatus + event run|status|script list|script enable|script disable - This command displays which scripts where run in the previous monitoring cycle and the result of each script. If a script failed with an error, causing the node to become unhealthy, the output from that script is also shown. + This command is used to control event daemon and to inspect + status of various events. - - Example - -# ctdb scriptstatus -7 scripts were executed last monitoring cycle -00.ctdb Status:OK Duration:0.056 Tue Mar 24 18:56:57 2009 -10.interface Status:OK Duration:0.077 Tue Mar 24 18:56:57 2009 -11.natgw Status:OK Duration:0.039 Tue Mar 24 18:56:57 2009 -20.multipathd Status:OK Duration:0.038 Tue Mar 24 18:56:57 2009 -31.clamd Status:DISABLED -40.vsftpd Status:OK Duration:0.045 Tue Mar 24 18:56:57 2009 -41.httpd Status:OK Duration:0.039 Tue Mar 24 18:56:57 2009 -50.samba Status:ERROR Duration:0.082 Tue Mar 24 18:56:57 2009 -OUTPUT:ERROR: Samba tcp port 445 is not responding - - - - - disablescript <parameter>SCRIPT</parameter> - - This command is used to disable an eventscript. - - - This will take effect the next time the eventscripts are being executed so it can take a short while until this is reflected in 'scriptstatus'. - + + + run EVENT TIMEOUT ARGUMENTS + + + This command can be used to manually run specified EVENT + with optional ARGUMENTS. The event will be allowed to run + a maximum of TIMEOUT seconds. If TIMEOUT is 0, then there + is no time limit for running the event. + + + + + + status EVENT lastrun|lastpass|lastfail + + + This command displays the last execution status of the + specified EVENT. If no event is specified, then the status + of last executed monitor event will be displayed. + + + To see the last successful execution of the event, lastpass + can be specified. Similarly lastfail can be specified + to see the last unsuccessful execution of the event. + The optional lastrun can be specified to query the last + execution of the event. + + + The command will terminate with the exit status + corresponding to the overall status of event that is + displayed. If lastpass is specified, then the command will + always terminate with 0. If lastfail is specified then the + command will always terminate with non-zero exit status. + If lastrun is specified, then the command will terminate + with 0 or not depending on if the last execution of the + event was successful or not. + + + The output is the list of event scripts executed. + Each line shows the name, status, duration and start time + for each script. + + + Example output: + + +00.ctdb OK 0.014 Sat Dec 17 19:39:11 2016 +01.reclock OK 0.013 Sat Dec 17 19:39:11 2016 +05.system OK 0.029 Sat Dec 17 19:39:11 2016 +06.nfs OK 0.014 Sat Dec 17 19:39:11 2016 +10.external DISABLED +10.interface OK 0.037 Sat Dec 17 19:39:11 2016 +11.natgw OK 0.011 Sat Dec 17 19:39:11 2016 +11.routing OK 0.007 Sat Dec 17 19:39:11 2016 +13.per_ip_routing OK 0.007 Sat Dec 17 19:39:11 2016 +20.multipathd OK 0.007 Sat Dec 17 19:39:11 2016 +31.clamd OK 0.007 Sat Dec 17 19:39:11 2016 +40.vsftpd OK 0.013 Sat Dec 17 19:39:11 2016 +41.httpd OK 0.018 Sat Dec 17 19:39:11 2016 +49.winbind OK 0.023 Sat Dec 17 19:39:11 2016 +50.samba OK 0.100 Sat Dec 17 19:39:12 2016 +60.nfs OK 0.376 Sat Dec 17 19:39:12 2016 +70.iscsi OK 0.009 Sat Dec 17 19:39:12 2016 +91.lvs OK 0.007 Sat Dec 17 19:39:12 2016 +99.timeout OK 0.007 Sat Dec 17 19:39:12 2016 + + + + + + script list + + + List the available event scripts. + + + Example output: + + +00.ctdb +01.reclock +05.system +06.nfs +10.external DISABLED +10.interface +11.natgw +11.routing +13.per_ip_routing +20.multipathd +31.clamd +40.vsftpd +41.httpd +49.winbind +50.samba +60.nfs +70.iscsi +91.lvs +99.timeout + + + + + + script enable SCRIPT + + + Enable the specified event SCRIPT. Only enabled scripts will be + executed when running any event. + + + + + + script disable SCRIPT + + + Disable the specified event SCRIPT. This will prevent the script + from executing when running any event. + + + + - enablescript <parameter>SCRIPT</parameter> + scriptstatus - This command is used to enable an eventscript. + This command displays which event scripts where run in the previous + monitoring cycle and the result of each script. If a script + failed with an error, causing the node to become unhealthy, + the output from that script is also shown. - This will take effect the next time the eventscripts are being executed so it can take a short while until this is reflected in 'scriptstatus'. + This command is deprecated. It's provided for backward + compatibility. In place of ctdb scriptstatus, + use ctdb event status. + + Example + +# ctdb scriptstatus +00.ctdb OK 0.011 Sat Dec 17 19:40:46 2016 +01.reclock OK 0.010 Sat Dec 17 19:40:46 2016 +05.system OK 0.030 Sat Dec 17 19:40:46 2016 +06.nfs OK 0.014 Sat Dec 17 19:40:46 2016 +10.external DISABLED +10.interface OK 0.041 Sat Dec 17 19:40:46 2016 +11.natgw OK 0.008 Sat Dec 17 19:40:46 2016 +11.routing OK 0.007 Sat Dec 17 19:40:46 2016 +13.per_ip_routing OK 0.007 Sat Dec 17 19:40:46 2016 +20.multipathd OK 0.007 Sat Dec 17 19:40:46 2016 +31.clamd OK 0.007 Sat Dec 17 19:40:46 2016 +40.vsftpd OK 0.013 Sat Dec 17 19:40:46 2016 +41.httpd OK 0.015 Sat Dec 17 19:40:46 2016 +49.winbind OK 0.022 Sat Dec 17 19:40:46 2016 +50.samba ERROR 0.077 Sat Dec 17 19:40:46 2016 + OUTPUT: ERROR: samba tcp port 445 is not responding + + @@ -687,8 +813,6 @@ DatabaseMaxDead = 5 RerecoveryTimeout = 10 EnableBans = 1 -DeterministicIPs = 0 -LCP2PublicIPs = 1 NoIPFailback = 0 DisableIPFailover = 0 VerboseMemoryNames = 0 @@ -719,9 +843,11 @@ DBSizeWarn = 100000000 PullDBPreallocation = 10485760 NoIPHostOnAllDisabled = 0 -Samba3AvoidDeadlocks = 0 TDBMutexEnabled = 0 LockProcessesPerDB = 200 +RecBufferSizeLimit = 1000000 +QueueBufferSize = 1024 +IPAllocAlgorithm = 2 @@ -1075,7 +1201,7 @@ when you have changed the tunables for the daemon to: - DeterministicIPs = 0 + IPAllocAlgorithm != 0 NoIPFailback = 1 @@ -1666,14 +1792,6 @@ - - eventscript <parameter>ARGUMENTS</parameter> - - This is a debugging command. This command can be used to manually - invoke and run the eventscritps with arbitrary arguments. - - - ban <parameter>BANTIME</parameter> diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdb.7 samba-4.6.5+dfsg/ctdb/doc/ctdb.7 --- samba-4.5.8+dfsg/ctdb/doc/ctdb.7 2016-10-24 19:44:56.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdb.7 2017-06-06 07:49:59.000000000 +0000 @@ -2,12 +2,12 @@ .\" Title: ctdb .\" Author: .\" Generator: DocBook XSL Stylesheets v1.78.1 -.\" Date: 10/24/2016 +.\" Date: 06/06/2017 .\" Manual: CTDB - clustered TDB database .\" Source: ctdb .\" Language: English .\" -.TH "CTDB" "7" "10/24/2016" "ctdb" "CTDB \- clustered TDB database" +.TH "CTDB" "7" "06/06/2017" "ctdb" "CTDB \- clustered TDB database" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -715,19 +715,19 @@ .PP Valid values for DEBUGLEVEL are: .RS 4 -ERR (0) +ERR .RE .RS 4 -WARNING (1) +WARNING .RE .RS 4 -NOTICE (2) +NOTICE .RE .RS 4 -INFO (3) +INFO .RE .RS 4 -DEBUG (4) +DEBUG .RE .SH "REMOTE CLUSTER NODES" .PP diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdb.7.html samba-4.6.5+dfsg/ctdb/doc/ctdb.7.html --- samba-4.5.8+dfsg/ctdb/doc/ctdb.7.html 2016-10-24 19:44:56.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdb.7.html 2017-06-06 07:50:00.000000000 +0000 @@ -1,4 +1,4 @@ -ctdb

    Name

    ctdb — Clustered TDB

    DESCRIPTION

    +ctdb

    Name

    ctdb — Clustered TDB

    DESCRIPTION

    CTDB is a clustered database component in clustered Samba that provides a high-availability load-sharing CIFS server cluster.

    @@ -16,7 +16,7 @@ Combined with a cluster filesystem CTDB provides a full high-availablity (HA) environment for services such as clustered Samba, NFS and other services. -

    ANATOMY OF A CTDB CLUSTER

    +

    ANATOMY OF A CTDB CLUSTER

    A CTDB cluster is a collection of nodes with 2 or more network interfaces. All nodes provide network (usually file/NAS) services to clients. Data served by file services is stored on shared @@ -25,7 +25,7 @@

    CTDB provides an "all active" cluster, where services are load balanced across all nodes. -

    Recovery Lock

    +

    Recovery Lock

    CTDB uses a recovery lock to avoid a split brain, where a cluster becomes partitioned and each partition attempts to operate @@ -72,7 +72,7 @@

    CTDB can run without a recovery lock but this is not recommended as there will be no protection from split brains. -

    Private vs Public addresses

    +

    Private vs Public addresses

    Each node in a CTDB cluster has multiple IP addresses assigned to it: @@ -83,7 +83,7 @@ One or more public IP addresses that are used to provide NAS or other services.

    -

    Private address

    +

    Private address

    Each node is configured with a unique, permanently assigned private address. This address is configured by the operating system. This address uniquely identifies a physical node in @@ -117,7 +117,7 @@ 192.168.1.2 192.168.1.3 192.168.1.4 -

    Public addresses

    +

    Public addresses

    Public addresses are used to provide services to clients. Public addresses are not configured at the operating system level and are not permanently associated with a particular @@ -188,7 +188,7 @@

    The ctdb ip command can be used to view the current assignment of public addresses to physical nodes. -

    Node status

    +

    Node status

    The current status of each node in the cluster can be viewed by the ctdb status command.

    @@ -233,7 +233,7 @@ like a healthy (OK) node. Some interfaces to serve public addresses are down, but at least one interface is up. See also ctdb ifaces. -

    CAPABILITIES

    +

    CAPABILITIES

    Cluster nodes can have several different capabilities enabled. These are listed below.

    RECMASTER

    @@ -252,7 +252,7 @@ The RECMASTER and LMASTER capabilities can be disabled when CTDB is used to create a cluster spanning across WAN links. In this case CTDB acts as a WAN accelerator. -

    LVS

    +

    LVS

    LVS is a mode where CTDB presents one single IP address for the entire cluster. This is an alternative to using public IP addresses and round-robin DNS to loadbalance clients across the @@ -326,7 +326,7 @@ reachable from a node before you enable LVS. Also ensure that outgoing traffic to these hosts is routed out through the configured public interface. -

    Configuration

    +

    Configuration

    To activate LVS on a CTDB node you must specify the CTDB_LVS_PUBLIC_IFACE, CTDB_LVS_PUBLIC_IP and @@ -360,7 +360,7 @@ 192.168.1.2 192.168.1.3 192.168.1.4 slave-only -

    TRACKING AND RESETTING TCP CONNECTIONS

    +

    TRACKING AND RESETTING TCP CONNECTIONS

    CTDB tracks TCP connections from clients to public IP addresses, on known ports. When an IP address moves from one node to another, all existing TCP connections to that IP address are @@ -373,7 +373,7 @@ a release and take of a public IP address on the same node. Such connections can get out of sync with sequence and ACK numbers, potentially causing a disruptive ACK storm. -

    NAT GATEWAY

    +

    NAT GATEWAY

    NAT gateway (NATGW) is an optional feature that is used to configure fallback routing for nodes. This allows cluster nodes to connect to external services (e.g. DNS, AD, NIS and LDAP) @@ -390,7 +390,7 @@ extra static IP address to a public interface on every node. This is simpler but it uses an extra IP address per node, while NAT gateway generally uses only one extra IP address. -

    Operation

    +

    Operation

    One extra NATGW public address is assigned on the public network to each NATGW group. Each NATGW group is a set of nodes in the cluster that shares the same NATGW address to @@ -411,7 +411,7 @@ public IP address and routes outgoing connections from slave nodes via this IP address. It also establishes a fallback default route. -

    Configuration

    +

    Configuration

    NATGW is usually configured similar to the following example configuration:

     CTDB_NATGW_NODES=/usr/local/etc/ctdb/natgw_nodes
    @@ -430,7 +430,7 @@
     	See the NAT GATEWAY section in
     	ctdbd.conf(5) for more details of
     	NATGW configuration.
    -      

    Implementation details

    +

    Implementation details

    When the NATGW functionality is used, one of the nodes is selected to act as a NAT gateway for all the other nodes in the group when they need to communicate with the external @@ -465,7 +465,7 @@ eventscript. Please see the eventscript file and the NAT GATEWAY section in ctdbd.conf(5) for more details. -

    POLICY ROUTING

    +

    POLICY ROUTING

    Policy routing is an optional CTDB feature to support complex network topologies. Public addresses may be spread across several different networks (or VLANs) and it may not be possible @@ -475,7 +475,7 @@ This allows routing to be specified for packets sourced from each public address. The routes are added and removed as CTDB moves public addresses between nodes. -

    Configuration variables

    +

    Configuration variables

    There are 4 configuration variables related to policy routing: CTDB_PER_IP_ROUTING_CONF, CTDB_PER_IP_ROUTING_RULE_PREF, @@ -483,7 +483,7 @@ CTDB_PER_IP_ROUTING_TABLE_ID_HIGH. See the POLICY ROUTING section in ctdbd.conf(5) for more details. -

    Configuration

    +

    Configuration

    The format of each line of CTDB_PER_IP_ROUTING_CONF is:

    @@ -545,7 +545,7 @@
           

       192.168.1.0/24 dev eth2 scope link 
       default via 192.168.1.1 dev eth2 
    -      

    Sample configuration

    +

    Sample configuration

    Here is a more complete example configuration.

     /usr/local/etc/ctdb/public_addresses:
    @@ -565,7 +565,7 @@
     	The routes local packets as expected, the default route is as
     	previously discussed, but packets to 192.168.200.0/24 are
     	routed via the alternate gateway 192.168.1.254.
    -      

    NOTIFICATION SCRIPT

    +

    NOTIFICATION SCRIPT

    When certain state changes occur in CTDB, it can be configured to perform arbitrary actions via a notification script. For example, sending SNMP traps or emails when a node becomes @@ -581,9 +581,9 @@

    CTDB currently generates notifications after CTDB changes to these states: -

    init
    setup
    startup
    healthy
    unhealthy

    DEBUG LEVELS

    +

    init
    setup
    startup
    healthy
    unhealthy

    DEBUG LEVELS

    Valid values for DEBUGLEVEL are: -

    ERR (0)
    WARNING (1)
    NOTICE (2)
    INFO (3)
    DEBUG (4)

    REMOTE CLUSTER NODES

    +

    ERR
    WARNING
    NOTICE
    INFO
    DEBUG

    REMOTE CLUSTER NODES

    It is possible to have a CTDB cluster that spans across a WAN link. For example where you have a CTDB cluster in your datacentre but you also want to have one additional CTDB node located at a remote branch site. @@ -612,7 +612,7 @@

    Verify with the command "ctdb getcapabilities" that that node no longer has the recmaster or the lmaster capabilities. -

    SEE ALSO

    +

    SEE ALSO

    ctdb(1), ctdbd(1), diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdb.7.xml samba-4.6.5+dfsg/ctdb/doc/ctdb.7.xml --- samba-4.5.8+dfsg/ctdb/doc/ctdb.7.xml 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdb.7.xml 2017-01-11 07:55:14.000000000 +0000 @@ -990,11 +990,11 @@ - ERR (0) - WARNING (1) - NOTICE (2) - INFO (3) - DEBUG (4) + ERR + WARNING + NOTICE + INFO + DEBUG diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdbd.1 samba-4.6.5+dfsg/ctdb/doc/ctdbd.1 --- samba-4.5.8+dfsg/ctdb/doc/ctdbd.1 2016-10-24 19:44:54.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdbd.1 2017-06-06 07:49:56.000000000 +0000 @@ -2,12 +2,12 @@ .\" Title: ctdbd .\" Author: .\" Generator: DocBook XSL Stylesheets v1.78.1 -.\" Date: 10/24/2016 +.\" Date: 06/06/2017 .\" Manual: CTDB - clustered TDB database .\" Source: ctdb .\" Language: English .\" -.TH "CTDBD" "1" "10/24/2016" "ctdb" "CTDB \- clustered TDB database" +.TH "CTDBD" "1" "06/06/2017" "ctdb" "CTDB \- clustered TDB database" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -305,7 +305,7 @@ .PP \-\-script\-log\-level=\fIDEBUGLEVEL\fR .RS 4 -This option sets the debug level of event script output to DEBUGLEVEL\&. The default is ERR (0)\&. +This option sets the debug level of event script output to DEBUGLEVEL\&. The default is ERR\&. .sp See the DEBUG LEVELS diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdbd.1.html samba-4.6.5+dfsg/ctdb/doc/ctdbd.1.html --- samba-4.5.8+dfsg/ctdb/doc/ctdbd.1.html 2016-10-24 19:44:54.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdbd.1.html 2017-06-06 07:49:57.000000000 +0000 @@ -1,11 +1,11 @@ -ctdbd

    Name

    ctdbd — The CTDB cluster daemon

    Synopsis

    ctdbd [OPTION...]

    DESCRIPTION

    +ctdbd

    Name

    ctdbd — The CTDB cluster daemon

    Synopsis

    ctdbd [OPTION...]

    DESCRIPTION

    ctdbd is the main CTDB daemon.

    Note that ctdbd is not usually invoked directly. It is invoked via ctdbd_wrapper(1) or via the initscript.

    See ctdb(7) for an overview of CTDB. -

    GENERAL OPTIONS

    -d, --debug=DEBUGLEVEL

    +

    GENERAL OPTIONS

    -d, --debug=DEBUGLEVEL

    This option sets the debug level to DEBUGLEVEL, which controls what will be written by the logging subsystem. The default is 2. @@ -193,7 +193,7 @@ The "infiniband" support is not regularly tested.

    -?, --help

    Display a summary of options. -

    DEBUGGING OPTIONS

    -i, --interactive

    +

    DEBUGGING OPTIONS

    -i, --interactive

    Enable interactive mode. This will make ctdbd run in the foreground and not detach from the terminal. By default ctdbd will detach itself and run in the background as a @@ -228,7 +228,7 @@ for testing.

    --script-log-level=DEBUGLEVEL

    This option sets the debug level of event script output to - DEBUGLEVEL. The default is ERR (0). + DEBUGLEVEL. The default is ERR.

    See the DEBUG LEVELS section in ctdb(7) for more @@ -250,7 +250,7 @@ This is a debugging option. This option is only used when debugging ctdbd. This enables additional debugging capabilities and implies --nosetsched. -

    SEE ALSO

    +

    SEE ALSO

    ctdb(1), ctdbd_wrapper(1), diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdbd.1.xml samba-4.6.5+dfsg/ctdb/doc/ctdbd.1.xml --- samba-4.5.8+dfsg/ctdb/doc/ctdbd.1.xml 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdbd.1.xml 2017-01-11 07:55:14.000000000 +0000 @@ -519,7 +519,7 @@ This option sets the debug level of event script output to - DEBUGLEVEL. The default is ERR (0). + DEBUGLEVEL. The default is ERR. See the DEBUG LEVELS section in diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdbd.conf.5 samba-4.6.5+dfsg/ctdb/doc/ctdbd.conf.5 --- samba-4.5.8+dfsg/ctdb/doc/ctdbd.conf.5 2016-10-24 19:44:56.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdbd.conf.5 2017-06-06 07:49:59.000000000 +0000 @@ -2,12 +2,12 @@ .\" Title: ctdbd.conf .\" Author: .\" Generator: DocBook XSL Stylesheets v1.78.1 -.\" Date: 10/24/2016 +.\" Date: 06/06/2017 .\" Manual: CTDB - clustered TDB database .\" Source: ctdb .\" Language: English .\" -.TH "CTDBD\&.CONF" "5" "10/24/2016" "ctdb" "CTDB \- clustered TDB database" +.TH "CTDBD\&.CONF" "5" "06/06/2017" "ctdb" "CTDB \- clustered TDB database" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -129,7 +129,7 @@ .PP CTDB_DEBUGLEVEL=\fIDEBUGLEVEL\fR .RS 4 -Default is NOTICE (2)\&. Corresponds to +Default is NOTICE\&. Corresponds to \fB\-d\fR or \fB\-\-debug\fR\&. @@ -250,7 +250,7 @@ .PP CTDB_SCRIPT_LOG_LEVEL=\fIDEBUGLEVEL\fR .RS 4 -Defaults to ERR (0)\&. Corresponds to +Defaults to ERR\&. Corresponds to \fB\-\-script\-log\-level\fR\&. .RE .PP @@ -1043,11 +1043,11 @@ Default is no\&. .RE .PP -CTDB_SCRIPT_DEBUGLEVEL=\fINUM\fR +CTDB_SCRIPT_DEBUGLEVEL=\fIDEBUGLEVEL\fR .RS 4 -NUM is the level debugging messages printed by CTDB scripts\&. Setting this to a higher number (e\&.g\&. 4) will cause some scripts to log more messages\&. +DEBUGLEVEL is the level debugging messages printed by CTDB scripts\&. Setting this to a higher level (e\&.g\&. DEBUG) will cause some scripts to log more messages\&. .sp -Default is 2\&. +Default is NOTICE\&. .RE .PP CTDB_SUPPRESS_COREFILE=yes|no diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdbd.conf.5.html samba-4.6.5+dfsg/ctdb/doc/ctdbd.conf.5.html --- samba-4.5.8+dfsg/ctdb/doc/ctdbd.conf.5.html 2016-10-24 19:44:56.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdbd.conf.5.html 2017-06-06 07:49:59.000000000 +0000 @@ -1,4 +1,4 @@ -ctdbd.conf

    Name

    ctdbd.conf — CTDB daemon configuration file

    DESCRIPTION

    +ctdbd.conf

    Name

    ctdbd.conf — CTDB daemon configuration file

    DESCRIPTION

    This file contains CTDB configuration variables that are affect the operation of CTDB. The default location of this file is /usr/local/etc/ctdb/ctdbd.conf. @@ -17,7 +17,7 @@ A historical alternative is /usr/local/etc/ctdb/sysconfig/ctdb - this is deprecated. -

    +

    INITSCRIPT CONFIGURATION

    Some options must be available to the initscript so they need to @@ -32,14 +32,14 @@

    Default is /usr/local/var/run/ctdb/ctdbd.pid. Corresponds to --pidfile. -

    +

    GLOBAL CONFIGURATION

    These options may be used in the initscripts, daemon and scripts.

    CTDB_BASE=DIRECTORY

    DIRECTORY containing CTDB scripts and configuration files. -

    +

    DAEMON CONFIGURATION

    Variables in this section are processed by @@ -79,7 +79,7 @@

    Corresponds to --dbdir-state.

    CTDB_DEBUGLEVEL=DEBUGLEVEL

    - Default is NOTICE (2). Corresponds to -d or + Default is NOTICE. Corresponds to -d or --debug.

    CTDB_EVENT_SCRIPT_DIR=DIRECTORY

    Default is CTDB_BASE/events.d, so usually @@ -175,7 +175,7 @@ RECOVERY LOCK section in ctdb(7).

    CTDB_SCRIPT_LOG_LEVEL=DEBUGLEVEL

    - Defaults to ERR (0). Corresponds to + Defaults to ERR. Corresponds to --script-log-level.

    CTDB_SOCKET=FILENAME

    Defaults to /usr/local/var/run/ctdb/ctdbd.socket. @@ -214,7 +214,7 @@ "setup" event before this timeout then it is killed.

    Defaults is 10. -

    NETWORK CONFIGURATION

    NAT GATEWAY

    +

    NETWORK CONFIGURATION

    NAT GATEWAY

    NAT gateway is used to configure fallback routing for nodes when they do not host any public IP addresses. For example, it allows unhealthy nodes to reliably communicate with @@ -298,7 +298,7 @@ route to avoid this.

    No default. -

    Example

    +	    

    Example

     CTDB_NATGW_NODES=/usr/local/etc/ctdb/natgw_nodes
     CTDB_NATGW_PRIVATE_NETWORK=192.168.1.0/24
     CTDB_NATGW_DEFAULT_GATEWAY=10.0.0.1
    @@ -317,7 +317,7 @@
     	

    Note that CTDB_NATGW_DEFAULT_GATEWAY is not specified. -

    POLICY ROUTING

    +

    POLICY ROUTING

    A node running CTDB may be a component of a complex network topology. In particular, public addresses may be spread across several different networks (or VLANs) and it may not be @@ -381,15 +381,15 @@ manipulate).

    No default, usually 1000 and 9000. -

    Example

    +	    

    Example

     CTDB_PER_IP_ROUTING_CONF=/usr/local/etc/ctdb/policy_routing
     CTDB_PER_IP_ROUTING_RULE_PREF=100
     CTDB_PER_IP_ROUTING_TABLE_ID_LOW=1000
     CTDB_PER_IP_ROUTING_TABLE_ID_HIGH=9000
    -	

    LVS

    +

    LVS

    For a general description see the LVS section in ctdb(7). -

    Eventscript

    91.lvs
    CTDB_LVS_NODES=FILENAME

    +

    Eventscript

    91.lvs
    CTDB_LVS_NODES=FILENAME

    FILENAME contains the list of nodes that belong to the same LVS group.

    @@ -417,7 +417,7 @@

    CTDB_LVS_PUBLIC_IP=IPADDR

    CTDB_LVS_PUBLIC_IP is the LVS public address. No default. -

    MISCELLANEOUS NETWORK CONFIGURATION

    CTDB_PARTIALLY_ONLINE_INTERFACES=yes|no

    +

    MISCELLANEOUS NETWORK CONFIGURATION

    CTDB_PARTIALLY_ONLINE_INTERFACES=yes|no

    Whether one or more offline interfaces should cause a monitor event to fail if there are other interfaces that are up. If this is "yes" and a node has some interfaces @@ -430,7 +430,7 @@ to be up.

    Default is "no". -

    SERVICE CONFIGURATION

    +

    SERVICE CONFIGURATION

    CTDB can be configured to manage and/or monitor various NAS (and other) services via its eventscripts.

    @@ -439,7 +439,7 @@ monitor the service and CTDB will do any required reconfiguration of the service when public IP addresses are failed over. -

    SAMBA

    Eventscripts

    49.winbind
    50.samba
    CTDB_MANAGES_SAMBA=yes|no

    +

    SAMBA

    Eventscripts

    49.winbind
    50.samba
    CTDB_MANAGES_SAMBA=yes|no

    Should CTDB manage Samba?

    Default is no. @@ -471,11 +471,11 @@ Distribution specific SERVICE for managing winbindd.

    Default is "winbind". -

    NFS

    +

    NFS

    This includes parameters for the kernel NFS server. Alternative NFS subsystems (such as NFS-Ganesha) can be integrated using CTDB_NFS_CALLOUT. -

    Eventscript

    60.nfs
    CTDB_MANAGES_NFS=yes|no

    +

    Eventscript

    60.nfs
    CTDB_MANAGES_NFS=yes|no

    Should CTDB manage NFS?

    Default is no. @@ -515,16 +515,16 @@

    CTDB_NFS_STATE_MNT=DIR

    The directory where a clustered NFS' shared state will be located. No default. -

    APACHE HTTPD

    +

    APACHE HTTPD

    CTDB can manage the Apache web server. -

    Eventscript

    41.httpd
    CTDB_MANAGES_HTTPD=yes|no

    +

    Eventscript

    41.httpd
    CTDB_MANAGES_HTTPD=yes|no

    Should CTDB manage the Apache web server?

    Default is no. -

    CLAMAV

    +

    CLAMAV

    CTDB has support to manage the popular anti-virus daemon ClamAV. -

    Eventscript

    31.clamd

    +

    Eventscript

    31.clamd

    This eventscript is not enabled by default. Use ctdb enablescript to enable it.

    CTDB_MANAGES_CLAMD=yes|no

    @@ -535,9 +535,9 @@ FILENAME is the socket to monitor ClamAV.

    No default. -

    ISCSI

    +

    ISCSI

    CTDB has support for managing the Linux iSCSI tgtd service. -

    Eventscript

    70.iscsi
    CTDB_MANAGES_ISCSI=yes|no

    +

    Eventscript

    70.iscsi
    CTDB_MANAGES_ISCSI=yes|no

    Should CTDB manage iSCSI tgtd?

    Default is no. @@ -546,23 +546,23 @@ tgtd for each public IP address.

    No default. -

    MULTIPATHD

    +

    MULTIPATHD

    CTDB can monitor multipath devices to ensure that active paths are available. -

    Eventscript

    20.multipathd

    +

    Eventscript

    20.multipathd

    This eventscript is not enabled by default. Use ctdb enablescript to enable it.

    CTDB_MONITOR_MPDEVICES=MP-DEVICE-LIST

    MP-DEVICE-LIST is a list of multipath devices for CTDB to monitor?

    No default. -

    VSFTPD

    +

    VSFTPD

    CTDB can manage the vsftpd FTP server. -

    Eventscript

    40.vsftpd
    CTDB_MANAGES_VSFTPD=yes|no

    +

    Eventscript

    40.vsftpd
    CTDB_MANAGES_VSFTPD=yes|no

    Should CTDB manage the vsftpd FTP server?

    Default is no. -

    +

    SYSTEM RESOURCE MONITORING CONFIGURATION

    CTDB can experience seemingly random (performance and other) @@ -575,7 +575,7 @@ Some checks are enabled by default. It is recommended that these checks remain enabled or are augmented by extra checks. There is no supported way of completely disabling the checks. -

    Eventscripts

    05.system

    +

    Eventscripts

    05.system

    Filesystem and memory usage monitoring is in 05.system.

    CTDB_MONITOR_FILESYSTEM_USAGE=FS-LIMIT-LIST

    @@ -614,7 +614,7 @@

    Default is 25, so warnings will be logged when swap usage reaches 25%. -

    MISCELLANEOUS SERVICE-RELATED CONFIGURATION

    CTDB_MANAGED_SERVICES=SERVICE-LIST

    +

    MISCELLANEOUS SERVICE-RELATED CONFIGURATION

    CTDB_MANAGED_SERVICES=SERVICE-LIST

    SERVICE-LIST is a space-separated list of SERVICEs that CTDB should manage. This can be used as an alternative to the @@ -627,7 +627,7 @@ managed or unmanaged.

    Default is no. -

    +

    TUNABLES CONFIGURATION

    CTDB tunables (see @@ -643,7 +643,7 @@

     CTDB_SET_MonitorInterval=20
           

    -

    +

    DEBUG AND TEST

    Variable in this section are for debugging and testing CTDB. @@ -722,12 +722,12 @@ This uses the 99.timeout eventscript.

    Default is no. -

    CTDB_SCRIPT_DEBUGLEVEL=NUM

    - NUM is the level debugging messages printed by CTDB - scripts. Setting this to a higher number (e.g. 4) will +

    CTDB_SCRIPT_DEBUGLEVEL=DEBUGLEVEL

    + DEBUGLEVEL is the level debugging messages printed by CTDB + scripts. Setting this to a higher level (e.g. DEBUG) will cause some scripts to log more messages.

    - Default is 2. + Default is NOTICE.

    CTDB_SUPPRESS_COREFILE=yes|no

    Whether CTDB core files should be suppressed.

    @@ -750,7 +750,7 @@ runtime.

    Defaults to /usr/local/var/lib/ctdb. -

    FILES

    /usr/local/etc/ctdb/ctdbd.conf
    /etc/sysconfig/ctdb
    /etc/default/ctdb
    /usr/local/etc/ctdb/sysconfig/ctdb

    SEE ALSO

    +

    FILES

    /usr/local/etc/ctdb/ctdbd.conf
    /etc/sysconfig/ctdb
    /etc/default/ctdb
    /usr/local/etc/ctdb/sysconfig/ctdb

    SEE ALSO

    ctdbd(1), ctdbd_wrapper(1), diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdbd.conf.5.xml samba-4.6.5+dfsg/ctdb/doc/ctdbd.conf.5.xml --- samba-4.5.8+dfsg/ctdb/doc/ctdbd.conf.5.xml 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdbd.conf.5.xml 2017-01-11 07:55:14.000000000 +0000 @@ -203,7 +203,7 @@ CTDB_DEBUGLEVEL=DEBUGLEVEL - Default is NOTICE (2). Corresponds to or + Default is NOTICE. Corresponds to or . @@ -418,7 +418,7 @@ CTDB_SCRIPT_LOG_LEVEL=DEBUGLEVEL - Defaults to ERR (0). Corresponds to + Defaults to ERR. Corresponds to . @@ -1697,15 +1697,15 @@ - CTDB_SCRIPT_DEBUGLEVEL=NUM + CTDB_SCRIPT_DEBUGLEVEL=DEBUGLEVEL - NUM is the level debugging messages printed by CTDB - scripts. Setting this to a higher number (e.g. 4) will + DEBUGLEVEL is the level debugging messages printed by CTDB + scripts. Setting this to a higher level (e.g. DEBUG) will cause some scripts to log more messages. - Default is 2. + Default is NOTICE. diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdb_diagnostics.1 samba-4.6.5+dfsg/ctdb/doc/ctdb_diagnostics.1 --- samba-4.5.8+dfsg/ctdb/doc/ctdb_diagnostics.1 2016-10-24 19:44:53.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdb_diagnostics.1 2017-06-06 07:49:58.000000000 +0000 @@ -2,12 +2,12 @@ .\" Title: ctdb_diagnostics .\" Author: .\" Generator: DocBook XSL Stylesheets v1.78.1 -.\" Date: 10/24/2016 +.\" Date: 06/06/2017 .\" Manual: CTDB - clustered TDB database .\" Source: ctdb .\" Language: English .\" -.TH "CTDB_DIAGNOSTICS" "1" "10/24/2016" "ctdb" "CTDB \- clustered TDB database" +.TH "CTDB_DIAGNOSTICS" "1" "06/06/2017" "ctdb" "CTDB \- clustered TDB database" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdb_diagnostics.1.html samba-4.6.5+dfsg/ctdb/doc/ctdb_diagnostics.1.html --- samba-4.5.8+dfsg/ctdb/doc/ctdb_diagnostics.1.html 2016-10-24 19:44:54.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdb_diagnostics.1.html 2017-06-06 07:49:58.000000000 +0000 @@ -1,10 +1,10 @@ -ctdb_diagnostics

    Name

    ctdb_diagnostics — dump diagnostic information about CTDB/Samba installation

    Synopsis

    ctdb_diagnostics [OPTIONS] ...

    DESCRIPTION

    +ctdb_diagnostics

    Name

    ctdb_diagnostics — dump diagnostic information about CTDB/Samba installation

    Synopsis

    ctdb_diagnostics [OPTIONS] ...

    DESCRIPTION

    ctdb_diagnostics is used to dump diagnostic information about a clustered Samba installation. This includes configuration files, output of relevant commands and logs. This information can be used to check the correctness of the configuration and to diagnose problems. -

    OPTIONS

    -n <nodes>

    +

    OPTIONS

    -n <nodes>

    Comma separated list of nodes to operate on

    -c

    Ignore comment lines (starting with '#') in file comparisons @@ -12,7 +12,7 @@ Ignore whitespace in file comparisons

    --no-ads

    Do not use commands that assume an Active Directory Server -

    SEE ALSO

    +

    SEE ALSO

    ctdb(1), ctdb(7), https://ctdb.samba.org/ diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdbd_wrapper.1 samba-4.6.5+dfsg/ctdb/doc/ctdbd_wrapper.1 --- samba-4.5.8+dfsg/ctdb/doc/ctdbd_wrapper.1 2016-10-24 19:44:54.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdbd_wrapper.1 2017-06-06 07:49:58.000000000 +0000 @@ -2,12 +2,12 @@ .\" Title: ctdbd_wrapper .\" Author: .\" Generator: DocBook XSL Stylesheets v1.78.1 -.\" Date: 10/24/2016 +.\" Date: 06/06/2017 .\" Manual: CTDB - clustered TDB database .\" Source: ctdb .\" Language: English .\" -.TH "CTDBD_WRAPPER" "1" "10/24/2016" "ctdb" "CTDB \- clustered TDB database" +.TH "CTDBD_WRAPPER" "1" "06/06/2017" "ctdb" "CTDB \- clustered TDB database" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdbd_wrapper.1.html samba-4.6.5+dfsg/ctdb/doc/ctdbd_wrapper.1.html --- samba-4.5.8+dfsg/ctdb/doc/ctdbd_wrapper.1.html 2016-10-24 19:44:54.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdbd_wrapper.1.html 2017-06-06 07:49:58.000000000 +0000 @@ -1,4 +1,4 @@ -ctdbd_wrapper

    Name

    ctdbd_wrapper — Wrapper for ctdbd

    Synopsis

    ctdbd_wrapper {PIDFILE} { start | stop }

    DESCRIPTION

    +ctdbd_wrapper

    Name

    ctdbd_wrapper — Wrapper for ctdbd

    Synopsis

    ctdbd_wrapper {PIDFILE} { start | stop }

    DESCRIPTION

    ctdbd_wrapper is used to start or stop the main CTDB daemon.

    PIDFILE specifies the location of the @@ -9,7 +9,7 @@ ctdbd.conf(5).

    See ctdb(7) for an overview of CTDB. -

    SEE ALSO

    +

    SEE ALSO

    ctdbd(1), ctdbd.conf(5), diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdb-etcd.7 samba-4.6.5+dfsg/ctdb/doc/ctdb-etcd.7 --- samba-4.5.8+dfsg/ctdb/doc/ctdb-etcd.7 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdb-etcd.7 2017-06-06 07:50:01.000000000 +0000 @@ -0,0 +1,106 @@ +'\" t +.\" Title: ctdb-etcd +.\" Author: +.\" Generator: DocBook XSL Stylesheets v1.78.1 +.\" Date: 06/06/2017 +.\" Manual: CTDB - clustered TDB database +.\" Source: ctdb +.\" Language: English +.\" +.TH "CTDB\-ETCD" "7" "06/06/2017" "ctdb" "CTDB \- clustered TDB database" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +ctdb-etcd \- CTDB etcd integration +.SH "SYNOPSIS" +.HP \w'\fBctdb_etcd_lock\fR\ 'u +\fBctdb_etcd_lock\fR +.SH "DESCRIPTION" +.PP +ctdb_etcd_lock is intended to be run as a mutex helper for CTDB\&. It will try to connect to an existing etcd cluster and grab a lock in that cluster to function as CTDB\*(Aqs recovery lock\&. Please see +\fIctdb/doc/cluster_mutex_helper\&.txt\fR +for details on the mutex helper API\&. To use this, include the following line in your CTDB config file: +.sp +.if n \{\ +.RS 4 +.\} +.nf +CTDB_RECOVERY_LOCK="!/usr/local/usr/libexec/ctdb/ctdb_etcd_lock" + +.fi +.if n \{\ +.RE +.\} +.PP +You can also pass "\-v", "\-vv", or "\-vvv" to include verbose output in the CTDB log\&. Additional "v"s indicate increases in verbosity\&. +.PP +This mutex helper expects the system Python interpreter to have access to the etcd Python module\&. It also expects an etcd cluster to be configured and running\&. To integrate with this, there is an optional config file of the following format: +.sp +.if n \{\ +.RS 4 +.\} +.nf +key = value + +.fi +.if n \{\ +.RE +.\} +.PP +The following configuration parameters (and their defaults) are defined for use by ctdb_etcd_lock: +.sp +.if n \{\ +.RS 4 +.\} +.nf +port = 2379 # connecting port for the etcd cluster +lock_ttl = 9 # seconds for TTL +refresh = 2 # seconds between attempts to maintain lock +locks_dir = _ctdb # where to store CTDB locks in etcd + # The final etcd directory for any given lock looks like: + # /_locks/{locks_dir}/{netbios name}/ + +.fi +.if n \{\ +.RE +.\} +.PP +In addition, any keyword parameter that can be used to configure an etcd client may be specified and modified here\&. For more documentation on these parameters, see here: https://github\&.com/jplana/python\-etcd/ +.SH "SEE ALSO" +.PP +\fBctdb\fR(7), +\fBctdbd\fR(1), +\m[blue]\fB\%http://ctdb.samba.org/\fR\m[] +.SH "AUTHOR" +.br +.PP +This documentation was written by Jose A\&. Rivera +.SH "COPYRIGHT" +.br +Copyright \(co 2016 Jose A. Rivera +.br +.PP +This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version\&. +.PP +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. See the GNU General Public License for more details\&. +.PP +You should have received a copy of the GNU General Public License along with this program; if not, see +\m[blue]\fB\%http://www.gnu.org/licenses\fR\m[]\&. +.sp diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdb-etcd.7.html samba-4.6.5+dfsg/ctdb/doc/ctdb-etcd.7.html --- samba-4.5.8+dfsg/ctdb/doc/ctdb-etcd.7.html 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdb-etcd.7.html 2017-06-06 07:50:01.000000000 +0000 @@ -0,0 +1,40 @@ +ctdb-etcd

    Name

    ctdb-etcd — CTDB etcd integration

    Synopsis

    ctdb_etcd_lock

    DESCRIPTION

    + ctdb_etcd_lock is intended to be run as a mutex helper for CTDB. It + will try to connect to an existing etcd cluster and grab a lock in that + cluster to function as CTDB's recovery lock. Please see + ctdb/doc/cluster_mutex_helper.txt for details on + the mutex helper API. To use this, include the following line in your + CTDB config file: +

    +CTDB_RECOVERY_LOCK="!/usr/local/usr/libexec/ctdb/ctdb_etcd_lock"
    +    

    + You can also pass "-v", "-vv", or "-vvv" to include verbose output in + the CTDB log. Additional "v"s indicate increases in verbosity. +

    + This mutex helper expects the system Python interpreter to have access + to the etcd Python module. It also expects an etcd cluster to be + configured and running. To integrate with this, there is an optional + config file of the following format: +

    +key = value
    +    

    + The following configuration parameters (and their defaults) are defined + for use by ctdb_etcd_lock: +

    +port      = 2379   # connecting port for the etcd cluster
    +lock_ttl  = 9      # seconds for TTL
    +refresh   = 2      # seconds between attempts to maintain lock
    +locks_dir = _ctdb  # where to store CTDB locks in etcd
    +                   # The final etcd directory for any given lock looks like:
    +                   #   /_locks/{locks_dir}/{netbios name}/
    +    

    + In addition, any keyword parameter that can be used to configure an + etcd client may be specified and modified here. For more documentation + on these parameters, see here: https://github.com/jplana/python-etcd/ +

    SEE ALSO

    + ctdb(7), + + ctdbd(1), + + http://ctdb.samba.org/ +

    diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdb-etcd.7.xml samba-4.6.5+dfsg/ctdb/doc/ctdb-etcd.7.xml --- samba-4.5.8+dfsg/ctdb/doc/ctdb-etcd.7.xml 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdb-etcd.7.xml 2017-01-11 07:55:14.000000000 +0000 @@ -0,0 +1,117 @@ + + + + + + + + This documentation was written by + Jose A. Rivera + + + + + 2016 + Jose A. Rivera + + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the GNU General Public License for more details. + + + You should have received a copy of the GNU General Public + License along with this program; if not, see + . + + + + + + ctdb-etcd + 7 + ctdb + CTDB - clustered TDB database + + + + ctdb-etcd + CTDB etcd integration + + + + + ctdb_etcd_lock + + + + + DESCRIPTION + + ctdb_etcd_lock is intended to be run as a mutex helper for CTDB. It + will try to connect to an existing etcd cluster and grab a lock in that + cluster to function as CTDB's recovery lock. Please see + ctdb/doc/cluster_mutex_helper.txt for details on + the mutex helper API. To use this, include the following line in your + CTDB config file: + + +CTDB_RECOVERY_LOCK="!/usr/local/usr/libexec/ctdb/ctdb_etcd_lock" + + + You can also pass "-v", "-vv", or "-vvv" to include verbose output in + the CTDB log. Additional "v"s indicate increases in verbosity. + + + This mutex helper expects the system Python interpreter to have access + to the etcd Python module. It also expects an etcd cluster to be + configured and running. To integrate with this, there is an optional + config file of the following format: + + +key = value + + + The following configuration parameters (and their defaults) are defined + for use by ctdb_etcd_lock: + + +port = 2379 # connecting port for the etcd cluster +lock_ttl = 9 # seconds for TTL +refresh = 2 # seconds between attempts to maintain lock +locks_dir = _ctdb # where to store CTDB locks in etcd + # The final etcd directory for any given lock looks like: + # /_locks/{locks_dir}/{netbios name}/ + + + In addition, any keyword parameter that can be used to configure an + etcd client may be specified and modified here. For more documentation + on these parameters, see here: https://github.com/jplana/python-etcd/ + + + + + SEE ALSO + + ctdb + 7, + + ctdbd + 1, + + + + + + + diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdb_mutex_ceph_rados_helper.7 samba-4.6.5+dfsg/ctdb/doc/ctdb_mutex_ceph_rados_helper.7 --- samba-4.5.8+dfsg/ctdb/doc/ctdb_mutex_ceph_rados_helper.7 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdb_mutex_ceph_rados_helper.7 2017-06-06 07:50:01.000000000 +0000 @@ -0,0 +1,79 @@ +'\" t +.\" Title: Ceph RADOS Mutex +.\" Author: +.\" Generator: DocBook XSL Stylesheets v1.78.1 +.\" Date: 06/06/2017 +.\" Manual: CTDB - clustered TDB database +.\" Source: ctdb +.\" Language: English +.\" +.TH "CEPH RADOS MUTEX" "7" "06/06/2017" "ctdb" "CTDB \- clustered TDB database" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +ctdb_mutex_ceph_rados_helper \- Ceph RADOS cluster mutex helper +.SH "DESCRIPTION" +.PP +ctdb_mutex_ceph_rados_helper can be used as a recovery lock provider for CTDB\&. When configured, split brain avoidance during CTDB recovery will be handled using locks against an object located in a Ceph RADOS pool\&. To enable this functionality, include the following line in your CTDB config file: +.sp +.if n \{\ +.RS 4 +.\} +.nf +CTDB_RECOVERY_LOCK="!ctdb_mutex_ceph_rados_helper [Cluster] [User] [Pool] [Object]" + +Cluster: Ceph cluster name (e\&.g\&. ceph) +User: Ceph cluster user name (e\&.g\&. client\&.admin) +Pool: Ceph RADOS pool name +Object: Ceph RADOS object name + +.fi +.if n \{\ +.RE +.\} +.PP +The Ceph cluster +\fICluster\fR +must be up and running, with a configuration, and keyring file for +\fIUser\fR +located in a librados default search path (e\&.g\&. /etc/ceph/)\&. +\fIPool\fR +must already exist\&. +.SH "SEE ALSO" +.PP +\fBctdb\fR(7), +\fBctdbd\fR(1), +\m[blue]\fB\%http://ctdb.samba.org/\fR\m[] +.SH "AUTHOR" +.br +.PP +This documentation was written by David Disseldorp +.SH "COPYRIGHT" +.br +Copyright \(co 2016 David Disseldorp +.br +.PP +This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version\&. +.PP +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. See the GNU General Public License for more details\&. +.PP +You should have received a copy of the GNU General Public License along with this program; if not, see +\m[blue]\fB\%http://www.gnu.org/licenses\fR\m[]\&. +.sp diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdb_mutex_ceph_rados_helper.7.html samba-4.6.5+dfsg/ctdb/doc/ctdb_mutex_ceph_rados_helper.7.html --- samba-4.5.8+dfsg/ctdb/doc/ctdb_mutex_ceph_rados_helper.7.html 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdb_mutex_ceph_rados_helper.7.html 2017-06-06 07:50:01.000000000 +0000 @@ -0,0 +1,26 @@ +Ceph RADOS Mutex

    Name

    ctdb_mutex_ceph_rados_helper — Ceph RADOS cluster mutex helper

    DESCRIPTION

    + ctdb_mutex_ceph_rados_helper can be used as a recovery lock provider + for CTDB. When configured, split brain avoidance during CTDB recovery + will be handled using locks against an object located in a Ceph RADOS + pool. + To enable this functionality, include the following line in your CTDB + config file: +

    +CTDB_RECOVERY_LOCK="!ctdb_mutex_ceph_rados_helper [Cluster] [User] [Pool] [Object]"
    +
    +Cluster: Ceph cluster name (e.g. ceph)
    +User: Ceph cluster user name (e.g. client.admin)
    +Pool: Ceph RADOS pool name
    +Object: Ceph RADOS object name
    +    

    + The Ceph cluster Cluster must be up and running, + with a configuration, and keyring file for User + located in a librados default search path (e.g. /etc/ceph/). + Pool must already exist. +

    SEE ALSO

    + ctdb(7), + + ctdbd(1), + + http://ctdb.samba.org/ +

    diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdb_mutex_ceph_rados_helper.7.xml samba-4.6.5+dfsg/ctdb/doc/ctdb_mutex_ceph_rados_helper.7.xml --- samba-4.5.8+dfsg/ctdb/doc/ctdb_mutex_ceph_rados_helper.7.xml 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdb_mutex_ceph_rados_helper.7.xml 2017-01-11 07:55:14.000000000 +0000 @@ -0,0 +1,90 @@ + + + + + + Ceph RADOS Mutex + 7 + ctdb + CTDB - clustered TDB database + + + + ctdb_mutex_ceph_rados_helper + Ceph RADOS cluster mutex helper + + + + DESCRIPTION + + ctdb_mutex_ceph_rados_helper can be used as a recovery lock provider + for CTDB. When configured, split brain avoidance during CTDB recovery + will be handled using locks against an object located in a Ceph RADOS + pool. + To enable this functionality, include the following line in your CTDB + config file: + + +CTDB_RECOVERY_LOCK="!ctdb_mutex_ceph_rados_helper [Cluster] [User] [Pool] [Object]" + +Cluster: Ceph cluster name (e.g. ceph) +User: Ceph cluster user name (e.g. client.admin) +Pool: Ceph RADOS pool name +Object: Ceph RADOS object name + + + The Ceph cluster Cluster must be up and running, + with a configuration, and keyring file for User + located in a librados default search path (e.g. /etc/ceph/). + Pool must already exist. + + + + + SEE ALSO + + ctdb + 7, + + ctdbd + 1, + + + + + + + + + This documentation was written by David Disseldorp + + + + + 2016 + David Disseldorp + + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the GNU General Public License for more details. + + + You should have received a copy of the GNU General Public + License along with this program; if not, see + . + + + + + diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdb-statistics.7 samba-4.6.5+dfsg/ctdb/doc/ctdb-statistics.7 --- samba-4.5.8+dfsg/ctdb/doc/ctdb-statistics.7 2016-10-24 19:44:57.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdb-statistics.7 2017-06-06 07:50:00.000000000 +0000 @@ -2,12 +2,12 @@ .\" Title: ctdb-statistics .\" Author: .\" Generator: DocBook XSL Stylesheets v1.78.1 -.\" Date: 10/24/2016 +.\" Date: 06/06/2017 .\" Manual: CTDB - clustered TDB database .\" Source: ctdb .\" Language: English .\" -.TH "CTDB\-STATISTICS" "7" "10/24/2016" "ctdb" "CTDB \- clustered TDB database" +.TH "CTDB\-STATISTICS" "7" "06/06/2017" "ctdb" "CTDB \- clustered TDB database" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdb-statistics.7.html samba-4.6.5+dfsg/ctdb/doc/ctdb-statistics.7.html --- samba-4.5.8+dfsg/ctdb/doc/ctdb-statistics.7.html 2016-10-24 19:44:57.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdb-statistics.7.html 2017-06-06 07:50:00.000000000 +0000 @@ -1,10 +1,10 @@ -ctdb-statistics

    Name

    ctdb-statistics — CTDB statistics output

    OVERALL STATISTICS

    +ctdb-statistics

    Name

    ctdb-statistics — CTDB statistics output

    OVERALL STATISTICS

    CTDB maintains information about various messages communicated and some of the important operations per node. See the ctdb(1) commands statistics and statisticsreset for displaying statistics. -

    Example: ctdb statistics

    +    

    Example: ctdb statistics

     CTDB version 1
     Current time of statistics  :                Fri Sep 12 13:32:32 2014
     Statistics collected since  : (000 01:49:20) Fri Sep 12 11:43:12 2014
    @@ -55,151 +55,151 @@
      reclock_recd       MIN/AVG/MAX     0.000000/0.000000/0.000000 sec out of 0
      call_latency       MIN/AVG/MAX     0.000006/0.000719/4.562991 sec out of 126626
      childwrite_latency MIN/AVG/MAX     0.014527/0.014527/0.014527 sec out of 1
    -	

    CTDB version

    +

    CTDB version

    Version of the ctdb protocol used by the node. -

    Current time of statistics

    +

    Current time of statistics

    Time when the statistics are generated.

    This is useful when collecting statistics output periodically for post-processing. -

    Statistics collected since

    +

    Statistics collected since

    Time when ctdb was started or the last time statistics was reset. The output shows the duration and the timestamp. -

    num_clients

    +

    num_clients

    Number of processes currently connected to CTDB's unix socket. This includes recovery daemon, ctdb tool and samba processes (smbd, winbindd). -

    frozen

    +

    frozen

    1 if the the databases are currently frozen, 0 otherwise. -

    recovering

    +

    recovering

    1 if recovery is active, 0 otherwise. -

    num_recoveries

    +

    num_recoveries

    Number of recoveries since the start of ctdb or since the last statistics reset. -

    client_packets_sent

    +

    client_packets_sent

    Number of packets sent to client processes via unix domain socket. -

    client_packets_recv

    +

    client_packets_recv

    Number of packets received from client processes via unix domain socket. -

    node_packets_sent

    +

    node_packets_sent

    Number of packets sent to the other nodes in the cluster via TCP. -

    node_packets_recv

    +

    node_packets_recv

    Number of packets received from the other nodes in the cluster via TCP. -

    keepalive_packets_sent

    +

    keepalive_packets_sent

    Number of keepalive messages sent to other nodes.

    CTDB periodically sends keepalive messages to other nodes. See KeepaliveInterval tunable in ctdb-tunables(7) for more details. -

    keepalive_packets_recv

    +

    keepalive_packets_recv

    Number of keepalive messages received from other nodes. -

    node

    +

    node

    This section lists various types of messages processed which originated from other nodes via TCP. -

    req_call

    +

    req_call

    Number of REQ_CALL messages from the other nodes. -

    reply_call

    +

    reply_call

    Number of REPLY_CALL messages from the other nodes. -

    req_dmaster

    +

    req_dmaster

    Number of REQ_DMASTER messages from the other nodes. -

    reply_dmaster

    +

    reply_dmaster

    Number of REPLY_DMASTER messages from the other nodes. -

    reply_error

    +

    reply_error

    Number of REPLY_ERROR messages from the other nodes. -

    req_message

    +

    req_message

    Number of REQ_MESSAGE messages from the other nodes. -

    req_control

    +

    req_control

    Number of REQ_CONTROL messages from the other nodes. -

    reply_control

    +

    reply_control

    Number of REPLY_CONTROL messages from the other nodes. -

    client

    +

    client

    This section lists various types of messages processed which originated from clients via unix domain socket. -

    req_call

    +

    req_call

    Number of REQ_CALL messages from the clients. -

    req_message

    +

    req_message

    Number of REQ_MESSAGE messages from the clients. -

    req_control

    +

    req_control

    Number of REQ_CONTROL messages from the clients. -

    timeouts

    +

    timeouts

    This section lists timeouts occurred when sending various messages. -

    call

    +

    call

    Number of timeouts for REQ_CALL messages. -

    control

    +

    control

    Number of timeouts for REQ_CONTROL messages. -

    traverse

    +

    traverse

    Number of timeouts for database traverse operations. -

    locks

    +

    locks

    This section lists locking statistics. -

    num_calls

    +

    num_calls

    Number of completed lock calls. This includes database locks and record locks. -

    num_current

    +

    num_current

    Number of scheduled lock calls. This includes database locks and record locks. -

    num_pending

    +

    num_pending

    Number of queued lock calls. This includes database locks and record locks. -

    num_failed

    +

    num_failed

    Number of failed lock calls. This includes database locks and record locks. -

    total_calls

    +

    total_calls

    Number of req_call messages processed from clients. This number should be same as client --> req_call. -

    pending_calls

    +

    pending_calls

    Number of req_call messages which are currenly being processed. This number indicates the number of record migrations in flight. -

    childwrite_calls

    +

    childwrite_calls

    Number of record update calls. Record update calls are used to update a record under a transaction. -

    pending_childwrite_calls

    +

    pending_childwrite_calls

    Number of record update calls currently active. -

    memory_used

    +

    memory_used

    The amount of memory in bytes currently used by CTDB using talloc. This includes all the memory used for CTDB's internal data structures. This does not include the memory mapped TDB databases. -

    max_hop_count

    +

    max_hop_count

    The maximum number of hops required for a record migration request to obtain the record. High numbers indicate record contention. -

    total_ro_delegations

    +

    total_ro_delegations

    Number of readonly delegations created. -

    total_ro_revokes

    +

    total_ro_revokes

    Number of readonly delegations that were revoked. The difference between total_ro_revokes and total_ro_delegations gives the number of currently active readonly delegations. -

    hop_count_buckets

    +

    hop_count_buckets

    Distribution of migration requests based on hop counts values. Buckets are 1, < 4, < 8, < 16, < 32, < 64, < 128, < 256, < 512, ≥ 512. -

    lock_buckets

    +

    lock_buckets

    Distribution of record lock requests based on time required to obtain locks. Buckets are < 1ms, < 10ms, < 100ms, < 1s, < 2s, < 4s, < 8s, < 16s, < 32s, < 64s, ≥ 64s. -

    locks_latency

    +

    locks_latency

    The minimum, the average and the maximum time (in seconds) required to obtain record locks. -

    reclock_ctdbd

    +

    reclock_ctdbd

    The minimum, the average and the maximum time (in seconds) required to check if recovery lock is still held by recovery daemon when recovery mode is changed. This check is done in ctdb daemon. -

    reclock_recd

    +

    reclock_recd

    The minimum, the average and the maximum time (in seconds) required to check if recovery lock is still held by recovery daemon during recovery. This check is done in recovery daemon. -

    call_latency

    +

    call_latency

    The minimum, the average and the maximum time (in seconds) required to process a REQ_CALL message from client. This includes the time required to migrate a record from remote node, if the record is not available on the local node. -

    childwrite_latency

    Default: 0

    +

    childwrite_latency

    Default: 0

    The minimum, the average and the maximum time (in seconds) required to update records under a transaction. -

    DATABASE STATISTICS

    +

    DATABASE STATISTICS

    CTDB maintains per database statistics about important operations. See the ctdb(1) command dbstatistics for displaying database statistics. -

    Example: ctdb dbstatistics notify_index.tdb

    +    

    Example: ctdb dbstatistics notify_index.tdb

     DB Statistics: notify_index.tdb
      ro_delegations                     0
      ro_revokes                         0
    @@ -215,45 +215,45 @@
          Count:7 Key:2f636c75737465726673
          Count:18 Key:2f636c757374657266732f64617461
          Count:7 Key:2f636c757374657266732f646174612f636c69656e7473
    -	

    DB Statistics

    +

    DB Statistics

    Name of the database. -

    ro_delegations

    +

    ro_delegations

    Number of readonly delegations created in the database. -

    ro_revokes

    +

    ro_revokes

    Number of readonly delegations revoked. The difference in ro_delegations and ro_revokes indicates the currently active readonly delegations. -

    locks

    +

    locks

    This section lists locking statistics. -

    total

    +

    total

    Number of completed lock calls. This includes database locks and record locks. -

    failed

    +

    failed

    Number of failed lock calls. This includes database locks and record locks. -

    current

    +

    current

    Number of scheduled lock calls. This includes database locks and record locks. -

    pending

    +

    pending

    Number of queued lock calls. This includes database locks and record locks. -

    hop_count_buckets

    +

    hop_count_buckets

    Distribution of migration requests based on hop counts values. Buckets are 1, < 4, < 8, < 16, < 32, < 64, < 128, < 256, < 512, ≥ 512. -

    lock_buckets

    +

    lock_buckets

    Distribution of record lock requests based on time required to obtain locks. Buckets are < 1ms, < 10ms, < 100ms, < 1s, < 2s, < 4s, < 8s, < 16s, < 32s, < 64s, ≥ 64s. -

    locks_latency

    +

    locks_latency

    The minimum, the average and the maximum time (in seconds) required to obtain record locks. -

    Num Hot Keys

    +

    Num Hot Keys

    Number of contended records determined by hop count. CTDB keeps track of top 10 hot records and the output shows hex encoded keys for the hot records. -

    SEE ALSO

    +

    SEE ALSO

    ctdb(1), ctdbd(1), diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdb-tunables.7 samba-4.6.5+dfsg/ctdb/doc/ctdb-tunables.7 --- samba-4.5.8+dfsg/ctdb/doc/ctdb-tunables.7 2016-10-24 19:44:57.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdb-tunables.7 2017-06-06 07:50:00.000000000 +0000 @@ -2,12 +2,12 @@ .\" Title: ctdb-tunables .\" Author: .\" Generator: DocBook XSL Stylesheets v1.78.1 -.\" Date: 10/24/2016 +.\" Date: 06/06/2017 .\" Manual: CTDB - clustered TDB database .\" Source: ctdb .\" Language: English .\" -.TH "CTDB\-TUNABLES" "7" "10/24/2016" "ctdb" "CTDB \- clustered TDB database" +.TH "CTDB\-TUNABLES" "7" "06/06/2017" "ctdb" "CTDB \- clustered TDB database" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -38,6 +38,8 @@ \fBgetvar\fR commands for more details\&. .PP +Unless otherwise stated, tunables should be set to the same value on all nodes\&. Setting tunables to different values across nodes may produce unexpected results\&. Future releases may set (some or most) tunables globally across the cluster but doing so is currently a manual process\&. +.PP The tunable variables are listed alphabetically\&. .SS "AllowClientDBAttach" .PP @@ -86,13 +88,6 @@ When databases are frozen we do not allow clients to attach to the databases\&. Instead of returning an error immediately to the client, the attach request from the client is deferred until the database becomes available again at which stage we respond to the client\&. .PP This timeout controls how long we will defer the request from the client before timing it out and returning an error to the client\&. -.SS "DeterministicIPs" -.PP -Default: 0 -.PP -When set to 1, ctdb will try to keep public IP addresses locked to specific nodes as far as possible\&. This makes it easier for debugging since you can know that as long as all nodes are healthy public IP X will always be hosted by node Y\&. -.PP -The cost of using deterministic IP address assignment is that it disables part of the logic where ctdb tries to reduce the number of public IP assignment changes in the cluster\&. This tunable may increase the number of IP failover/failbacks that are performed on the cluster by a small margin\&. .SS "DisableIPFailover" .PP Default: 0 @@ -111,7 +106,7 @@ .PP This parameter allows ctdb to ban a node if the node is misbehaving\&. .PP -When set to 0, this disables banning completely in the cluster and thus nodes can not get banned, even it they break\&. Don\*(Aqt set to 0 unless you know what you are doing\&. You should set this to the same value on all nodes to avoid unexpected behaviour\&. +When set to 0, this disables banning completely in the cluster and thus nodes can not get banned, even it they break\&. Don\*(Aqt set to 0 unless you know what you are doing\&. .SS "EventScriptTimeout" .PP Default: 30 @@ -136,6 +131,34 @@ \fIStickyPindown\fRmilliseconds and prevented from being migrated off the node\&. .PP This will improve performance for certain workloads, such as locking\&.tdb if many clients are opening/closing the same file concurrently\&. +.SS "IPAllocAlgorithm" +.PP +Default: 2 +.PP +Selects the algorithm that CTDB should use when doing public IP address allocation\&. Meaningful values are: +.PP +0 +.RS 4 +Deterministic IP address allocation\&. +.sp +This is a simple and fast option\&. However, it can cause unnecessary address movement during fail\-over because each address has a "home" node\&. Works badly when some nodes do not have any addresses defined\&. Should be used with care when addresses are defined across multiple networks\&. +.RE +.PP +1 +.RS 4 +Non\-deterministic IP address allocation\&. +.sp +This is a relatively fast option that attempts to do a minimise unnecessary address movements\&. Addresses do not have a "home" node\&. Rebalancing is limited but it usually adequate\&. Works badly when addresses are defined across multiple networks\&. +.RE +.PP +2 +.RS 4 +LCP2 IP address allocation\&. +.sp +Uses a heuristic to assign addresses defined across multiple networks, usually balancing addresses on each network evenly across nodes\&. Addresses do not have a "home" node\&. Minimises unnecessary address movements\&. The algorithm is complex, so is slower than other choices for a large number of addresses\&. However, it can calculate an optimal assignment of 900 addresses in under 10 seconds on modern hardware\&. +.RE +.PP +If the specified value is not one of these then the default will be used\&. .SS "KeepaliveInterval" .PP Default: 5 @@ -151,11 +174,6 @@ \fIKeepaliveInterval\fR * (\fIKeepaliveLimit\fR + 1) seconds before ctdb determines that the node is DISCONNECTED and performs a recovery\&. This limit should not be set too high to enable early detection and avoid any application timeouts (e\&.g\&. SMB1) to kick in before the fail over is completed\&. -.SS "LCP2PublicIPs" -.PP -Default: 1 -.PP -When set to 1, ctdb uses the LCP2 ip allocation algorithm\&. .SS "LockProcessesPerDB" .PP Default: 200 @@ -194,12 +212,12 @@ .PP Default: 0 .PP -If no nodes are HEALTHY then by default ctdb will happily host public IPs on disabled (unhealthy or administratively disabled) nodes\&. This can cause problems, for example if the underlying cluster filesystem is not mounted\&. When set to 1 on a node and that node is disabled, any IPs hosted by this node will be released and the node will not takeover any IPs until it is no longer disabled\&. +If no nodes are HEALTHY then by default ctdb will happily host public IPs on disabled (unhealthy or administratively disabled) nodes\&. This can cause problems, for example if the underlying cluster filesystem is not mounted\&. When set to 1 and a node is disabled, any IPs hosted by this node will be released and the node will not takeover any IPs until it is no longer disabled\&. .SS "NoIPTakeover" .PP Default: 0 .PP -When set to 1, ctdb will not allow IP addresses to be failed over onto this node\&. Any IP addresses that the node currently hosts will remain on the node but no new IP addresses can be failed over to the node\&. +When set to 1, ctdb will not allow IP addresses to be failed over to other nodes\&. Any IP addresses already hosted on healthy nodes will remain\&. Usually IP addresses hosted on unhealthy nodes will also remain, if NoIPHostOnAllDisabled is 0\&. However, if NoIPHostOnAllDisabled is 1 then IP addresses will be released by unhealthy nodes and will become un\-hosted\&. .SS "PullDBPreallocation" .PP Default: 10*1024*1024 @@ -239,15 +257,6 @@ Default: 1 .PP How frequently in seconds should the recovery daemon perform the consistency checks to determine if it should perform a recovery\&. -.SS "RecoverPDBBySeqNum" -.PP -Default: 1 -.PP -When set to zero, database recovery for persistent databases is record\-by\-record and recovery process simply collects the most recent version of every individual record\&. -.PP -When set to non\-zero, persistent databases will instead be recovered as a whole db and not by individual records\&. The node that contains the highest value stored in the record "__db_sequence_number__" is selected and the copy of that nodes database is used as the recovered database\&. -.PP -By default, recovery of persistent databses is done using __db_sequence_number__ record\&. .SS "RecoverTimeout" .PP Default: 120 diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdb-tunables.7.html samba-4.6.5+dfsg/ctdb/doc/ctdb-tunables.7.html --- samba-4.5.8+dfsg/ctdb/doc/ctdb-tunables.7.html 2016-10-24 19:44:57.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdb-tunables.7.html 2017-06-06 07:50:00.000000000 +0000 @@ -1,42 +1,48 @@ -ctdb-tunables

    Name

    ctdb-tunables — CTDB tunable configuration variables

    DESCRIPTION

    +ctdb-tunables

    Name

    ctdb-tunables — CTDB tunable configuration variables

    DESCRIPTION

    CTDB's behaviour can be configured by setting run-time tunable variables. This lists and describes all tunables. See the ctdb(1) listvars, setvar and getvar commands for more details.

    + Unless otherwise stated, tunables should be set to the same + value on all nodes. Setting tunables to different values across + nodes may produce unexpected results. Future releases may set + (some or most) tunables globally across the cluster but doing so + is currently a manual process. +

    The tunable variables are listed alphabetically. -

    AllowClientDBAttach

    Default: 1

    +

    AllowClientDBAttach

    Default: 1

    When set to 0, clients are not allowed to attach to any databases. This can be used to temporarily block any new processes from attaching to and accessing the databases. This is mainly used for detaching a volatile database using 'ctdb detach'. -

    AllowUnhealthyDBRead

    Default: 0

    +

    AllowUnhealthyDBRead

    Default: 0

    When set to 1, ctdb allows database traverses to read unhealthy databases. By default, ctdb does not allow reading records from unhealthy databases. -

    ControlTimeout

    Default: 60

    +

    ControlTimeout

    Default: 60

    This is the default setting for timeout for when sending a control message to either the local or a remote ctdb daemon. -

    DatabaseHashSize

    Default: 100001

    +

    DatabaseHashSize

    Default: 100001

    Number of the hash chains for the local store of the tdbs that ctdb manages. -

    DatabaseMaxDead

    Default: 5

    +

    DatabaseMaxDead

    Default: 5

    Maximum number of dead records per hash chain for the tdb databses managed by ctdb. -

    DBRecordCountWarn

    Default: 100000

    +

    DBRecordCountWarn

    Default: 100000

    When set to non-zero, ctdb will log a warning during recovery if a database has more than this many records. This will produce a warning if a database grows uncontrollably with orphaned records. -

    DBRecordSizeWarn

    Default: 10000000

    +

    DBRecordSizeWarn

    Default: 10000000

    When set to non-zero, ctdb will log a warning during recovery if a single record is bigger than this size. This will produce a warning if a database record grows uncontrollably. -

    DBSizeWarn

    Default: 1000000000

    +

    DBSizeWarn

    Default: 1000000000

    When set to non-zero, ctdb will log a warning during recovery if a database size is bigger than this. This will produce a warning if a database grows uncontrollably. -

    DeferredAttachTO

    Default: 120

    +

    DeferredAttachTO

    Default: 120

    When databases are frozen we do not allow clients to attach to the databases. Instead of returning an error immediately to the client, the attach request from the client is deferred until @@ -45,18 +51,7 @@

    This timeout controls how long we will defer the request from the client before timing it out and returning an error to the client. -

    DeterministicIPs

    Default: 0

    - When set to 1, ctdb will try to keep public IP addresses locked - to specific nodes as far as possible. This makes it easier - for debugging since you can know that as long as all nodes are - healthy public IP X will always be hosted by node Y. -

    - The cost of using deterministic IP address assignment is that it - disables part of the logic where ctdb tries to reduce the number - of public IP assignment changes in the cluster. This tunable may - increase the number of IP failover/failbacks that are performed - on the cluster by a small margin. -

    DisableIPFailover

    Default: 0

    +

    DisableIPFailover

    Default: 0

    When set to non-zero, ctdb will not perform failover or failback. Even if a node fails while holding public IPs, ctdb will not recover the IPs or assign them to another node. @@ -66,19 +61,18 @@ nodes. This leads to a service outage until the administrator has manually performed IP failover to replacement nodes using the 'ctdb moveip' command. -

    ElectionTimeout

    Default: 3

    +

    ElectionTimeout

    Default: 3

    The number of seconds to wait for the election of recovery master to complete. If the election is not completed during this interval, then that round of election fails and ctdb starts a new election. -

    EnableBans

    Default: 1

    - This parameter allows ctdb to ban a node if the node is misbehaving. +

    EnableBans

    Default: 1

    + This parameter allows ctdb to ban a node if the node is misbehaving.

    When set to 0, this disables banning completely in the cluster and thus nodes can not get banned, even it they break. Don't - set to 0 unless you know what you are doing. You should set - this to the same value on all nodes to avoid unexpected behaviour. -

    EventScriptTimeout

    Default: 30

    + set to 0 unless you know what you are doing. +

    EventScriptTimeout

    Default: 30

    Maximum time in seconds to allow an event to run before timing out. This is the total time for all enabled scripts that are run for an event, not just a single event script. @@ -87,7 +81,7 @@ "releaseip", "startrecovery", "recovered") and converted to success. The logic here is that the callers of these events implement their own additional timeout. -

    FetchCollapse

    Default: 1

    +

    FetchCollapse

    Default: 1

    This parameter is used to avoid multiple migration requests for the same record from a single node. All the record requests for the same record are queued up and processed when the record is @@ -100,7 +94,7 @@ bounce that record around very fast, and poor performance. This can improve performance and reduce CPU utilization for certain workloads. -

    HopcountMakeSticky

    Default: 50

    +

    HopcountMakeSticky

    Default: 50

    For database(s) marked STICKY (using 'ctdb setdbsticky'), any record that is migrating so fast that hopcount exceeds this limit is marked as STICKY record for @@ -112,10 +106,44 @@ This will improve performance for certain workloads, such as locking.tdb if many clients are opening/closing the same file concurrently. -

    KeepaliveInterval

    Default: 5

    +

    IPAllocAlgorithm

    Default: 2

    + Selects the algorithm that CTDB should use when doing public + IP address allocation. Meaningful values are: +

    0

    + Deterministic IP address allocation. +

    + This is a simple and fast option. However, it can cause + unnecessary address movement during fail-over because + each address has a "home" node. Works badly when some + nodes do not have any addresses defined. Should be used + with care when addresses are defined across multiple + networks. +

    1

    + Non-deterministic IP address allocation. +

    + This is a relatively fast option that attempts to do a + minimise unnecessary address movements. Addresses do + not have a "home" node. Rebalancing is limited but it + usually adequate. Works badly when addresses are + defined across multiple networks. +

    2

    + LCP2 IP address allocation. +

    + Uses a heuristic to assign addresses defined across + multiple networks, usually balancing addresses on each + network evenly across nodes. Addresses do not have a + "home" node. Minimises unnecessary address movements. + The algorithm is complex, so is slower than other + choices for a large number of addresses. However, it + can calculate an optimal assignment of 900 addresses in + under 10 seconds on modern hardware. +

    + If the specified value is not one of these then the default + will be used. +

    KeepaliveInterval

    Default: 5

    How often in seconds should the nodes send keep-alive packets to each other. -

    KeepaliveLimit

    Default: 5

    +

    KeepaliveLimit

    Default: 5

    After how many keepalive intervals without any traffic should a node wait until marking the peer as DISCONNECTED.

    @@ -126,31 +154,29 @@ a recovery. This limit should not be set too high to enable early detection and avoid any application timeouts (e.g. SMB1) to kick in before the fail over is completed. -

    LCP2PublicIPs

    Default: 1

    - When set to 1, ctdb uses the LCP2 ip allocation algorithm. -

    LockProcessesPerDB

    Default: 200

    +

    LockProcessesPerDB

    Default: 200

    This is the maximum number of lock helper processes ctdb will create for obtaining record locks. When ctdb cannot get a record lock without blocking, it creates a helper process that waits for the lock to be obtained. -

    LogLatencyMs

    Default: 0

    +

    LogLatencyMs

    Default: 0

    When set to non-zero, ctdb will log if certains operations take longer than this value, in milliseconds, to complete. These operations include "process a record request from client", "take a record or database lock", "update a persistent database record" and "vaccum a database". -

    MaxQueueDropMsg

    Default: 1000000

    +

    MaxQueueDropMsg

    Default: 1000000

    This is the maximum number of messages to be queued up for a client before ctdb will treat the client as hung and will terminate the client connection. -

    MonitorInterval

    Default: 15

    +

    MonitorInterval

    Default: 15

    How often should ctdb run the 'monitor' event in seconds to check for a node's health. -

    MonitorTimeoutCount

    Default: 20

    +

    MonitorTimeoutCount

    Default: 20

    How many 'monitor' events in a row need to timeout before a node is flagged as UNHEALTHY. This setting is useful if scripts can not be written so that they do not hang for benign reasons. -

    NoIPFailback

    Default: 0

    +

    NoIPFailback

    Default: 0

    When set to 1, ctdb will not perform failback of IP addresses when a node becomes healthy. When a node becomes UNHEALTHY, ctdb WILL perform failover of public IP addresses, but when the @@ -168,27 +194,28 @@ until there is manual intervention from the administrator. When this parameter is set, you can manually fail public IP addresses over to the new node(s) using the 'ctdb moveip' command. -

    NoIPHostOnAllDisabled

    Default: 0

    +

    NoIPHostOnAllDisabled

    Default: 0

    If no nodes are HEALTHY then by default ctdb will happily host public IPs on disabled (unhealthy or administratively disabled) nodes. This can cause problems, for example if the underlying - cluster filesystem is not mounted. When set to 1 on a node and - that node is disabled, any IPs hosted by this node will be - released and the node will not takeover any IPs until it is no - longer disabled. -

    NoIPTakeover

    Default: 0

    + cluster filesystem is not mounted. When set to 1 and a node + is disabled, any IPs hosted by this node will be released and + the node will not takeover any IPs until it is no longer disabled. +

    NoIPTakeover

    Default: 0

    When set to 1, ctdb will not allow IP addresses to be failed - over onto this node. Any IP addresses that the node currently - hosts will remain on the node but no new IP addresses can be - failed over to the node. -

    PullDBPreallocation

    Default: 10*1024*1024

    + over to other nodes. Any IP addresses already hosted on + healthy nodes will remain. Usually IP addresses hosted on + unhealthy nodes will also remain, if NoIPHostOnAllDisabled is + 0. However, if NoIPHostOnAllDisabled is 1 then IP addresses + will be released by unhealthy nodes and will become un-hosted. +

    PullDBPreallocation

    Default: 10*1024*1024

    This is the size of a record buffer to pre-allocate for sending reply to PULLDB control. Usually record buffer starts with size of the first record and gets reallocated every time a new record is added to the record buffer. For a large number of records, this can be very inefficient to grow the record buffer one record at a time. -

    QueueBufferSize

    Default: 1024

    +

    QueueBufferSize

    Default: 1024

    This is the maximum amount of data (in bytes) ctdb will read from a socket at a time.

    @@ -197,48 +224,35 @@ then this tunable value should be increased. However, large values can keep ctdb busy processing packets and prevent ctdb from handling other events. -

    RecBufferSizeLimit

    Default: 1000000

    +

    RecBufferSizeLimit

    Default: 1000000

    This is the limit on the size of the record buffer to be sent in various controls. This limit is used by new controls used for recovery and controls used in vacuuming. -

    RecdFailCount

    Default: 10

    +

    RecdFailCount

    Default: 10

    If the recovery daemon has failed to ping the main dameon for this many consecutive intervals, the main daemon will consider the recovery daemon as hung and will try to restart it to recover. -

    RecdPingTimeout

    Default: 60

    +

    RecdPingTimeout

    Default: 60

    If the main dameon has not heard a "ping" from the recovery dameon for this many seconds, the main dameon will log a message that the recovery daemon is potentially hung. This also increments a counter which is checked against RecdFailCount for detection of hung recovery daemon. -

    RecLockLatencyMs

    Default: 1000

    +

    RecLockLatencyMs

    Default: 1000

    When using a reclock file for split brain prevention, if set to non-zero this tunable will make the recovery dameon log a message if the fcntl() call to lock/testlock the recovery file takes longer than this number of milliseconds. -

    RecoverInterval

    Default: 1

    +

    RecoverInterval

    Default: 1

    How frequently in seconds should the recovery daemon perform the consistency checks to determine if it should perform a recovery. -

    RecoverPDBBySeqNum

    Default: 1

    - When set to zero, database recovery for persistent databases is - record-by-record and recovery process simply collects the most - recent version of every individual record. -

    - When set to non-zero, persistent databases will instead be - recovered as a whole db and not by individual records. The - node that contains the highest value stored in the record - "__db_sequence_number__" is selected and the copy of that nodes - database is used as the recovered database. -

    - By default, recovery of persistent databses is done using - __db_sequence_number__ record. -

    RecoverTimeout

    Default: 120

    +

    RecoverTimeout

    Default: 120

    This is the default setting for timeouts for controls when sent from the recovery daemon. We allow longer control timeouts from the recovery daemon than from normal use since the recovery dameon often use controls that can take a lot longer than normal controls. -

    RecoveryBanPeriod

    Default: 300

    +

    RecoveryBanPeriod

    Default: 300

    The duration in seconds for which a node is banned if the node fails during recovery. After this time has elapsed the node will automatically get unbanned and will attempt to rejoin the cluster. @@ -246,26 +260,26 @@ A node usually gets banned due to real problems with the node. Don't set this value too small. Otherwise, a problematic node will try to re-join cluster too soon causing unnecessary recoveries. -

    RecoveryDropAllIPs

    Default: 120

    +

    RecoveryDropAllIPs

    Default: 120

    If a node is stuck in recovery, or stopped, or banned, for this many seconds, then ctdb will release all public addresses on that node. -

    RecoveryGracePeriod

    Default: 120

    +

    RecoveryGracePeriod

    Default: 120

    During recoveries, if a node has not caused recovery failures during the last grace period in seconds, any records of transgressions that the node has caused recovery failures will be forgiven. This resets the ban-counter back to zero for that node. -

    RepackLimit

    Default: 10000

    +

    RepackLimit

    Default: 10000

    During vacuuming, if the number of freelist records are more than RepackLimit, then the database is repacked to get rid of the freelist records to avoid fragmentation.

    Databases are repacked only if both RepackLimit and VacuumLimit are exceeded. -

    RerecoveryTimeout

    Default: 10

    +

    RerecoveryTimeout

    Default: 10

    Once a recovery has completed, no additional recoveries are permitted until this timeout in seconds has expired. -

    SeqnumInterval

    Default: 1000

    +

    SeqnumInterval

    Default: 1000

    Some databases have seqnum tracking enabled, so that samba will be able to detect asynchronously when there has been updates to the database. Everytime a database is updated its sequence @@ -274,57 +288,57 @@ This tunable is used to specify in milliseconds how frequently ctdb will send out updates to remote nodes to inform them that the sequence number is increased. -

    StatHistoryInterval

    Default: 1

    +

    StatHistoryInterval

    Default: 1

    Granularity of the statistics collected in the statistics history. This is reported by 'ctdb stats' command. -

    StickyDuration

    Default: 600

    +

    StickyDuration

    Default: 600

    Once a record has been marked STICKY, this is the duration in seconds, the record will be flagged as a STICKY record. -

    StickyPindown

    Default: 200

    +

    StickyPindown

    Default: 200

    Once a STICKY record has been migrated onto a node, it will be pinned down on that node for this number of milliseconds. Any request from other nodes to migrate the record off the node will be deferred. -

    TakeoverTimeout

    Default: 9

    +

    TakeoverTimeout

    Default: 9

    This is the duration in seconds in which ctdb tries to complete IP failover. -

    TDBMutexEnabled

    Default: 0

    +

    TDBMutexEnabled

    Default: 0

    This paramter enables TDB_MUTEX_LOCKING feature on volatile databases if the robust mutexes are supported. This optimizes the record locking using robust mutexes and is much more efficient that using posix locks. -

    TickleUpdateInterval

    Default: 20

    +

    TickleUpdateInterval

    Default: 20

    Every TickleUpdateInterval seconds, ctdb synchronizes the client connection information across nodes. -

    TraverseTimeout

    Default: 20

    +

    TraverseTimeout

    Default: 20

    This is the duration in seconds for which a database traverse is allowed to run. If the traverse does not complete during this interval, ctdb will abort the traverse. -

    VacuumFastPathCount

    Default: 60

    +

    VacuumFastPathCount

    Default: 60

    During a vacuuming run, ctdb usually processes only the records marked for deletion also called the fast path vacuuming. After finishing VacuumFastPathCount number of fast path vacuuming runs, ctdb will trigger a scan of complete database for any empty records that need to be deleted. -

    VacuumInterval

    Default: 10

    +

    VacuumInterval

    Default: 10

    Periodic interval in seconds when vacuuming is triggered for volatile databases. -

    VacuumLimit

    Default: 5000

    +

    VacuumLimit

    Default: 5000

    During vacuuming, if the number of deleted records are more than VacuumLimit, then databases are repacked to avoid fragmentation.

    Databases are repacked only if both RepackLimit and VacuumLimit are exceeded. -

    VacuumMaxRunTime

    Default: 120

    +

    VacuumMaxRunTime

    Default: 120

    The maximum time in seconds for which the vacuuming process is allowed to run. If vacuuming process takes longer than this value, then the vacuuming process is terminated. -

    VerboseMemoryNames

    Default: 0

    +

    VerboseMemoryNames

    Default: 0

    When set to non-zero, ctdb assigns verbose names for some of the talloc allocated memory objects. These names are visible in the talloc memory report generated by 'ctdb dumpmemory'. -

    SEE ALSO

    +

    SEE ALSO

    ctdb(1), ctdbd(1), diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdb-tunables.7.xml samba-4.6.5+dfsg/ctdb/doc/ctdb-tunables.7.xml --- samba-4.5.8+dfsg/ctdb/doc/ctdb-tunables.7.xml 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ctdb-tunables.7.xml 2017-01-11 07:55:14.000000000 +0000 @@ -30,6 +30,14 @@ + Unless otherwise stated, tunables should be set to the same + value on all nodes. Setting tunables to different values across + nodes may produce unexpected results. Future releases may set + (some or most) tunables globally across the cluster but doing so + is currently a manual process. + + + The tunable variables are listed alphabetically. @@ -128,24 +136,6 @@ - DeterministicIPs - Default: 0 - - When set to 1, ctdb will try to keep public IP addresses locked - to specific nodes as far as possible. This makes it easier - for debugging since you can know that as long as all nodes are - healthy public IP X will always be hosted by node Y. - - - The cost of using deterministic IP address assignment is that it - disables part of the logic where ctdb tries to reduce the number - of public IP assignment changes in the cluster. This tunable may - increase the number of IP failover/failbacks that are performed - on the cluster by a small margin. - - - - DisableIPFailover Default: 0 @@ -177,13 +167,12 @@ EnableBans Default: 1 - This parameter allows ctdb to ban a node if the node is misbehaving. + This parameter allows ctdb to ban a node if the node is misbehaving. When set to 0, this disables banning completely in the cluster and thus nodes can not get banned, even it they break. Don't - set to 0 unless you know what you are doing. You should set - this to the same value on all nodes to avoid unexpected behaviour. + set to 0 unless you know what you are doing. @@ -243,6 +232,70 @@ + IPAllocAlgorithm + Default: 2 + + Selects the algorithm that CTDB should use when doing public + IP address allocation. Meaningful values are: + + + + 0 + + + Deterministic IP address allocation. + + + This is a simple and fast option. However, it can cause + unnecessary address movement during fail-over because + each address has a "home" node. Works badly when some + nodes do not have any addresses defined. Should be used + with care when addresses are defined across multiple + networks. + + + + + 1 + + + Non-deterministic IP address allocation. + + + This is a relatively fast option that attempts to do a + minimise unnecessary address movements. Addresses do + not have a "home" node. Rebalancing is limited but it + usually adequate. Works badly when addresses are + defined across multiple networks. + + + + + 2 + + + LCP2 IP address allocation. + + + Uses a heuristic to assign addresses defined across + multiple networks, usually balancing addresses on each + network evenly across nodes. Addresses do not have a + "home" node. Minimises unnecessary address movements. + The algorithm is complex, so is slower than other + choices for a large number of addresses. However, it + can calculate an optimal assignment of 900 addresses in + under 10 seconds on modern hardware. + + + + + + If the specified value is not one of these then the default + will be used. + + + + KeepaliveInterval Default: 5 @@ -270,14 +323,6 @@ - LCP2PublicIPs - Default: 1 - - When set to 1, ctdb uses the LCP2 ip allocation algorithm. - - - - LockProcessesPerDB Default: 200 @@ -362,10 +407,9 @@ If no nodes are HEALTHY then by default ctdb will happily host public IPs on disabled (unhealthy or administratively disabled) nodes. This can cause problems, for example if the underlying - cluster filesystem is not mounted. When set to 1 on a node and - that node is disabled, any IPs hosted by this node will be - released and the node will not takeover any IPs until it is no - longer disabled. + cluster filesystem is not mounted. When set to 1 and a node + is disabled, any IPs hosted by this node will be released and + the node will not takeover any IPs until it is no longer disabled. @@ -374,9 +418,11 @@ Default: 0 When set to 1, ctdb will not allow IP addresses to be failed - over onto this node. Any IP addresses that the node currently - hosts will remain on the node but no new IP addresses can be - failed over to the node. + over to other nodes. Any IP addresses already hosted on + healthy nodes will remain. Usually IP addresses hosted on + unhealthy nodes will also remain, if NoIPHostOnAllDisabled is + 0. However, if NoIPHostOnAllDisabled is 1 then IP addresses + will be released by unhealthy nodes and will become un-hosted. @@ -461,27 +507,6 @@ - - RecoverPDBBySeqNum - Default: 1 - - When set to zero, database recovery for persistent databases is - record-by-record and recovery process simply collects the most - recent version of every individual record. - - - When set to non-zero, persistent databases will instead be - recovered as a whole db and not by individual records. The - node that contains the highest value stored in the record - "__db_sequence_number__" is selected and the copy of that nodes - database is used as the recovered database. - - - By default, recovery of persistent databses is done using - __db_sequence_number__ record. - - - RecoverTimeout Default: 120 diff -Nru samba-4.5.8+dfsg/ctdb/doc/ltdbtool.1 samba-4.6.5+dfsg/ctdb/doc/ltdbtool.1 --- samba-4.5.8+dfsg/ctdb/doc/ltdbtool.1 2016-10-24 19:44:55.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ltdbtool.1 2017-06-06 07:49:57.000000000 +0000 @@ -2,12 +2,12 @@ .\" Title: ltdbtool .\" Author: .\" Generator: DocBook XSL Stylesheets v1.78.1 -.\" Date: 10/24/2016 +.\" Date: 06/06/2017 .\" Manual: CTDB - clustered TDB database .\" Source: ctdb .\" Language: English .\" -.TH "LTDBTOOL" "1" "10/24/2016" "ctdb" "CTDB \- clustered TDB database" +.TH "LTDBTOOL" "1" "06/06/2017" "ctdb" "CTDB \- clustered TDB database" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff -Nru samba-4.5.8+dfsg/ctdb/doc/ltdbtool.1.html samba-4.6.5+dfsg/ctdb/doc/ltdbtool.1.html --- samba-4.5.8+dfsg/ctdb/doc/ltdbtool.1.html 2016-10-24 19:44:55.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ltdbtool.1.html 2017-06-06 07:49:57.000000000 +0000 @@ -1,4 +1,4 @@ -ltdbtool

    Name

    ltdbtool — manipulate CTDB's local TDB files

    Synopsis

    ltdbtool [OPTION...] {COMMAND} [COMMAND-ARGS]

    DESCRIPTION

    +ltdbtool

    Name

    ltdbtool — manipulate CTDB's local TDB files

    Synopsis

    ltdbtool [OPTION...] {COMMAND} [COMMAND-ARGS]

    DESCRIPTION

    ltdbtool is a utility to manipulate CTDB's local TDB databases (LTDBs) without connecting to a CTDB daemon.

    @@ -11,7 +11,7 @@ by adding or removing CTDB headers and

  • convert between 64 and 32 bit LTDBs where the CTDB record headers differ by 4 bytes of padding. -

  • OPTIONS

    -e

    +

    OPTIONS

    -e

    Dump empty records. These are normally excluded.

    -p

    Dump with header information, similar to "ctdb catdb". @@ -37,7 +37,7 @@ output database in bytes.

    -h

    Print help text. -

    COMMANDS

    help

    +

    COMMANDS

    help

    Print help text.

    dump IDB

    Dump the contents of an LTDB input file IDB to standard @@ -47,7 +47,7 @@

    Copy an LTDB input file IDB to output file ODB, optionally adding or removing CTDB headers. -

    EXAMPLES

    +

    EXAMPLES

    Print a local tdb in "tdbdump" style:

           ltdbtool dump idmap2.tdb.0
    @@ -75,7 +75,7 @@
           Add a default header:
         

           ltdbtool convert -s0 idmap.tdb idmap2.tdb.0
    -    

    SEE ALSO

    +

    SEE ALSO

    ctdb(1), tdbdump(1), diff -Nru samba-4.5.8+dfsg/ctdb/doc/Makefile samba-4.6.5+dfsg/ctdb/doc/Makefile --- samba-4.5.8+dfsg/ctdb/doc/Makefile 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/Makefile 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -DOCS = ctdb.1 ctdb.1.html \ - ctdb_diagnostics.1 ctdb_diagnostics.1.html \ - ctdbd.1 ctdbd.1.html \ - ctdbd_wrapper.1 ctdbd_wrapper.1.html \ - onnode.1 onnode.1.html \ - ltdbtool.1 ltdbtool.1.html \ - ping_pong.1 ping_pong.1.html \ - ctdbd.conf.5 ctdbd.conf.5.html \ - ctdb.7 ctdb.7.html \ - ctdb-statistics.7 ctdb-statistics.7.html \ - ctdb-tunables.7 ctdb-tunables.7.html - -all: $(DOCS) - -%: %.xml - xsltproc -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $< - -%.html: %.xml - xsltproc -o $@ http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl $< - -distclean: - rm -f $(DOCS) diff -Nru samba-4.5.8+dfsg/ctdb/doc/onnode.1 samba-4.6.5+dfsg/ctdb/doc/onnode.1 --- samba-4.5.8+dfsg/ctdb/doc/onnode.1 2016-10-24 19:44:55.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/onnode.1 2017-06-06 07:49:58.000000000 +0000 @@ -2,12 +2,12 @@ .\" Title: onnode .\" Author: .\" Generator: DocBook XSL Stylesheets v1.78.1 -.\" Date: 10/24/2016 +.\" Date: 06/06/2017 .\" Manual: CTDB - clustered TDB database .\" Source: ctdb .\" Language: English .\" -.TH "ONNODE" "1" "10/24/2016" "ctdb" "CTDB \- clustered TDB database" +.TH "ONNODE" "1" "06/06/2017" "ctdb" "CTDB \- clustered TDB database" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff -Nru samba-4.5.8+dfsg/ctdb/doc/onnode.1.html samba-4.6.5+dfsg/ctdb/doc/onnode.1.html --- samba-4.5.8+dfsg/ctdb/doc/onnode.1.html 2016-10-24 19:44:55.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/onnode.1.html 2017-06-06 07:49:59.000000000 +0000 @@ -1,4 +1,4 @@ -onnode

    Name

    onnode — run commands on CTDB cluster nodes

    Synopsis

    onnode [OPTION...] {NODES} {COMMAND}

    DESCRIPTION

    +onnode

    Name

    onnode — run commands on CTDB cluster nodes

    Synopsis

    onnode [OPTION...] {NODES} {COMMAND}

    DESCRIPTION

    onnode is a utility to run commands on a specific node of a CTDB cluster, or on all nodes.

    @@ -9,7 +9,7 @@ COMMAND can be any shell command. The onnode utility uses ssh or rsh to connect to the remote nodes and run the command. -

    OPTIONS

    -c

    +

    OPTIONS

    -c

    Execute COMMAND in the current working directory on the specified nodes.

    -f FILENAME

    @@ -49,7 +49,7 @@ more than one node is specified.

    -h, --help

    Show a short usage guide. -

    NODES SPECIFICATION

    +

    NODES SPECIFICATION

    Nodes can be specified via numeric node numbers (from 0 to N-1) or mnemonics. Multiple nodes are specified using lists of nodes, separated by commas, and ranges of numeric node numbers, @@ -68,7 +68,7 @@ unhealthy.

    con | connected

    All nodes that are not disconnected. -

    EXAMPLES

    +

    EXAMPLES

    The following command would show the process ID of ctdbd on all nodes

           onnode all ctdb getpid
    @@ -87,14 +87,14 @@
           directory, in parallel, on nodes 0, 2, 3 and 4.
         

           onnode -c -p 0,2-4 ./foo
    -    

    ENVIRONMENT

    CTDB_BASE

    +

    ENVIRONMENT

    CTDB_BASE

    Directory containing CTDB configuration files. The default is /usr/local/etc/ctdb.

    CTDB_NODES_FILE

    Name of alternative nodes file to use instead of the default. See the FILES section for more details. -

    FILES

    /usr/local/etc/ctdb/nodes

    +

    FILES

    /usr/local/etc/ctdb/nodes

    Default file containing a list of each node's IP address or hostname.

    @@ -119,7 +119,7 @@ SSH to something other than "ssh". In this case the -t option is ignored. For example, the administrator may choose to use use rsh instead of ssh. -

    SEE ALSO

    +

    SEE ALSO

    ctdb(7), http://ctdb.samba.org/ diff -Nru samba-4.5.8+dfsg/ctdb/doc/ping_pong.1 samba-4.6.5+dfsg/ctdb/doc/ping_pong.1 --- samba-4.5.8+dfsg/ctdb/doc/ping_pong.1 2016-10-24 19:44:55.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ping_pong.1 2017-06-06 07:49:57.000000000 +0000 @@ -2,12 +2,12 @@ .\" Title: ping_pong .\" Author: .\" Generator: DocBook XSL Stylesheets v1.78.1 -.\" Date: 10/24/2016 +.\" Date: 06/06/2017 .\" Manual: CTDB - clustered TDB database .\" Source: ctdb .\" Language: English .\" -.TH "PING_PONG" "1" "10/24/2016" "ctdb" "CTDB \- clustered TDB database" +.TH "PING_PONG" "1" "06/06/2017" "ctdb" "CTDB \- clustered TDB database" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff -Nru samba-4.5.8+dfsg/ctdb/doc/ping_pong.1.html samba-4.6.5+dfsg/ctdb/doc/ping_pong.1.html --- samba-4.5.8+dfsg/ctdb/doc/ping_pong.1.html 2016-10-24 19:44:56.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/doc/ping_pong.1.html 2017-06-06 07:49:57.000000000 +0000 @@ -1,4 +1,4 @@ -ping_pong

    Name

    ping_pong — measures the ping-pong byte range lock latency

    Synopsis

    ping_pong { -r | -w | -rw } [-m] [-c] {FILENAME} {NUM-LOCKS}

    DESCRIPTION

    +ping_pong

    Name

    ping_pong — measures the ping-pong byte range lock latency

    Synopsis

    ping_pong { -r | -w | -rw } [-m] [-c] {FILENAME} {NUM-LOCKS}

    DESCRIPTION

    ping_pong measures the byte range lock latency. It is especially useful on a cluster of nodes sharing a common lock manager as it will give some indication of the lock manager's performance @@ -9,7 +9,7 @@

    NUM-LOCKS is the number of byte range locks, so needs to be (strictly) greater than the number of nodes in the cluster. -

    OPTIONS

    -r

    +

    OPTIONS

    -r

    test read performance

    -w

    test write performance @@ -17,7 +17,7 @@ use mmap

    -c

    validate the locks -

    EXAMPLES

    +

    EXAMPLES

    Testing lock coherence

           ping_pong test.dat N
    @@ -29,7 +29,7 @@
           Testing IO coherence
         

           ping_pong -rw test.dat N
    -    

    SEE ALSO

    +

    SEE ALSO

    ctdb(7), https://wiki.samba.org/index.php/Ping_pong diff -Nru samba-4.5.8+dfsg/ctdb/include/ctdb_client.h samba-4.6.5+dfsg/ctdb/include/ctdb_client.h --- samba-4.5.8+dfsg/ctdb/include/ctdb_client.h 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/include/ctdb_client.h 2017-01-11 07:55:14.000000000 +0000 @@ -528,12 +528,6 @@ int ctdb_ctrl_recd_ping(struct ctdb_context *ctdb); -int ctdb_ctrl_getscriptstatus(struct ctdb_context *ctdb, - struct timeval timeout, uint32_t destnode, - TALLOC_CTX *mem_ctx, - enum ctdb_event type, - struct ctdb_script_list_old **script_status); - int ctdb_ctrl_report_recd_lock_latency(struct ctdb_context *ctdb, struct timeval timeout, double latency); @@ -553,11 +547,6 @@ struct timeval timeout, uint32_t destnode, uint32_t recmasterrole); -int ctdb_ctrl_enablescript(struct ctdb_context *ctdb, struct timeval timeout, - uint32_t destnode, const char *script); -int ctdb_ctrl_disablescript(struct ctdb_context *ctdb, struct timeval timeout, - uint32_t destnode, const char *script); - int ctdb_ctrl_set_ban(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode, struct ctdb_ban_state *bantime); int ctdb_ctrl_get_ban(struct ctdb_context *ctdb, struct timeval timeout, diff -Nru samba-4.5.8+dfsg/ctdb/include/ctdb_private.h samba-4.6.5+dfsg/ctdb/include/ctdb_private.h --- samba-4.5.8+dfsg/ctdb/include/ctdb_private.h 2016-09-13 08:21:35.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/include/ctdb_private.h 2017-01-11 07:55:14.000000000 +0000 @@ -45,12 +45,6 @@ */ #define ctdb_validate_pnn(ctdb, pnn) (((uint32_t)(pnn)) < (ctdb)->num_nodes) - -/* called from the queue code when a packet comes in. Called with data==NULL - on error */ -typedef void (*ctdb_queue_cb_fn_t)(uint8_t *data, size_t length, - void *private_data); - /* used for callbacks in ctdb_control requests */ typedef void (*ctdb_control_callback_fn_t)(struct ctdb_context *, int32_t status, TDB_DATA data, @@ -248,10 +242,10 @@ struct ctdb_cluster_mutex_handle; +struct eventd_context; enum ctdb_freeze_mode {CTDB_FREEZE_NONE, CTDB_FREEZE_PENDING, CTDB_FREEZE_FROZEN}; -#define NUM_DB_PRIORITIES 3 /* main state of the ctdb daemon */ struct ctdb_context { struct tevent_context *ev; @@ -318,11 +312,7 @@ TALLOC_CTX *recd_ctx; /* a context used to track recoverd monitoring events */ TALLOC_CTX *release_ips_ctx; /* a context used to automatically drop all IPs if we fail to recover the node */ - TALLOC_CTX *event_script_ctx; - int active_events; - - struct ctdb_event_script_state *current_monitor; - struct ctdb_script_list_old *last_status[CTDB_EVENT_MAX]; + struct eventd_context *ectx; TALLOC_CTX *banning_ctx; @@ -337,9 +327,6 @@ /* if we are a child process, do we have a domain socket to send controls on */ bool can_send_controls; - /* list of event script callback functions that are active */ - struct event_script_callback *script_callbacks; - struct ctdb_reloadips_handle *reload_ips; const char *nodes_file; @@ -600,17 +587,16 @@ void ctdb_shutdown_sequence(struct ctdb_context *ctdb, int exit_code); -int switch_from_server_to_client(struct ctdb_context *ctdb, - const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +int switch_from_server_to_client(struct ctdb_context *ctdb); /* From server/ctdb_fork.c */ -void ctdb_set_child_info(TALLOC_CTX *mem_ctx, const char *child_name_fmt, - ...) PRINTF_ATTRIBUTE(2,3); - void ctdb_track_child(struct ctdb_context *ctdb, pid_t pid); pid_t ctdb_fork(struct ctdb_context *ctdb); +pid_t ctdb_vfork_exec(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb, + const char *helper, int helper_argc, + const char **helper_argv); struct tevent_signal *ctdb_init_sigchld(struct ctdb_context *ctdb); @@ -675,15 +661,8 @@ /* from ctdb_logging.c */ -extern const char *debug_extra; - -typedef int (*ctdb_log_setup_fn_t)(TALLOC_CTX *mem_ctx, - const char *logging, - const char *app_name); - -void ctdb_log_register_backend(const char *prefix, ctdb_log_setup_fn_t init); - -bool ctdb_logging_init(TALLOC_CTX *mem_ctx, const char *logging); +bool ctdb_logging_init(TALLOC_CTX *mem_ctx, const char *logging, + const char *debug_level); struct ctdb_log_state *ctdb_vfork_with_logging(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb, @@ -696,7 +675,6 @@ void *logfn_private, pid_t *pid); int ctdb_set_child_logging(struct ctdb_context *ctdb); -int ctdb_init_tevent_logging(struct ctdb_context *ctdb); /* from ctdb_logging_file.c */ @@ -909,9 +887,6 @@ int ctdb_set_public_addresses(struct ctdb_context *ctdb, bool check_addresses); -int ctdb_takeover_run(struct ctdb_context *ctdb, struct ctdb_node_map_old *nodemap, - uint32_t *force_rebalance_nodes); - int32_t ctdb_control_tcp_client(struct ctdb_context *ctdb, uint32_t client_id, TDB_DATA indata); int32_t ctdb_control_tcp_add(struct ctdb_context *ctdb, TDB_DATA indata, @@ -1011,9 +986,8 @@ /* from eventscript.c */ -int32_t ctdb_control_get_event_script_status(struct ctdb_context *ctdb, - uint32_t call_type, - TDB_DATA *outdata); +int ctdb_start_eventd(struct ctdb_context *ctdb); +void ctdb_stop_eventd(struct ctdb_context *ctdb); int ctdb_event_script_callback(struct ctdb_context *ctdb, TALLOC_CTX *mem_ctx, @@ -1030,11 +1004,4 @@ int ctdb_event_script(struct ctdb_context *ctdb, enum ctdb_event call); -int32_t ctdb_run_eventscripts(struct ctdb_context *ctdb, - struct ctdb_req_control_old *c, - TDB_DATA data, bool *async_reply); - -int32_t ctdb_control_enable_script(struct ctdb_context *ctdb, TDB_DATA indata); -int32_t ctdb_control_disable_script(struct ctdb_context *ctdb, TDB_DATA indata); - #endif diff -Nru samba-4.5.8+dfsg/ctdb/include/ctdb_protocol.h samba-4.6.5+dfsg/ctdb/include/ctdb_protocol.h --- samba-4.5.8+dfsg/ctdb/include/ctdb_protocol.h 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/include/ctdb_protocol.h 2017-01-11 07:55:14.000000000 +0000 @@ -266,8 +266,6 @@ }; -#define CTDB_PUBLIC_IP_FLAGS_ONLY_AVAILABLE 0x00010000 - struct ctdb_public_ip_info_old { struct ctdb_public_ip ip; uint32_t active_idx; diff -Nru samba-4.5.8+dfsg/ctdb/Makefile samba-4.6.5+dfsg/ctdb/Makefile --- samba-4.5.8+dfsg/ctdb/Makefile 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/Makefile 2017-02-28 20:04:57.000000000 +0000 @@ -27,6 +27,10 @@ @touch .tmplock @WAFLOCK=.tmplock $(WAF) show_version +manpages: + touch .tmplock + WAFLOCK=.tmplock $(WAF) manpages + dist: touch .tmplock WAFLOCK=.tmplock $(WAF) dist diff -Nru samba-4.5.8+dfsg/ctdb/packaging/RPM/ctdb.spec.in samba-4.6.5+dfsg/ctdb/packaging/RPM/ctdb.spec.in --- samba-4.5.8+dfsg/ctdb/packaging/RPM/ctdb.spec.in 2016-12-05 08:18:44.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/packaging/RPM/ctdb.spec.in 2017-01-11 07:55:14.000000000 +0000 @@ -38,7 +38,7 @@ # Required minimum library versions when building with system libraries %define libtalloc_version 2.0.8 -%define libtdb_version 1.2.11 +%define libtdb_version 1.3.11 %define libtevent_version 0.9.16 %if ! %with_included_talloc @@ -213,10 +213,12 @@ %{_bindir}/ctdb_diagnostics %{_bindir}/onnode %dir %{_libexecdir}/ctdb +%{_libexecdir}/ctdb/ctdb_eventd %{_libexecdir}/ctdb/ctdb_lock_helper -%{_libexecdir}/ctdb/ctdb_event_helper %{_libexecdir}/ctdb/ctdb_recovery_helper +%{_libexecdir}/ctdb/ctdb_takeover_helper %{_libexecdir}/ctdb/ctdb_mutex_fcntl_helper +%{_libexecdir}/ctdb/ctdb_event %{_libexecdir}/ctdb/ctdb_natgw %{_libexecdir}/ctdb/ctdb_lvs %{_libexecdir}/ctdb/ctdb_killtcp diff -Nru samba-4.5.8+dfsg/ctdb/protocol/protocol_api.h samba-4.6.5+dfsg/ctdb/protocol/protocol_api.h --- samba-4.5.8+dfsg/ctdb/protocol/protocol_api.h 2017-01-17 19:55:44.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/protocol/protocol_api.h 2017-01-11 07:55:14.000000000 +0000 @@ -204,10 +204,10 @@ void ctdb_req_control_get_debug(struct ctdb_req_control *request); int ctdb_reply_control_get_debug(struct ctdb_reply_control *reply, - uint32_t *debug_level); + int *debug_level); void ctdb_req_control_set_debug(struct ctdb_req_control *request, - uint32_t debug_level); + int debug_level); int ctdb_reply_control_set_debug(struct ctdb_reply_control *reply); void ctdb_req_control_get_dbmap(struct ctdb_req_control *request); @@ -397,10 +397,6 @@ struct ctdb_addr_info *addr_info); int ctdb_reply_control_del_public_ip(struct ctdb_reply_control *reply); -void ctdb_req_control_run_eventscripts(struct ctdb_req_control *request, - const char *event_str); -int ctdb_reply_control_run_eventscripts(struct ctdb_reply_control *reply); - void ctdb_req_control_get_capabilities(struct ctdb_req_control *request); int ctdb_reply_control_get_capabilities(struct ctdb_reply_control *reply, uint32_t *caps); @@ -416,7 +412,8 @@ struct ctdb_public_ip *pubip); int ctdb_reply_control_takeover_ip(struct ctdb_reply_control *reply); -void ctdb_req_control_get_public_ips(struct ctdb_req_control *request); +void ctdb_req_control_get_public_ips(struct ctdb_req_control *request, + bool available_only); int ctdb_reply_control_get_public_ips(struct ctdb_reply_control *reply, TALLOC_CTX *mem_ctx, struct ctdb_public_ip_list **pubip_list); @@ -426,12 +423,6 @@ TALLOC_CTX *mem_ctx, struct ctdb_node_map **nodemap); -void ctdb_req_control_get_event_script_status(struct ctdb_req_control *request, - uint32_t event); -int ctdb_reply_control_get_event_script_status(struct ctdb_reply_control *reply, - TALLOC_CTX *mem_ctx, - struct ctdb_script_list **script_list); - void ctdb_req_control_traverse_kill(struct ctdb_req_control *request, struct ctdb_traverse_start *traverse); int ctdb_reply_control_traverse_kill(struct ctdb_reply_control *reply); @@ -459,14 +450,6 @@ uint32_t recmaster_role); int ctdb_reply_control_set_recmasterrole(struct ctdb_reply_control *reply); -void ctdb_req_control_enable_script(struct ctdb_req_control *request, - const char *script); -int ctdb_reply_control_enable_script(struct ctdb_reply_control *reply); - -void ctdb_req_control_disable_script(struct ctdb_req_control *request, - const char *script); -int ctdb_reply_control_disable_script(struct ctdb_reply_control *reply); - void ctdb_req_control_set_ban_state(struct ctdb_req_control *request, struct ctdb_ban_state *ban_state); int ctdb_reply_control_set_ban_state(struct ctdb_reply_control *reply); @@ -644,6 +627,28 @@ TALLOC_CTX *mem_ctx, struct ctdb_req_message_data *c); +/* From protocol/protocol_event.c */ + +void ctdb_event_header_fill(struct ctdb_event_header *h, uint32_t reqid); + +size_t ctdb_event_request_len(struct ctdb_event_request *in); + +int ctdb_event_request_push(struct ctdb_event_request *in, + uint8_t *buf, size_t *buflen); + +int ctdb_event_request_pull(uint8_t *buf, size_t buflen, + TALLOC_CTX *mem_ctx, + struct ctdb_event_request *out); + +size_t ctdb_event_reply_len(struct ctdb_event_reply *in); + +int ctdb_event_reply_push(struct ctdb_event_reply *in, + uint8_t *buf, size_t *buflen); + +int ctdb_event_reply_pull(uint8_t *buf, size_t buflen, + TALLOC_CTX *mem_ctx, + struct ctdb_event_reply *out); + /* From protocol/protocol_packet.c */ int ctdb_allocate_pkt(TALLOC_CTX *mem_ctx, size_t datalen, diff -Nru samba-4.5.8+dfsg/ctdb/protocol/protocol_client.c samba-4.6.5+dfsg/ctdb/protocol/protocol_client.c --- samba-4.5.8+dfsg/ctdb/protocol/protocol_client.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/protocol/protocol_client.c 2017-01-11 07:55:14.000000000 +0000 @@ -222,14 +222,14 @@ } int ctdb_reply_control_get_debug(struct ctdb_reply_control *reply, - uint32_t *loglevel) + int *loglevel) { if (reply->rdata.opcode != CTDB_CONTROL_GET_DEBUG) { return EPROTO; } if (reply->status == 0) { - *loglevel = reply->rdata.data.loglevel; + *loglevel = (int)reply->rdata.data.loglevel; } return reply->status; } @@ -237,7 +237,7 @@ /* CTDB_CONTROL_SET_DEBUG */ void ctdb_req_control_set_debug(struct ctdb_req_control *request, - uint32_t loglevel) + int loglevel) { request->opcode = CTDB_CONTROL_SET_DEBUG; request->pad = 0; @@ -246,7 +246,7 @@ request->flags = 0; request->rdata.opcode = CTDB_CONTROL_SET_DEBUG; - request->rdata.data.loglevel = loglevel; + request->rdata.data.loglevel = (uint32_t)loglevel; } int ctdb_reply_control_set_debug(struct ctdb_reply_control *reply) @@ -1272,27 +1272,6 @@ return ctdb_reply_control_generic(reply, CTDB_CONTROL_DEL_PUBLIC_IP); } -/* CTDB_CONTROL_RUN_EVENTSCRIPTS */ - -void ctdb_req_control_run_eventscripts(struct ctdb_req_control *request, - const char *event_str) -{ - request->opcode = CTDB_CONTROL_RUN_EVENTSCRIPTS; - request->pad = 0; - request->srvid = 0; - request->client_id = 0; - request->flags = 0; - - request->rdata.opcode = CTDB_CONTROL_RUN_EVENTSCRIPTS; - request->rdata.data.event_str = event_str; -} - -int ctdb_reply_control_run_eventscripts(struct ctdb_reply_control *reply) -{ - return ctdb_reply_control_generic(reply, - CTDB_CONTROL_RUN_EVENTSCRIPTS); -} - /* CTDB_CONTROL_GET_CAPABILITIES */ void ctdb_req_control_get_capabilities(struct ctdb_req_control *request) @@ -1379,7 +1358,8 @@ /* CTDB_CONTROL_GET_PUBLIC_IPS */ -void ctdb_req_control_get_public_ips(struct ctdb_req_control *request) +void ctdb_req_control_get_public_ips(struct ctdb_req_control *request, + bool available_only) { request->opcode = CTDB_CONTROL_GET_PUBLIC_IPS; request->pad = 0; @@ -1388,6 +1368,9 @@ request->flags = 0; request->rdata.opcode = CTDB_CONTROL_GET_PUBLIC_IPS; + if (available_only) { + request->flags = CTDB_PUBLIC_IP_FLAGS_ONLY_AVAILABLE; + } } int ctdb_reply_control_get_public_ips(struct ctdb_reply_control *reply, @@ -1432,35 +1415,6 @@ return reply->status; } -/* CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS */ - -void ctdb_req_control_get_event_script_status(struct ctdb_req_control *request, - uint32_t event) -{ - request->opcode = CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS; - request->pad = 0; - request->srvid = 0; - request->client_id = 0; - request->flags = 0; - - request->rdata.opcode = CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS; - request->rdata.data.event = event; -} - -int ctdb_reply_control_get_event_script_status(struct ctdb_reply_control *reply, - TALLOC_CTX *mem_ctx, - struct ctdb_script_list **slist) -{ - if (reply->rdata.opcode != CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS) { - return EPROTO; - } - - if (reply->status == 0) { - *slist = talloc_steal(mem_ctx, reply->rdata.data.script_list); - } - return reply->status; -} - /* CTDB_CONTROL_TRAVERSE_KILL */ void ctdb_req_control_traverse_kill(struct ctdb_req_control *request, @@ -1607,46 +1561,6 @@ CTDB_CONTROL_SET_RECMASTERROLE); } -/* CTDB_CONTROL_ENABLE_SCRIPT */ - -void ctdb_req_control_enable_script(struct ctdb_req_control *request, - const char *script) -{ - request->opcode = CTDB_CONTROL_ENABLE_SCRIPT; - request->pad = 0; - request->srvid = 0; - request->client_id = 0; - request->flags = 0; - - request->rdata.opcode = CTDB_CONTROL_ENABLE_SCRIPT; - request->rdata.data.script = script; -} - -int ctdb_reply_control_enable_script(struct ctdb_reply_control *reply) -{ - return ctdb_reply_control_generic(reply, CTDB_CONTROL_ENABLE_SCRIPT); -} - -/* CTDB_CONTROL_DISABLE_SCRIPT */ - -void ctdb_req_control_disable_script(struct ctdb_req_control *request, - const char *script) -{ - request->opcode = CTDB_CONTROL_DISABLE_SCRIPT; - request->pad = 0; - request->srvid = 0; - request->client_id = 0; - request->flags = 0; - - request->rdata.opcode = CTDB_CONTROL_DISABLE_SCRIPT; - request->rdata.data.script = script; -} - -int ctdb_reply_control_disable_script(struct ctdb_reply_control *reply) -{ - return ctdb_reply_control_generic(reply, CTDB_CONTROL_DISABLE_SCRIPT); -} - /* CTDB_CONTROL_SET_BAN_STATE */ void ctdb_req_control_set_ban_state(struct ctdb_req_control *request, diff -Nru samba-4.5.8+dfsg/ctdb/protocol/protocol_control.c samba-4.6.5+dfsg/ctdb/protocol/protocol_control.c --- samba-4.5.8+dfsg/ctdb/protocol/protocol_control.c 2016-10-24 19:37:30.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/protocol/protocol_control.c 2017-01-11 07:55:14.000000000 +0000 @@ -253,10 +253,6 @@ len = ctdb_addr_info_len(cd->data.addr_info); break; - case CTDB_CONTROL_RUN_EVENTSCRIPTS: - len = ctdb_string_len(cd->data.event_str); - break; - case CTDB_CONTROL_GET_CAPABILITIES: break; @@ -277,10 +273,6 @@ case CTDB_CONTROL_GET_NODEMAP: break; - case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS: - len = ctdb_uint32_len(cd->data.event); - break; - case CTDB_CONTROL_TRAVERSE_KILL: len = ctdb_traverse_start_len(cd->data.traverse_start); break; @@ -306,14 +298,6 @@ len = ctdb_uint32_len(cd->data.role); break; - case CTDB_CONTROL_ENABLE_SCRIPT: - len = ctdb_string_len(cd->data.script); - break; - - case CTDB_CONTROL_DISABLE_SCRIPT: - len = ctdb_string_len(cd->data.script); - break; - case CTDB_CONTROL_SET_BAN_STATE: len = ctdb_ban_state_len(cd->data.ban_state); break; @@ -574,10 +558,6 @@ ctdb_addr_info_push(cd->data.addr_info, buf); break; - case CTDB_CONTROL_RUN_EVENTSCRIPTS: - ctdb_string_push(cd->data.event_str, buf); - break; - case CTDB_CONTROL_RELEASE_IP: ctdb_public_ip_push(cd->data.pubip, buf); break; @@ -586,10 +566,6 @@ ctdb_public_ip_push(cd->data.pubip, buf); break; - case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS: - ctdb_uint32_push(cd->data.event, buf); - break; - case CTDB_CONTROL_TRAVERSE_KILL: ctdb_traverse_start_push(cd->data.traverse_start, buf); break; @@ -606,14 +582,6 @@ ctdb_uint32_push(cd->data.role, buf); break; - case CTDB_CONTROL_ENABLE_SCRIPT: - ctdb_string_push(cd->data.script, buf); - break; - - case CTDB_CONTROL_DISABLE_SCRIPT: - ctdb_string_push(cd->data.script, buf); - break; - case CTDB_CONTROL_SET_BAN_STATE: ctdb_ban_state_push(cd->data.ban_state, buf); break; @@ -887,11 +855,6 @@ &cd->data.addr_info); break; - case CTDB_CONTROL_RUN_EVENTSCRIPTS: - ret = ctdb_string_pull(buf, buflen, mem_ctx, - &cd->data.event_str); - break; - case CTDB_CONTROL_RELEASE_IP: ret = ctdb_public_ip_pull(buf, buflen, mem_ctx, &cd->data.pubip); @@ -902,11 +865,6 @@ &cd->data.pubip); break; - case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS: - ret = ctdb_uint32_pull(buf, buflen, mem_ctx, - &cd->data.event); - break; - case CTDB_CONTROL_TRAVERSE_KILL: ret = ctdb_traverse_start_pull(buf, buflen, mem_ctx, &cd->data.traverse_start); @@ -927,16 +885,6 @@ &cd->data.role); break; - case CTDB_CONTROL_ENABLE_SCRIPT: - ret = ctdb_string_pull(buf, buflen, mem_ctx, - &cd->data.script); - break; - - case CTDB_CONTROL_DISABLE_SCRIPT: - ret = ctdb_string_pull(buf, buflen, mem_ctx, - &cd->data.script); - break; - case CTDB_CONTROL_SET_BAN_STATE: ret = ctdb_ban_state_pull(buf, buflen, mem_ctx, &cd->data.ban_state); @@ -1269,9 +1217,6 @@ case CTDB_CONTROL_DEL_PUBLIC_IP: break; - case CTDB_CONTROL_RUN_EVENTSCRIPTS: - break; - case CTDB_CONTROL_GET_CAPABILITIES: len = ctdb_uint32_len(cd->data.caps); break; @@ -1293,10 +1238,6 @@ len = ctdb_node_map_len(cd->data.nodemap); break; - case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS: - len = ctdb_script_list_len(cd->data.script_list); - break; - case CTDB_CONTROL_TRAVERSE_KILL: break; @@ -1319,12 +1260,6 @@ case CTDB_CONTROL_SET_RECMASTERROLE: break; - case CTDB_CONTROL_ENABLE_SCRIPT: - break; - - case CTDB_CONTROL_DISABLE_SCRIPT: - break; - case CTDB_CONTROL_SET_BAN_STATE: break; @@ -1539,10 +1474,6 @@ ctdb_node_map_push(cd->data.nodemap, buf); break; - case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS: - ctdb_script_list_push(cd->data.script_list, buf); - break; - case CTDB_CONTROL_GET_RECLOCK_FILE: ctdb_string_push(cd->data.reclock_file, buf); break; @@ -1716,11 +1647,6 @@ &cd->data.nodemap); break; - case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS: - ret = ctdb_script_list_pull(buf, buflen, mem_ctx, - &cd->data.script_list); - break; - case CTDB_CONTROL_GET_RECLOCK_FILE: ret = ctdb_string_pull(buf, buflen, mem_ctx, &cd->data.reclock_file); diff -Nru samba-4.5.8+dfsg/ctdb/protocol/protocol_event.c samba-4.6.5+dfsg/ctdb/protocol/protocol_event.c --- samba-4.5.8+dfsg/ctdb/protocol/protocol_event.c 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/protocol/protocol_event.c 2017-01-11 07:55:14.000000000 +0000 @@ -0,0 +1,848 @@ +/* + CTDB eventd protocol marshalling + + Copyright (C) Amitay Isaacs 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include "replace.h" +#include "system/network.h" + +#include + +#include "protocol.h" +#include "protocol_private.h" +#include "protocol_api.h" + +static size_t ctdb_event_len(enum ctdb_event in) +{ + return ctdb_uint32_len((uint32_t)in); +} + +static void ctdb_event_push(enum ctdb_event in, uint8_t *buf) +{ + ctdb_uint32_push((uint32_t)in, buf); +} + +static int ctdb_event_pull(uint8_t *buf, size_t buflen, + TALLOC_CTX *mem_ctx, enum ctdb_event *out) +{ + uint32_t uint32_value; + enum ctdb_event value; + int ret; + + ret = ctdb_uint32_pull(buf, buflen, mem_ctx, &uint32_value); + if (ret != 0) { + return ret; + } + + switch (uint32_value) { + case 0: + value = CTDB_EVENT_INIT; + break; + + case 1: + value = CTDB_EVENT_SETUP; + break; + + case 2: + value = CTDB_EVENT_STARTUP; + break; + + case 3: + value = CTDB_EVENT_START_RECOVERY; + break; + + case 4: + value = CTDB_EVENT_RECOVERED; + break; + + case 5: + value = CTDB_EVENT_TAKE_IP; + break; + + case 6: + value = CTDB_EVENT_RELEASE_IP; + break; + + case 7: + value = CTDB_EVENT_STOPPED; + break; + + case 8: + value = CTDB_EVENT_MONITOR; + break; + + case 9: + value = CTDB_EVENT_STATUS; + break; + + case 10: + value = CTDB_EVENT_SHUTDOWN; + break; + + case 11: + value = CTDB_EVENT_RELOAD; + break; + + case 12: + value = CTDB_EVENT_UPDATE_IP; + break; + + case 13: + value = CTDB_EVENT_IPREALLOCATED; + break; + + default: + return EINVAL; + } + + *out = value; + return 0; +} + +static size_t ctdb_event_command_len(enum ctdb_event_command in) +{ + return ctdb_uint32_len((uint32_t)in); +} + +static void ctdb_event_command_push(enum ctdb_event_command in, uint8_t *buf) +{ + ctdb_uint32_push((uint32_t)in, buf); +} + +static int ctdb_event_command_pull(uint8_t *buf, size_t buflen, + TALLOC_CTX *mem_ctx, + enum ctdb_event_command *out) +{ + uint32_t uint32_value; + enum ctdb_event_command value; + int ret; + + ret = ctdb_uint32_pull(buf, buflen, mem_ctx, &uint32_value); + if (ret != 0) { + return ret; + } + + switch (uint32_value) { + case 1: + value = CTDB_EVENT_COMMAND_RUN; + break; + + case 2: + value = CTDB_EVENT_COMMAND_STATUS; + break; + + case 3: + value = CTDB_EVENT_COMMAND_SCRIPT_LIST; + break; + + case 4: + value = CTDB_EVENT_COMMAND_SCRIPT_ENABLE; + break; + + case 5: + value = CTDB_EVENT_COMMAND_SCRIPT_DISABLE; + break; + + default: + return EINVAL; + } + + *out = value; + return 0; +} + +static size_t ctdb_event_status_state_len(enum ctdb_event_status_state in) +{ + return ctdb_uint32_len((uint32_t)in); +} + +static void ctdb_event_status_state_push(enum ctdb_event_status_state in, + uint8_t *buf) +{ + ctdb_uint32_push((uint32_t)in, buf); +} + +static int ctdb_event_status_state_pull(uint8_t *buf, size_t buflen, + TALLOC_CTX *mem_ctx, + enum ctdb_event_status_state *out) +{ + uint32_t uint32_value; + enum ctdb_event_status_state value; + int ret; + + ret = ctdb_uint32_pull(buf, buflen, mem_ctx, &uint32_value); + if (ret != 0) { + return ret; + } + + switch (uint32_value) { + case 1: + value = CTDB_EVENT_LAST_RUN; + break; + + case 2: + value = CTDB_EVENT_LAST_PASS; + break; + + case 3: + value = CTDB_EVENT_LAST_FAIL; + break; + + default: + return EINVAL; + } + + *out = value; + return 0; +} + +static size_t ctdb_event_request_run_len(struct ctdb_event_request_run *in) +{ + return ctdb_event_len(in->event) + + ctdb_uint32_len(in->timeout) + + ctdb_stringn_len(in->arg_str); +} + +static void ctdb_event_request_run_push(struct ctdb_event_request_run *in, + uint8_t *buf) +{ + size_t offset = 0; + + ctdb_event_push(in->event, buf); + offset += ctdb_event_len(in->event); + + ctdb_uint32_push(in->timeout, buf+offset); + offset += ctdb_uint32_len(in->timeout); + + ctdb_stringn_push(in->arg_str, buf+offset); +} + +static int ctdb_event_request_run_pull(uint8_t *buf, size_t buflen, + TALLOC_CTX *mem_ctx, + struct ctdb_event_request_run **out) +{ + struct ctdb_event_request_run *rdata; + size_t offset = 0; + int ret; + + rdata = talloc(mem_ctx, struct ctdb_event_request_run); + if (rdata == NULL) { + return ENOMEM; + } + + ret = ctdb_event_pull(buf, buflen, rdata, &rdata->event); + if (ret != 0) { + goto fail; + } + offset += ctdb_event_len(rdata->event); + + ret = ctdb_uint32_pull(buf+offset, buflen-offset, + rdata, &rdata->timeout); + if (ret != 0) { + goto fail; + } + offset += ctdb_uint32_len(rdata->timeout); + + ret = ctdb_stringn_pull(buf+offset, buflen-offset, + rdata, &rdata->arg_str); + if (ret != 0) { + goto fail; + } + + *out = rdata; + return 0; + +fail: + talloc_free(rdata); + return ret; +} + +static size_t ctdb_event_request_status_len( + struct ctdb_event_request_status *in) +{ + return ctdb_event_len(in->event) + + ctdb_event_status_state_len(in->state); +} + +static void ctdb_event_request_status_push( + struct ctdb_event_request_status *in, + uint8_t *buf) +{ + size_t offset = 0; + + ctdb_event_push(in->event, buf); + offset += ctdb_event_len(in->event); + + ctdb_event_status_state_push(in->state, buf+offset); +} + +static int ctdb_event_request_status_pull( + uint8_t *buf, size_t buflen, + TALLOC_CTX *mem_ctx, + struct ctdb_event_request_status **out) +{ + struct ctdb_event_request_status *rdata; + size_t offset = 0; + int ret; + + rdata = talloc(mem_ctx, struct ctdb_event_request_status); + if (rdata == NULL) { + return ENOMEM; + } + + ret = ctdb_event_pull(buf, buflen, rdata, &rdata->event); + if (ret != 0) { + talloc_free(rdata); + return ret; + } + offset += ctdb_event_len(rdata->event); + + ret = ctdb_event_status_state_pull(buf+offset, buflen-offset, + rdata, &rdata->state); + if (ret != 0) { + talloc_free(rdata); + return ret; + } + + *out = rdata; + return 0; +} + +static size_t ctdb_event_request_script_enable_len( + struct ctdb_event_request_script_enable *in) +{ + return ctdb_stringn_len(in->script_name); +} + +static void ctdb_event_request_script_enable_push( + struct ctdb_event_request_script_enable *in, + uint8_t *buf) +{ + ctdb_stringn_push(in->script_name, buf); +} + +static int ctdb_event_request_script_enable_pull( + uint8_t *buf, size_t buflen, + TALLOC_CTX *mem_ctx, + struct ctdb_event_request_script_enable **out) +{ + struct ctdb_event_request_script_enable *rdata; + int ret; + + rdata = talloc(mem_ctx, struct ctdb_event_request_script_enable); + if (rdata == NULL) { + return ENOMEM; + } + + ret = ctdb_stringn_pull(buf, buflen, rdata, &rdata->script_name); + if (ret != 0) { + talloc_free(rdata); + return ret; + } + + *out = rdata; + return 0; +} + +static size_t ctdb_event_request_script_disable_len( + struct ctdb_event_request_script_disable *in) +{ + return ctdb_stringn_len(in->script_name); +} + +static void ctdb_event_request_script_disable_push( + struct ctdb_event_request_script_disable *in, + uint8_t *buf) +{ + ctdb_stringn_push(in->script_name, buf); +} + +static int ctdb_event_request_script_disable_pull( + uint8_t *buf, size_t buflen, + TALLOC_CTX *mem_ctx, + struct ctdb_event_request_script_disable **out) +{ + struct ctdb_event_request_script_disable *rdata; + int ret; + + rdata = talloc(mem_ctx, struct ctdb_event_request_script_disable); + if (rdata == NULL) { + return ENOMEM; + } + + ret = ctdb_stringn_pull(buf, buflen, rdata, &rdata->script_name); + if (ret != 0) { + talloc_free(rdata); + return ret; + } + + *out = rdata; + return 0; +} + +static size_t ctdb_event_request_data_len(struct ctdb_event_request_data *in) +{ + size_t len = 0; + + len += ctdb_event_command_len(in->command); + + switch(in->command) { + case CTDB_EVENT_COMMAND_RUN: + len += ctdb_event_request_run_len(in->data.run); + break; + + case CTDB_EVENT_COMMAND_STATUS: + len += ctdb_event_request_status_len(in->data.status); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_LIST: + break; + + case CTDB_EVENT_COMMAND_SCRIPT_ENABLE: + len += ctdb_event_request_script_enable_len( + in->data.script_enable); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_DISABLE: + len += ctdb_event_request_script_disable_len( + in->data.script_disable); + break; + } + + return len; +} + +static void ctdb_event_request_data_push(struct ctdb_event_request_data *in, + uint8_t *buf) +{ + size_t offset = 0; + + ctdb_event_command_push(in->command, buf); + offset += ctdb_event_command_len(in->command); + + switch (in->command) { + case CTDB_EVENT_COMMAND_RUN: + ctdb_event_request_run_push(in->data.run, buf+offset); + break; + + case CTDB_EVENT_COMMAND_STATUS: + ctdb_event_request_status_push(in->data.status, buf+offset); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_LIST: + break; + + case CTDB_EVENT_COMMAND_SCRIPT_ENABLE: + ctdb_event_request_script_enable_push( + in->data.script_enable, + buf+offset); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_DISABLE: + ctdb_event_request_script_disable_push( + in->data.script_disable, + buf+offset); + break; + } +} + +static int ctdb_event_request_data_pull(uint8_t *buf, size_t buflen, + TALLOC_CTX *mem_ctx, + struct ctdb_event_request_data *out) +{ + size_t offset = 0; + int ret; + + ret = ctdb_event_command_pull(buf, buflen, mem_ctx, &out->command); + if (ret != 0) { + return ret; + } + offset += ctdb_event_command_len(out->command); + + switch (out->command) { + case CTDB_EVENT_COMMAND_RUN: + ret = ctdb_event_request_run_pull(buf+offset, buflen-offset, + mem_ctx, &out->data.run); + break; + + case CTDB_EVENT_COMMAND_STATUS: + ret = ctdb_event_request_status_pull( + buf+offset, buflen-offset, + mem_ctx, &out->data.status); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_LIST: + ret = 0; + break; + + case CTDB_EVENT_COMMAND_SCRIPT_ENABLE: + ret = ctdb_event_request_script_enable_pull( + buf+offset, buflen-offset, + mem_ctx, + &out->data.script_enable); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_DISABLE: + ret = ctdb_event_request_script_disable_pull( + buf+offset, buflen-offset, + mem_ctx, + &out->data.script_disable); + break; + } + + if (ret != 0) { + return ret; + } + + return 0; +} + +static size_t ctdb_event_reply_status_len(struct ctdb_event_reply_status *in) +{ + return ctdb_int32_len(in->status) + + ctdb_script_list_len(in->script_list); +} + +static void ctdb_event_reply_status_push(struct ctdb_event_reply_status *in, + uint8_t *buf) +{ + size_t offset = 0; + + ctdb_int32_push(in->status, buf); + offset += ctdb_int32_len(in->status); + + ctdb_script_list_push(in->script_list, buf+offset); +} + +static int ctdb_event_reply_status_pull(uint8_t *buf, size_t buflen, + TALLOC_CTX *mem_ctx, + struct ctdb_event_reply_status **out) +{ + struct ctdb_event_reply_status *rdata; + size_t offset = 0; + int ret; + + rdata = talloc(mem_ctx, struct ctdb_event_reply_status); + if (rdata == NULL) { + return ENOMEM; + } + + ret = ctdb_int32_pull(buf, buflen, rdata, &rdata->status); + if (ret != 0) { + talloc_free(rdata); + return ret; + } + offset += ctdb_int32_len(rdata->status); + + ret = ctdb_script_list_pull(buf+offset, buflen-offset, + rdata, &rdata->script_list); + if (ret != 0) { + talloc_free(rdata); + return ret; + } + + *out = rdata; + return 0; +} + +static size_t ctdb_event_reply_script_list_len( + struct ctdb_event_reply_script_list *in) +{ + return ctdb_script_list_len(in->script_list); +} + +static void ctdb_event_reply_script_list_push( + struct ctdb_event_reply_script_list *in, + uint8_t *buf) +{ + ctdb_script_list_push(in->script_list, buf); +} + +static int ctdb_event_reply_script_list_pull( + uint8_t *buf, size_t buflen, + TALLOC_CTX *mem_ctx, + struct ctdb_event_reply_script_list **out) +{ + struct ctdb_event_reply_script_list *rdata; + int ret; + + rdata = talloc(mem_ctx, struct ctdb_event_reply_script_list); + if (rdata == NULL) { + return ENOMEM; + } + + ret = ctdb_script_list_pull(buf, buflen, rdata, &rdata->script_list); + if (ret != 0) { + talloc_free(rdata); + return ret; + } + + *out = rdata; + return 0; +} + +static size_t ctdb_event_reply_data_len(struct ctdb_event_reply_data *in) +{ + size_t len = 0; + + len += ctdb_event_command_len(in->command); + len += ctdb_int32_len(in->result); + + switch (in->command) { + case CTDB_EVENT_COMMAND_RUN: + break; + + case CTDB_EVENT_COMMAND_STATUS: + len += ctdb_event_reply_status_len(in->data.status); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_LIST: + len += ctdb_event_reply_script_list_len(in->data.script_list); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_ENABLE: + break; + + case CTDB_EVENT_COMMAND_SCRIPT_DISABLE: + break; + } + + return len; +} + +static void ctdb_event_reply_data_push(struct ctdb_event_reply_data *in, + uint8_t *buf) +{ + size_t offset = 0; + + ctdb_event_command_push(in->command, buf); + offset += ctdb_event_command_len(in->command); + + ctdb_int32_push(in->result, buf+offset); + offset += ctdb_int32_len(in->result); + + switch (in->command) { + case CTDB_EVENT_COMMAND_RUN: + break; + + case CTDB_EVENT_COMMAND_STATUS: + ctdb_event_reply_status_push(in->data.status, buf+offset); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_LIST: + ctdb_event_reply_script_list_push(in->data.script_list, + buf+offset); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_ENABLE: + break; + + case CTDB_EVENT_COMMAND_SCRIPT_DISABLE: + break; + } +} + +static int ctdb_event_reply_data_pull(uint8_t *buf, size_t buflen, + TALLOC_CTX *mem_ctx, + struct ctdb_event_reply_data *out) +{ + size_t offset = 0; + int ret; + + ret = ctdb_event_command_pull(buf, buflen, mem_ctx, &out->command); + if (ret != 0) { + return ret; + } + offset += ctdb_event_command_len(out->command); + + ret = ctdb_int32_pull(buf+offset, buflen-offset, + mem_ctx, &out->result); + if (ret != 0) { + return ret; + } + offset += ctdb_int32_len(out->result); + + switch (out->command) { + case CTDB_EVENT_COMMAND_RUN: + break; + + case CTDB_EVENT_COMMAND_STATUS: + ret = ctdb_event_reply_status_pull( + buf+offset, buflen-offset, + mem_ctx, &out->data.status); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_LIST: + ret = ctdb_event_reply_script_list_pull( + buf+offset, buflen-offset, + mem_ctx, &out->data.script_list); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_ENABLE: + break; + + case CTDB_EVENT_COMMAND_SCRIPT_DISABLE: + break; + } + + if (ret != 0) { + return ret; + } + + return 0; +} + +static size_t ctdb_event_header_len(struct ctdb_event_header *in) +{ + return ctdb_uint32_len(in->length) + ctdb_uint32_len(in->reqid); +} + +static void ctdb_event_header_push(struct ctdb_event_header *in, uint8_t *buf) +{ + size_t offset = 0; + + ctdb_uint32_push(in->length, buf); + offset += ctdb_uint32_len(in->length); + + ctdb_uint32_push(in->reqid, buf+offset); +} + +static int ctdb_event_header_pull(uint8_t *buf, size_t buflen, + TALLOC_CTX *mem_ctx, + struct ctdb_event_header *out) +{ + size_t offset = 0; + int ret; + + ret = ctdb_uint32_pull(buf, buflen, mem_ctx, &out->length); + if (ret != 0) { + return ret; + } + offset += ctdb_uint32_len(out->length); + + ret = ctdb_uint32_pull(buf+offset, buflen-offset, + mem_ctx, &out->reqid); + if (ret != 0) { + return ret; + } + + return 0; +} + +void ctdb_event_header_fill(struct ctdb_event_header *h, uint32_t reqid) +{ + h->length = ctdb_event_header_len(h); + h->reqid = reqid; +} + +size_t ctdb_event_request_len(struct ctdb_event_request *in) +{ + return ctdb_event_header_len(&in->header) + + ctdb_event_request_data_len(&in->rdata); +} + +int ctdb_event_request_push(struct ctdb_event_request *in, + uint8_t *buf, size_t *buflen) +{ + size_t len, offset = 0; + + len = ctdb_event_request_len(in); + if (*buflen < len) { + *buflen = len; + return EMSGSIZE; + } + + in->header.length = *buflen; + + ctdb_event_header_push(&in->header, buf); + offset += ctdb_event_header_len(&in->header); + + ctdb_event_request_data_push(&in->rdata, buf+offset); + + return 0; +} + +int ctdb_event_request_pull(uint8_t *buf, size_t buflen, + TALLOC_CTX *mem_ctx, + struct ctdb_event_request *out) +{ + size_t offset = 0; + int ret; + + ret = ctdb_event_header_pull(buf, buflen, mem_ctx, &out->header); + if (ret != 0) { + return ret; + } + offset += ctdb_event_header_len(&out->header); + + ret = ctdb_event_request_data_pull(buf+offset, buflen-offset, + mem_ctx, &out->rdata); + if (ret != 0) { + return ret; + } + + return 0; +} + +size_t ctdb_event_reply_len(struct ctdb_event_reply *in) +{ + return ctdb_event_header_len(&in->header) + + ctdb_event_reply_data_len(&in->rdata); +} + +int ctdb_event_reply_push(struct ctdb_event_reply *in, + uint8_t *buf, size_t *buflen) +{ + size_t len, offset = 0; + + len = ctdb_event_reply_len(in); + if (*buflen < len) { + *buflen = len; + return EMSGSIZE; + } + + in->header.length = *buflen; + + ctdb_event_header_push(&in->header, buf); + offset += ctdb_event_header_len(&in->header); + + ctdb_event_reply_data_push(&in->rdata, buf+offset); + + return 0; +} + +int ctdb_event_reply_pull(uint8_t *buf, size_t buflen, + TALLOC_CTX *mem_ctx, + struct ctdb_event_reply *out) +{ + size_t offset = 0; + int ret; + + ret = ctdb_event_header_pull(buf, buflen, mem_ctx, &out->header); + if (ret != 0) { + return ret; + } + offset += ctdb_event_header_len(&out->header); + + ret = ctdb_event_reply_data_pull(buf+offset, buflen-offset, + mem_ctx, &out->rdata); + if (ret != 0) { + return ret; + } + + return 0; +} diff -Nru samba-4.5.8+dfsg/ctdb/protocol/protocol.h samba-4.6.5+dfsg/ctdb/protocol/protocol.h --- samba-4.5.8+dfsg/ctdb/protocol/protocol.h 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/protocol/protocol.h 2017-01-11 07:55:14.000000000 +0000 @@ -295,7 +295,7 @@ CTDB_CONTROL_DISABLE_MONITOR = 76, CTDB_CONTROL_ADD_PUBLIC_IP = 77, CTDB_CONTROL_DEL_PUBLIC_IP = 78, - CTDB_CONTROL_RUN_EVENTSCRIPTS = 79, + CTDB_CONTROL_RUN_EVENTSCRIPTS = 79, /* obsolete */ CTDB_CONTROL_GET_CAPABILITIES = 80, CTDB_CONTROL_START_PERSISTENT_UPDATE = 81, /* obsolete */ CTDB_CONTROL_CANCEL_PERSISTENT_UPDATE= 82, /* obsolete */ @@ -309,7 +309,7 @@ CTDB_CONTROL_GET_PUBLIC_IPS = 90, CTDB_CONTROL_GET_NODEMAP = 91, /* missing */ - CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS = 96, + CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS = 96, /* obsolete */ CTDB_CONTROL_TRAVERSE_KILL = 97, CTDB_CONTROL_RECD_RECLOCK_LATENCY = 98, CTDB_CONTROL_GET_RECLOCK_FILE = 99, @@ -319,8 +319,8 @@ CTDB_CONTROL_SET_NATGWSTATE = 103, /* obsolete */ CTDB_CONTROL_SET_LMASTERROLE = 104, CTDB_CONTROL_SET_RECMASTERROLE = 105, - CTDB_CONTROL_ENABLE_SCRIPT = 107, - CTDB_CONTROL_DISABLE_SCRIPT = 108, + CTDB_CONTROL_ENABLE_SCRIPT = 107, /* obsolete */ + CTDB_CONTROL_DISABLE_SCRIPT = 108, /* obsolete */ CTDB_CONTROL_SET_BAN_STATE = 109, CTDB_CONTROL_GET_BAN_STATE = 110, CTDB_CONTROL_SET_DB_PRIORITY = 111, /* obsolete */ @@ -631,6 +631,7 @@ uint32_t lock_processes_per_db; uint32_t rec_buffer_size_limit; uint32_t queue_buffer_size; + uint32_t ip_alloc_algorithm; }; struct ctdb_tickle_list { @@ -856,12 +857,10 @@ struct ctdb_client_id *cid; struct ctdb_addr_info *addr_info; struct ctdb_transdb *transdb; - const char *event_str; struct ctdb_public_ip *pubip; enum ctdb_event event; double reclock_latency; uint32_t role; - const char *script; struct ctdb_ban_state *ban_state; struct ctdb_notify_data *notify; uint64_t srvid; @@ -894,7 +893,6 @@ uint32_t caps; struct ctdb_public_ip_list *pubip_list; struct ctdb_node_map *nodemap; - struct ctdb_script_list *script_list; const char *reclock_file; struct ctdb_ban_state *ban_state; uint64_t seqnum; @@ -916,6 +914,8 @@ uint32_t client_id; #define CTDB_CTRL_FLAG_NOREPLY 1 #define CTDB_CTRL_FLAG_OPCODE_SPECIFIC 0xFFFF0000 +/* Ugly overloading of this field... */ +#define CTDB_PUBLIC_IP_FLAGS_ONLY_AVAILABLE 0x00010000 uint32_t flags; struct ctdb_req_control_data rdata; }; @@ -1002,4 +1002,84 @@ struct ctdb_g_lock *lock; }; +/* + * Eventd protocol + */ + +enum ctdb_event_command { + CTDB_EVENT_COMMAND_RUN = 1, + CTDB_EVENT_COMMAND_STATUS = 2, + CTDB_EVENT_COMMAND_SCRIPT_LIST = 3, + CTDB_EVENT_COMMAND_SCRIPT_ENABLE = 4, + CTDB_EVENT_COMMAND_SCRIPT_DISABLE = 5, +}; + +enum ctdb_event_status_state { + CTDB_EVENT_LAST_RUN = 1, + CTDB_EVENT_LAST_PASS = 2, + CTDB_EVENT_LAST_FAIL = 3, +}; + +struct ctdb_event_request_run { + enum ctdb_event event; + uint32_t timeout; + const char *arg_str; +}; + +struct ctdb_event_request_status { + enum ctdb_event event; + enum ctdb_event_status_state state; +}; + +struct ctdb_event_request_script_enable { + const char *script_name; +}; + +struct ctdb_event_request_script_disable { + const char *script_name; +}; + +struct ctdb_event_request_data { + enum ctdb_event_command command; + union { + struct ctdb_event_request_run *run; + struct ctdb_event_request_status *status; + struct ctdb_event_request_script_enable *script_enable; + struct ctdb_event_request_script_disable *script_disable; + } data; +}; + +struct ctdb_event_reply_status { + int status; + struct ctdb_script_list *script_list; +}; + +struct ctdb_event_reply_script_list { + struct ctdb_script_list *script_list; +}; + +struct ctdb_event_reply_data { + enum ctdb_event_command command; + int32_t result; + union { + struct ctdb_event_reply_status *status; + struct ctdb_event_reply_script_list *script_list; + } data; +}; + +struct ctdb_event_header { + uint32_t length; + uint32_t reqid; +}; + +struct ctdb_event_request { + struct ctdb_event_header header; + struct ctdb_event_request_data rdata; +}; + +struct ctdb_event_reply { + struct ctdb_event_header header; + struct ctdb_event_reply_data rdata; +}; + #endif /* __CTDB_PROTOCOL_H__ */ diff -Nru samba-4.5.8+dfsg/ctdb/protocol/protocol_private.h samba-4.6.5+dfsg/ctdb/protocol/protocol_private.h --- samba-4.5.8+dfsg/ctdb/protocol/protocol_private.h 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/protocol/protocol_private.h 2017-01-11 07:55:14.000000000 +0000 @@ -22,6 +22,11 @@ #include "protocol.h" +size_t ctdb_int32_len(int32_t val); +void ctdb_int32_push(int32_t val, uint8_t *buf); +int ctdb_int32_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx, + int32_t *out); + size_t ctdb_uint32_len(uint32_t val); void ctdb_uint32_push(uint32_t val, uint8_t *buf); int ctdb_uint32_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx, diff -Nru samba-4.5.8+dfsg/ctdb/protocol/protocol_types.c samba-4.6.5+dfsg/ctdb/protocol/protocol_types.c --- samba-4.5.8+dfsg/ctdb/protocol/protocol_types.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/protocol/protocol_types.c 2017-01-11 07:55:14.000000000 +0000 @@ -27,6 +27,27 @@ #include "protocol_private.h" #include "protocol_api.h" +size_t ctdb_int32_len(int32_t val) +{ + return sizeof(int32_t); +} + +void ctdb_int32_push(int32_t val, uint8_t *buf) +{ + memcpy(buf, &val, sizeof(int32_t)); +} + +int ctdb_int32_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx, + int32_t *out) +{ + if (buflen < sizeof(int32_t)) { + return EMSGSIZE; + } + + *out = *(int32_t *)buf; + return 0; +} + size_t ctdb_uint32_len(uint32_t val) { return sizeof(uint32_t); @@ -231,19 +252,15 @@ size_t ctdb_stringn_len(const char *str) { - return sizeof(uint32_t) + strlen(str) + 1; + return sizeof(uint32_t) + ctdb_string_len(str); } void ctdb_stringn_push(const char *str, uint8_t *buf) { struct stringn_wire *wire = (struct stringn_wire *)buf; - if (str == NULL) { - wire->length = 0; - } else { - wire->length = strlen(str) + 1; - memcpy(wire->str, str, wire->length); - } + wire->length = ctdb_string_len(str); + ctdb_string_push(str, wire->str); } int ctdb_stringn_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx, @@ -265,6 +282,11 @@ return EMSGSIZE; } + if (wire->length == 0) { + *out = NULL; + return 0; + } + str = talloc_strndup(mem_ctx, (char *)wire->str, wire->length); if (str == NULL) { return ENOMEM; diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_call.c samba-4.6.5+dfsg/ctdb/server/ctdb_call.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_call.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_call.c 2017-06-06 07:47:19.000000000 +0000 @@ -30,6 +30,7 @@ #include "lib/util/dlinklist.h" #include "lib/util/debug.h" #include "lib/util/samba_util.h" +#include "lib/util/sys_rw.h" #include "lib/util/util_process.h" #include "ctdb_private.h" @@ -1561,6 +1562,7 @@ struct revokechild_deferred_call { + struct revokechild_deferred_call *prev, *next; struct ctdb_context *ctdb; struct ctdb_req_header *hdr; deferred_requeue_fn fn; @@ -1576,48 +1578,31 @@ int fd[2]; pid_t child; TDB_DATA key; -}; - -struct revokechild_requeue_handle { - struct ctdb_context *ctdb; - struct ctdb_req_header *hdr; - deferred_requeue_fn fn; - void *ctx; + struct revokechild_deferred_call *deferred_call_list; }; static void deferred_call_requeue(struct tevent_context *ev, struct tevent_timer *te, struct timeval t, void *private_data) { - struct revokechild_requeue_handle *requeue_handle = talloc_get_type(private_data, struct revokechild_requeue_handle); + struct revokechild_deferred_call *dlist = talloc_get_type_abort( + private_data, struct revokechild_deferred_call); - requeue_handle->fn(requeue_handle->ctx, requeue_handle->hdr); - talloc_free(requeue_handle); -} + while (dlist != NULL) { + struct revokechild_deferred_call *dcall = dlist; -static int deferred_call_destructor(struct revokechild_deferred_call *deferred_call) -{ - struct ctdb_context *ctdb = deferred_call->ctdb; - struct revokechild_requeue_handle *requeue_handle = talloc(ctdb, struct revokechild_requeue_handle); - struct ctdb_req_call_old *c = (struct ctdb_req_call_old *)deferred_call->hdr; - - requeue_handle->ctdb = ctdb; - requeue_handle->hdr = deferred_call->hdr; - requeue_handle->fn = deferred_call->fn; - requeue_handle->ctx = deferred_call->ctx; - talloc_steal(requeue_handle, requeue_handle->hdr); - - /* when revoking, any READONLY requests have 1 second grace to let read/write finish first */ - tevent_add_timer(ctdb->ev, requeue_handle, - timeval_current_ofs(c->flags & CTDB_WANT_READONLY ? 1 : 0, 0), - deferred_call_requeue, requeue_handle); - - return 0; + DLIST_REMOVE(dlist, dcall); + dcall->fn(dcall->ctx, dcall->hdr); + talloc_free(dcall); + } } static int revokechild_destructor(struct revokechild_handle *rc) { + struct revokechild_deferred_call *now_list = NULL; + struct revokechild_deferred_call *delay_list = NULL; + if (rc->fde != NULL) { talloc_free(rc->fde); } @@ -1631,6 +1616,48 @@ ctdb_kill(rc->ctdb, rc->child, SIGKILL); DLIST_REMOVE(rc->ctdb_db->revokechild_active, rc); + + while (rc->deferred_call_list != NULL) { + struct revokechild_deferred_call *dcall; + + dcall = rc->deferred_call_list; + DLIST_REMOVE(rc->deferred_call_list, dcall); + + /* If revoke is successful, then first process all the calls + * that need write access, and delay readonly requests by 1 + * second grace. + * + * If revoke is unsuccessful, most likely because of node + * failure, delay all the pending requests, so database can + * be recovered. + */ + + if (rc->status == 0) { + struct ctdb_req_call_old *c; + + c = (struct ctdb_req_call_old *)dcall->hdr; + if (c->flags & CTDB_WANT_READONLY) { + DLIST_ADD(delay_list, dcall); + } else { + DLIST_ADD(now_list, dcall); + } + } else { + DLIST_ADD(delay_list, dcall); + } + } + + if (now_list != NULL) { + tevent_add_timer(rc->ctdb->ev, rc->ctdb_db, + tevent_timeval_current_ofs(0, 0), + deferred_call_requeue, now_list); + } + + if (delay_list != NULL) { + tevent_add_timer(rc->ctdb->ev, rc->ctdb_db, + tevent_timeval_current_ofs(1, 0), + deferred_call_requeue, delay_list); + } + return 0; } @@ -1852,10 +1879,9 @@ if (rc->child == 0) { char c = 0; close(rc->fd[0]); - debug_extra = talloc_asprintf(NULL, "revokechild-%s:", ctdb_db->db_name); prctl_set_comment("ctdb_revokechild"); - if (switch_from_server_to_client(ctdb, "revokechild-%s", ctdb_db->db_name) != 0) { + if (switch_from_server_to_client(ctdb) != 0) { DEBUG(DEBUG_ERR,("Failed to switch from server to client for revokechild process\n")); c = 1; goto child_finished; @@ -1909,19 +1935,18 @@ return -1; } - deferred_call = talloc(rc, struct revokechild_deferred_call); + deferred_call = talloc(ctdb_db, struct revokechild_deferred_call); if (deferred_call == NULL) { DEBUG(DEBUG_ERR,("Failed to allocate deferred call structure for revoking record\n")); return -1; } deferred_call->ctdb = ctdb; - deferred_call->hdr = hdr; + deferred_call->hdr = talloc_steal(deferred_call, hdr); deferred_call->fn = fn; deferred_call->ctx = call_context; - talloc_set_destructor(deferred_call, deferred_call_destructor); - talloc_steal(deferred_call, hdr); + DLIST_ADD(rc->deferred_call_list, deferred_call); return 0; } diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_cluster_mutex.c samba-4.6.5+dfsg/ctdb/server/ctdb_cluster_mutex.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_cluster_mutex.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_cluster_mutex.c 2017-01-11 07:55:14.000000000 +0000 @@ -28,12 +28,12 @@ #include "lib/util/time.h" #include "lib/util/strv.h" #include "lib/util/strv_util.h" +#include "lib/util/sys_rw.h" #include "lib/util/blocking.h" #include "ctdb_private.h" #include "common/common.h" #include "common/logging.h" -#include "common/system.h" #include "ctdb_cluster_mutex.h" diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_control.c samba-4.6.5+dfsg/ctdb/server/ctdb_control.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_control.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_control.c 2017-01-11 07:55:14.000000000 +0000 @@ -332,9 +332,9 @@ CHECK_CONTROL_DATA_SIZE(0); ctdb_enable_monitoring(ctdb); return 0; - - case CTDB_CONTROL_RUN_EVENTSCRIPTS: - return ctdb_run_eventscripts(ctdb, c, indata, async_reply); + + case CTDB_CONTROL_RUN_EVENTSCRIPTS: + return control_not_implemented("RUN_EVENTSCRIPTS", NULL); case CTDB_CONTROL_DISABLE_MONITOR: CHECK_CONTROL_DATA_SIZE(0); @@ -496,8 +496,7 @@ return ctdb_control_recd_ping(ctdb); case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS: - CHECK_CONTROL_DATA_SIZE(sizeof(uint32_t)); - return ctdb_control_get_event_script_status(ctdb, *(uint32_t *)indata.dptr, outdata); + return control_not_implemented("GET_EVENT_SCRIPT_STATUS", NULL); case CTDB_CONTROL_RECD_RECLOCK_LATENCY: CHECK_CONTROL_DATA_SIZE(sizeof(double)); @@ -551,10 +550,10 @@ } case CTDB_CONTROL_ENABLE_SCRIPT: - return ctdb_control_enable_script(ctdb, indata); + return control_not_implemented("ENABLE_SCRIPT", NULL); case CTDB_CONTROL_DISABLE_SCRIPT: - return ctdb_control_disable_script(ctdb, indata); + return control_not_implemented("DISABLE_SCRIPT", NULL); case CTDB_CONTROL_SET_BAN_STATE: CHECK_CONTROL_DATA_SIZE(sizeof(struct ctdb_ban_state)); diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_daemon.c samba-4.6.5+dfsg/ctdb/server/ctdb_daemon.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_daemon.c 2016-10-24 19:37:30.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_daemon.c 2017-01-11 07:55:14.000000000 +0000 @@ -58,11 +58,11 @@ static void daemon_incoming_packet(void *, struct ctdb_req_header *); +static pid_t __ctdbd_pid; + static void print_exit_message(void) { - if (debug_extra != NULL && debug_extra[0] != '\0') { - DEBUG(DEBUG_NOTICE,("CTDB %s shutting down\n", debug_extra)); - } else { + if (getpid() == __ctdbd_pid) { DEBUG(DEBUG_NOTICE,("CTDB daemon shutting down\n")); /* Wait a second to allow pending log messages to be flushed */ @@ -1104,6 +1104,16 @@ static struct timeval tevent_before_wait_ts; static struct timeval tevent_after_wait_ts; +static void ctdb_tevent_trace_init(void) +{ + struct timeval now; + + now = timeval_current(); + + tevent_before_wait_ts = now; + tevent_after_wait_ts = now; +} + static void ctdb_tevent_trace(enum tevent_trace_point tp, void *private_data) { @@ -1120,25 +1130,21 @@ switch (tp) { case TEVENT_TRACE_BEFORE_WAIT: - if (!timeval_is_zero(&tevent_after_wait_ts)) { - diff = timeval_until(&tevent_after_wait_ts, &now); - if (diff.tv_sec > 3) { - DEBUG(DEBUG_ERR, - ("Handling event took %ld seconds!\n", - (long)diff.tv_sec)); - } + diff = timeval_until(&tevent_after_wait_ts, &now); + if (diff.tv_sec > 3) { + DEBUG(DEBUG_ERR, + ("Handling event took %ld seconds!\n", + (long)diff.tv_sec)); } tevent_before_wait_ts = now; break; case TEVENT_TRACE_AFTER_WAIT: - if (!timeval_is_zero(&tevent_before_wait_ts)) { - diff = timeval_until(&tevent_before_wait_ts, &now); - if (diff.tv_sec > 3) { - DEBUG(DEBUG_CRIT, - ("No event for %ld seconds!\n", - (long)diff.tv_sec)); - } + diff = timeval_until(&tevent_before_wait_ts, &now); + if (diff.tv_sec > 3) { + DEBUG(DEBUG_ERR, + ("No event for %ld seconds!\n", + (long)diff.tv_sec)); } tevent_after_wait_ts = now; break; @@ -1259,6 +1265,7 @@ * This must be the first exit handler to run (so the last to * be registered. */ + __ctdbd_pid = getpid(); atexit(print_exit_message); if (ctdb->do_setsched) { @@ -1275,12 +1282,8 @@ exit(1); } tevent_loop_allow_nesting(ctdb->ev); + ctdb_tevent_trace_init(); tevent_set_trace_callback(ctdb->ev, ctdb_tevent_trace, ctdb); - ret = ctdb_init_tevent_logging(ctdb); - if (ret != 0) { - DEBUG(DEBUG_ALERT,("Failed to initialize TEVENT logging\n")); - exit(1); - } /* set up a handler to pick up sigchld */ if (ctdb_init_sigchld(ctdb) == NULL) { @@ -1288,7 +1291,9 @@ exit(1); } - ctdb_set_child_logging(ctdb); + if (do_fork) { + ctdb_set_child_logging(ctdb); + } TALLOC_FREE(ctdb->srv); if (srvid_init(ctdb, &ctdb->srv) != 0) { @@ -1302,6 +1307,11 @@ /* force initial recovery for election */ ctdb->recovery_mode = CTDB_RECOVERY_ACTIVE; + if (ctdb_start_eventd(ctdb) != 0) { + DEBUG(DEBUG_ERR, ("Failed to start event daemon\n")); + exit(1); + } + ctdb_set_runstate(ctdb, CTDB_RUNSTATE_INIT); ret = ctdb_event_script(ctdb, CTDB_EVENT_INIT); if (ret != 0) { @@ -1834,6 +1844,7 @@ ctdb_stop_monitoring(ctdb); ctdb_release_all_ips(ctdb); ctdb_event_script(ctdb, CTDB_EVENT_SHUTDOWN); + ctdb_stop_eventd(ctdb); if (ctdb->methods != NULL && ctdb->methods->shutdown != NULL) { ctdb->methods->shutdown(ctdb); } @@ -1848,15 +1859,9 @@ * process must be created using ctdb_fork() and not fork() - * ctdb_fork() does some necessary housekeeping. */ -int switch_from_server_to_client(struct ctdb_context *ctdb, const char *fmt, ...) +int switch_from_server_to_client(struct ctdb_context *ctdb) { int ret; - va_list ap; - - /* Add extra information so we can identify this in the logs */ - va_start(ap, fmt); - debug_extra = talloc_strdup_append(talloc_vasprintf(NULL, fmt, ap), ":"); - va_end(ap); /* get a new event context */ ctdb->ev = tevent_context_init(ctdb); diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdbd.c samba-4.6.5+dfsg/ctdb/server/ctdbd.c --- samba-4.5.8+dfsg/ctdb/server/ctdbd.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdbd.c 2017-01-11 07:55:14.000000000 +0000 @@ -36,11 +36,12 @@ #include "common/reqid.h" #include "common/system.h" -#include "common/cmdline.h" #include "common/common.h" #include "common/logging.h" static struct { + const char *socket; + const char *debuglevel; const char *nlist; const char *transport; const char *myaddress; @@ -62,7 +63,10 @@ int script_log_level; int no_publicipcheck; int max_persistent_check_errors; + int torture; } options = { + .socket = CTDB_RUNDIR "/ctdbd.socket", + .debuglevel = "NOTICE", .nlist = NULL, .public_address_list = NULL, .transport = "tcp", @@ -116,7 +120,8 @@ struct poptOption popt_options[] = { POPT_AUTOHELP - POPT_CTDB_CMDLINE + { "socket", 0, POPT_ARG_STRING, &options.socket, 0, "local socket name", "filename" }, + { "debug", 'd', POPT_ARG_STRING, &options.debuglevel, 0, "debug level", NULL }, { "interactive", 'i', POPT_ARG_NONE, &interactive, 0, "don't fork", NULL }, { "public-addresses", 0, POPT_ARG_STRING, &options.public_address_list, 0, "public address list file", "filename" }, { "public-interface", 0, POPT_ARG_STRING, &options.public_interface, 0, "public interface", "interface"}, @@ -143,11 +148,11 @@ &options.max_persistent_check_errors, 0, "max allowed persistent check errors (default 0)", NULL }, { "sloppy-start", 0, POPT_ARG_NONE, &fast_start, 0, "Do not perform full recovery on start", NULL }, + { "torture", 0, POPT_ARG_NONE, &options.torture, 0, "enable nastiness in library", NULL }, POPT_TABLEEND }; int opt, ret; const char **extra_argv; - int extra_argc = 0; poptContext pc; struct tevent_context *ev; @@ -162,11 +167,14 @@ } } - /* setup the remaining options for the main program to use */ + /* If there are extra arguments then exit with usage message */ extra_argv = poptGetArgs(pc); if (extra_argv) { extra_argv++; - while (extra_argv[extra_argc]) extra_argc++; + if (extra_argv[0]) { + poptPrintHelp(pc, stdout, 0); + exit(1); + } } talloc_enable_null_tracking(); @@ -175,21 +183,44 @@ ev = tevent_context_init(NULL); if (ev == NULL) { - DEBUG(DEBUG_ALERT,("tevent_context_init() failed\n")); + fprintf(stderr, "tevent_context_init() failed\n"); exit(1); } tevent_loop_allow_nesting(ev); - ctdb = ctdb_cmdline_init(ev); + ctdb = ctdb_init(ev); + if (ctdb == NULL) { + fprintf(stderr, "Failed to init ctdb\n"); + exit(1); + } - ctdb->start_as_disabled = options.start_as_disabled; - ctdb->start_as_stopped = options.start_as_stopped; + if (options.torture == 1) { + ctdb_set_flags(ctdb, CTDB_FLAG_TORTURE); + } - script_log_level = options.script_log_level; + /* Log to stderr when running as interactive */ + if (interactive) { + options.logging = "file:"; + } - if (!ctdb_logging_init(ctdb, options.logging)) { + /* Initialize logging and set the debug level */ + if (!ctdb_logging_init(ctdb, options.logging, options.debuglevel)) { exit(1); } + setenv("CTDB_LOGGING", options.logging, 1); + setenv("CTDB_DEBUGLEVEL", debug_level_to_string(DEBUGLEVEL), 1); + + setenv("CTDB_SOCKET", options.socket, 1); + ret = ctdb_set_socketname(ctdb, options.socket); + if (ret == -1) { + DEBUG(DEBUG_ERR, ("ctdb_set_socketname() failed\n")); + exit(1); + } + + ctdb->start_as_disabled = options.start_as_disabled; + ctdb->start_as_stopped = options.start_as_stopped; + + script_log_level = options.script_log_level; DEBUG(DEBUG_NOTICE,("CTDB starting on node\n")); @@ -208,7 +239,7 @@ TALLOC_FREE(ctdb->idr); ret = reqid_init(ctdb, 0, &ctdb->idr);; if (ret != 0) { - DEBUG(DEBUG_ALERT, ("reqid_init failed (%s)\n", strerror(ret))); + DEBUG(DEBUG_ERR, ("reqid_init failed (%s)\n", strerror(ret))); exit(1); } @@ -216,7 +247,8 @@ ret = ctdb_set_transport(ctdb, options.transport); if (ret == -1) { - DEBUG(DEBUG_ALERT,("ctdb_set_transport failed - %s\n", ctdb_errstr(ctdb))); + DEBUG(DEBUG_ERR,("ctdb_set_transport failed - %s\n", + ctdb_errstr(ctdb))); exit(1); } @@ -224,7 +256,8 @@ if (options.myaddress) { ret = ctdb_set_address(ctdb, options.myaddress); if (ret == -1) { - DEBUG(DEBUG_ALERT,("ctdb_set_address failed - %s\n", ctdb_errstr(ctdb))); + DEBUG(DEBUG_ERR,("ctdb_set_address failed - %s\n", + ctdb_errstr(ctdb))); exit(1); } } @@ -258,7 +291,7 @@ ctdb->nodes_file = talloc_asprintf(ctdb, "%s/nodes", getenv("CTDB_BASE")); if (ctdb->nodes_file == NULL) { - DEBUG(DEBUG_ALERT,(__location__ " Out of memory\n")); + DEBUG(DEBUG_ERR,(__location__ " Out of memory\n")); exit(1); } } @@ -284,7 +317,7 @@ ctdb->event_script_dir = talloc_asprintf(ctdb, "%s/events.d", getenv("CTDB_BASE")); if (ctdb->event_script_dir == NULL) { - DEBUG(DEBUG_ALERT,(__location__ " Out of memory\n")); + DEBUG(DEBUG_ERR,(__location__ " Out of memory\n")); exit(1); } } @@ -292,7 +325,7 @@ if (options.notification_script != NULL) { ret = ctdb_set_notification_script(ctdb, options.notification_script); if (ret == -1) { - DEBUG(DEBUG_ALERT,("Unable to setup notification script\n")); + DEBUG(DEBUG_ERR,("Unable to setup notification script\n")); exit(1); } } diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_eventd.c samba-4.6.5+dfsg/ctdb/server/ctdb_eventd.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_eventd.c 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_eventd.c 2017-01-11 07:55:14.000000000 +0000 @@ -0,0 +1,1802 @@ +/* + Event script running daemon + + Copyright (C) Amitay Isaacs 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include "replace.h" +#include "system/filesys.h" +#include "system/dir.h" +#include "system/wait.h" +#include "system/locale.h" + +#include +#include +#include + +#include "lib/util/debug.h" +#include "lib/util/tevent_unix.h" +#include "lib/util/blocking.h" +#include "lib/util/sys_rw.h" +#include "lib/util/dlinklist.h" +#include "lib/async_req/async_sock.h" + +#include "protocol/protocol_api.h" + +#include "common/comm.h" +#include "common/logging.h" +#include "common/run_proc.h" +#include "common/sock_daemon.h" + +struct pending_event { + struct pending_event *prev, *next; + + struct tevent_req *req; +}; + +struct eventd_client { + struct eventd_client *prev, *next; + + struct sock_client_context *client_ctx; + struct pending_event *pending_list; +}; + +struct eventd_context { + const char *script_dir; + const char *debug_script; + struct run_proc_context *run_ctx; + struct tevent_queue *queue; + + /* current state */ + bool running; + enum ctdb_event event; + struct tevent_req *req; + + /* result of last execution */ + int result_run; + int result_fail; + struct ctdb_script_list *status_run[CTDB_EVENT_MAX]; + struct ctdb_script_list *status_pass[CTDB_EVENT_MAX]; + struct ctdb_script_list *status_fail[CTDB_EVENT_MAX]; + + struct eventd_client *client_list; +}; + +/* + * Global state manipulation functions + */ + +static int eventd_context_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *script_dir, + const char *debug_script, + struct eventd_context **result) +{ + struct eventd_context *ectx; + int ret; + + ectx = talloc_zero(mem_ctx, struct eventd_context); + if (ectx == NULL) { + return ENOMEM; + } + + ectx->script_dir = talloc_strdup(ectx, script_dir); + if (ectx->script_dir == NULL) { + talloc_free(ectx); + return ENOMEM; + } + + if (debug_script != NULL) { + ectx->debug_script = talloc_strdup(ectx, debug_script); + if (ectx->debug_script == NULL) { + talloc_free(ectx); + return ENOMEM; + } + } + + ret = run_proc_init(ectx, ev, &ectx->run_ctx); + if (ret != 0) { + talloc_free(ectx); + return ret; + } + + ectx->queue = tevent_queue_create(ectx, "run event queue"); + if (ectx->queue == NULL) { + talloc_free(ectx); + return ENOMEM; + } + + ectx->running = false; + ectx->event = CTDB_EVENT_INIT; + + *result = ectx; + return 0; +} + +static const char *eventd_script_dir(struct eventd_context *ectx) +{ + return ectx->script_dir; +} + +static const char *eventd_debug_script(struct eventd_context *ectx) +{ + return ectx->debug_script; +} + +static struct tevent_queue *eventd_queue(struct eventd_context *ectx) +{ + return ectx->queue; +} + +static void eventd_start_running(struct eventd_context *ectx, + enum ctdb_event event, + struct tevent_req *req) +{ + ectx->running = true; + ectx->event = event; + ectx->req = req; +} + +static void eventd_stop_running(struct eventd_context *ectx) +{ + ectx->running = false; + ectx->req = NULL; +} + +static void eventd_cancel_running(struct eventd_context *ectx) +{ + if (ectx->req != NULL) { + tevent_req_error(ectx->req, ECANCELED); + } + + eventd_stop_running(ectx); +} + +static bool eventd_is_running(struct eventd_context *ectx, + enum ctdb_event *event) +{ + if (event != NULL && ectx->running) { + *event = ectx->event; + } + + return ectx->running; +} + +static struct ctdb_script_list *script_list_copy(TALLOC_CTX *mem_ctx, + struct ctdb_script_list *s) +{ + struct ctdb_script_list *s2; + + s2 = talloc_zero(mem_ctx, struct ctdb_script_list); + if (s2 == NULL) { + return NULL; + } + + s2->num_scripts = s->num_scripts; + s2->script = talloc_memdup(s2, s->script, + s->num_scripts * sizeof(struct ctdb_script)); + if (s2->script == NULL) { + talloc_free(s2); + return NULL; + } + + return s2; +} + +static void eventd_set_result(struct eventd_context *ectx, + enum ctdb_event event, + struct ctdb_script_list *script_list, + int result) +{ + struct ctdb_script_list *s; + + /* Avoid negative values, they represent -errno */ + result = (result < 0) ? -result : result; + + ectx->result_run = result; + if (script_list == NULL) { + return; + } + + TALLOC_FREE(ectx->status_run[event]); + ectx->status_run[event] = talloc_steal(ectx, script_list); + + s = script_list_copy(ectx, script_list); + if (s == NULL) { + return; + } + + if (result == 0) { + TALLOC_FREE(ectx->status_pass[event]); + ectx->status_pass[event] = s; + } else { + TALLOC_FREE(ectx->status_fail[event]); + ectx->status_fail[event] = s; + ectx->result_fail = result; + } +} + +static int eventd_get_result(struct eventd_context *ectx, + enum ctdb_event event, + enum ctdb_event_status_state state, + struct ctdb_script_list **out) +{ + struct ctdb_script_list *s = NULL; + int result = 0; + + switch (state) { + case CTDB_EVENT_LAST_RUN: + s = ectx->status_run[event]; + result = ectx->result_run; + break; + + case CTDB_EVENT_LAST_PASS: + s = ectx->status_pass[event]; + result = 0; + break; + + case CTDB_EVENT_LAST_FAIL: + s = ectx->status_fail[event]; + result = ectx->result_fail; + break; + } + + *out = s; + return result; +} + +/* + * Run debug script to dianose hung scripts + */ + +static int debug_args(TALLOC_CTX *mem_ctx, const char *path, + enum ctdb_event event, pid_t pid, const char ***out) +{ + const char **argv; + + argv = talloc_array(mem_ctx, const char *, 4); + if (argv == NULL) { + return ENOMEM; + } + + argv[0] = path; + argv[1] = talloc_asprintf(argv, "%d", pid); + argv[2] = ctdb_event_to_string(event); + if (argv[1] == NULL) { + talloc_free(argv); + return ENOMEM; + } + argv[3] = NULL; + + *out = argv; + return 0; +} + +static void debug_log(int loglevel, char *output, const char *log_prefix) +{ + char *line; + + line = strtok(output, "\n"); + while (line != NULL) { + DEBUG(loglevel, ("%s: %s\n", log_prefix, line)); + line = strtok(NULL, "\n"); + } +} + +struct run_debug_state { + pid_t pid; +}; + +static void run_debug_done(struct tevent_req *subreq); + +static struct tevent_req *run_debug_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct eventd_context *ectx, + enum ctdb_event event, pid_t pid) +{ + struct tevent_req *req, *subreq; + struct run_debug_state *state; + const char **argv; + const char *debug_script; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct run_debug_state); + if (req == NULL) { + return NULL; + } + + state->pid = pid; + + debug_script = eventd_debug_script(ectx); + if (debug_script == NULL) { + tevent_req_done(req); + return tevent_req_post(req, ev); + } + + if (pid == -1) { + D_DEBUG("Script terminated, nothing to debug\n"); + tevent_req_done(req); + return tevent_req_post(req, ev); + } + + ret = debug_args(state, debug_script, event, pid, &argv); + if (ret != 0) { + D_ERR("debug_args() failed\n"); + tevent_req_error(req, ret); + return tevent_req_post(req, ev); + } + + D_DEBUG("Running debug %s with args \"%s %s\"\n", + debug_script, argv[1], argv[2]); + + subreq = run_proc_send(state, ev, ectx->run_ctx, debug_script, argv, + tevent_timeval_zero()); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, run_debug_done, req); + + talloc_free(argv); + return req; +} + +static void run_debug_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct run_debug_state *state = tevent_req_data( + req, struct run_debug_state); + char *output; + int ret; + bool status; + + status = run_proc_recv(subreq, &ret, NULL, NULL, state, &output); + TALLOC_FREE(subreq); + if (! status) { + D_ERR("Running debug failed, ret=%d\n", ret); + } + + /* Log output */ + if (output != NULL) { + debug_log(DEBUG_ERR, output, "event_debug"); + talloc_free(output); + } + + kill(-state->pid, SIGTERM); + tevent_req_done(req); +} + +static bool run_debug_recv(struct tevent_req *req, int *perr) +{ + int ret; + + if (tevent_req_is_unix_error(req, &ret)) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + return true; +} + +/* + * Utility functions for running a single event + */ + +static int script_filter(const struct dirent *de) +{ + size_t namelen = strlen(de->d_name); + char *ptr; + + /* Ignore . and .. */ + if (namelen < 3) { + return 0; + } + + /* Skip filenames with ~ */ + ptr = strchr(de->d_name, '~'); + if (ptr != NULL) { + return 0; + } + + /* Filename should start with [0-9][0-9]. */ + if ((! isdigit(de->d_name[0])) || + (! isdigit(de->d_name[1])) || + (de->d_name[2] != '.')) { + return 0; + } + + /* Ignore file names longer than MAX_SCRIPT_NAME */ + if (namelen > MAX_SCRIPT_NAME) { + return 0; + } + + return 1; +} + +static int get_script_list(TALLOC_CTX *mem_ctx, + const char *script_dir, + struct ctdb_script_list **out) +{ + struct dirent **namelist = NULL; + struct ctdb_script_list *script_list; + int count, ret; + int i; + + script_list = talloc_zero(mem_ctx, struct ctdb_script_list); + if (script_list == NULL) { + return ENOMEM; + } + + count = scandir(script_dir, &namelist, script_filter, alphasort); + if (count == -1) { + ret = errno; + if (ret == ENOENT) { + D_WARNING("event script dir %s removed\n", script_dir); + } else { + D_WARNING("scandir() failed on %s, ret=%d\n", + script_dir, ret); + } + *out = script_list; + ret = 0; + goto done; + } + + if (count == 0) { + *out = script_list; + ret = 0; + goto done; + } + + script_list->num_scripts = count; + script_list->script = talloc_zero_array(script_list, + struct ctdb_script, + count); + if (script_list->script == NULL) { + ret = ENOMEM; + talloc_free(script_list); + goto done; + } + + for (i=0; iscript[i]; + size_t len; + + len = strlcpy(s->name, namelist[i]->d_name, sizeof(s->name)); + if (len >= sizeof(s->name)) { + ret = EIO; + talloc_free(script_list); + goto done; + } + } + + *out = script_list; + ret = 0; + +done: + if (namelist != NULL && count != -1) { + for (i=0; id_name, script_name) == 0) { + + /* check for valid script names */ + ret = script_filter(de); + if (ret == 0) { + closedir(dirp); + return EINVAL; + } + + found = true; + break; + } + } + closedir(dirp); + + if (! found) { + return ENOENT; + } + + filename = talloc_asprintf(mem_ctx, "%s/%s", script_dir, script_name); + if (filename == NULL) { + return ENOMEM; + } + + ret = stat(filename, &st); + if (ret != 0) { + ret = errno; + goto done; + } + + if (enable) { + new_mode = st.st_mode | S_IXUSR; + } else { + new_mode = st.st_mode & ~(S_IXUSR | S_IXGRP | S_IXOTH); + } + + ret = chmod(filename, new_mode); + if (ret != 0) { + ret = errno; + goto done; + } + +done: + talloc_free(filename); + return ret; +} + +static int script_args(TALLOC_CTX *mem_ctx, enum ctdb_event event, + const char *arg_str, const char ***out) +{ + const char **argv; + int argc; + + argv = talloc_array(mem_ctx, const char *, 7); + if (argv == NULL) { + return ENOMEM; + } + + argv[0] = NULL; /* script name */ + argv[1] = ctdb_event_to_string(event); + argc = 2; + + if (arg_str != NULL) { + char *str, *t, *tok; + + str = talloc_strdup(argv, arg_str); + if (str == NULL) { + return ENOMEM; + } + + t = str; + while ((tok = strtok(t, " ")) != NULL) { + argv[argc] = talloc_strdup(argv, tok); + if (argv[argc] == NULL) { + talloc_free(argv); + return ENOMEM; + } + argc += 1; + if (argc >= 7) { + talloc_free(argv); + return EINVAL; + } + t = NULL; + } + + talloc_free(str); + } + + argv[argc] = NULL; + argc += 1; + + *out = argv; + return 0; +} + +/* + * Run a single event + */ + +struct run_event_state { + struct tevent_context *ev; + struct eventd_context *ectx; + struct timeval timeout; + enum ctdb_event event; + + struct ctdb_script_list *script_list; + const char **argv; + int index; + int status; +}; + +static struct tevent_req *run_event_run_script(struct tevent_req *req); +static void run_event_next_script(struct tevent_req *subreq); +static void run_event_debug(struct tevent_req *req, pid_t pid); +static void run_event_debug_done(struct tevent_req *subreq); + +static struct tevent_req *run_event_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct eventd_context *ectx, + enum ctdb_event event, + const char *arg_str, + uint32_t timeout) +{ + struct tevent_req *req, *subreq; + struct run_event_state *state; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct run_event_state); + if (req == NULL) { + return NULL; + } + + state->ev = ev; + state->ectx = ectx; + state->event = event; + + ret = get_script_list(state, eventd_script_dir(ectx), + &state->script_list); + if (ret != 0) { + D_ERR("get_script_list() failed, ret=%d\n", ret); + tevent_req_error(req, ret); + return tevent_req_post(req, ev); + } + + /* No scripts */ + if (state->script_list->num_scripts == 0) { + tevent_req_done(req); + return tevent_req_post(req, ev); + } + + ret = script_args(state, event, arg_str, &state->argv); + if (ret != 0) { + D_ERR("script_args() failed, ret=%d\n", ret); + tevent_req_error(req, ret); + return tevent_req_post(req, ev); + } + + if (timeout > 0) { + state->timeout = tevent_timeval_current_ofs(timeout, 0); + } + state->index = 0; + eventd_start_running(ectx, event, req); + + subreq = run_event_run_script(req); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, run_event_next_script, req); + + return req; +} + +static struct tevent_req *run_event_run_script(struct tevent_req *req) +{ + struct run_event_state *state = tevent_req_data( + req, struct run_event_state); + struct ctdb_script *script; + struct tevent_req *subreq; + char *path; + + script = &state->script_list->script[state->index]; + + path = talloc_asprintf(state, "%s/%s", + eventd_script_dir(state->ectx), script->name); + if (path == NULL) { + return NULL; + } + + state->argv[0] = script->name; + script->start = tevent_timeval_current(); + + D_DEBUG("Running %s with args \"%s %s\"\n", + path, state->argv[0], state->argv[1]); + + subreq = run_proc_send(state, state->ev, state->ectx->run_ctx, + path, state->argv, state->timeout); + + talloc_free(path); + + return subreq; +} + +static void run_event_next_script(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct run_event_state *state = tevent_req_data( + req, struct run_event_state); + struct ctdb_script *script; + char *output; + struct run_proc_result result; + pid_t pid; + int ret; + bool status; + + script = &state->script_list->script[state->index]; + script->finished = tevent_timeval_current(); + + status = run_proc_recv(subreq, &ret, &result, &pid, state, &output); + TALLOC_FREE(subreq); + if (! status) { + D_ERR("run_proc failed for %s, ret=%d\n", script->name, ret); + tevent_req_error(req, ret); + return; + } + + D_DEBUG("Script %s finished sig=%d, err=%d, status=%d\n", + script->name, result.sig, result.err, result.status); + + if (output != NULL) { + debug_log(DEBUG_ERR, output, script->name); + } + + if (result.sig > 0) { + script->status = -EINTR; + } else if (result.err > 0) { + if (result.err == EACCES) { + /* Map EACCESS to ENOEXEC */ + script->status = -ENOEXEC; + } else { + script->status = -result.err; + } + } else { + script->status = result.status; + } + + if (script->status != 0 && output != NULL) { + size_t n; + + n = strlcpy(script->output, output, MAX_SCRIPT_OUTPUT); + if (n >= MAX_SCRIPT_OUTPUT) { + script->output[MAX_SCRIPT_OUTPUT] = '\0'; + } + } + + /* If a script fails, stop running */ + if (script->status != 0 && script->status != -ENOEXEC) { + state->status = script->status; + eventd_stop_running(state->ectx); + state->script_list->num_scripts = state->index + 1; + eventd_set_result(state->ectx, state->event, + state->script_list, state->status); + + if (state->status == -ETIME && pid != -1) { + run_event_debug(req, pid); + } + + D_ERR("%s event %s\n", ctdb_event_to_string(state->event), + (state->status == -ETIME) ? "timed out" : "failed"); + + tevent_req_done(req); + return; + } + + state->index += 1; + + /* All scripts executed */ + if (state->index >= state->script_list->num_scripts) { + eventd_stop_running(state->ectx); + eventd_set_result(state->ectx, state->event, + state->script_list, state->status); + tevent_req_done(req); + return; + } + + subreq = run_event_run_script(req); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, run_event_next_script, req); +} + +static void run_event_debug(struct tevent_req *req, pid_t pid) +{ + struct run_event_state *state = tevent_req_data( + req, struct run_event_state); + struct tevent_req *subreq; + + /* Debug script is run with ectx as the memory context */ + subreq = run_debug_send(state->ectx, state->ev, state->ectx, + state->event, pid); + if (subreq == NULL) { + /* If run debug fails, it's not an error */ + D_NOTICE("Failed to run event debug\n"); + return; + } + tevent_req_set_callback(subreq, run_event_debug_done, NULL); +} + +static void run_event_debug_done(struct tevent_req *subreq) +{ + int ret = 0; + bool status; + + status = run_debug_recv(subreq, &ret); + TALLOC_FREE(subreq); + if (! status) { + D_NOTICE("run_debug() failed, ret=%d\n", ret); + } +} + +static bool run_event_recv(struct tevent_req *req, int *perr, int *status) +{ + struct run_event_state *state = tevent_req_data( + req, struct run_event_state); + int ret; + + if (tevent_req_is_unix_error(req, &ret)) { + if (ret == ECANCELED) { + if (status != NULL) { + *status = -ECANCELED; + } + return true; + } + + if (perr != NULL) { + *perr = ret; + } + return false; + } + + if (status != NULL) { + *status = state->status; + } + return true; +} + +/* + * Process RUN command + */ + +struct command_run_state { + struct tevent_context *ev; + struct eventd_context *ectx; + struct eventd_client *client; + + enum ctdb_event event; + uint32_t timeout; + const char *arg_str; + struct ctdb_event_reply *reply; +}; + +static void command_run_trigger(struct tevent_req *req, void *private_data); +static void command_run_done(struct tevent_req *subreq); + +static struct tevent_req *command_run_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct eventd_context *ectx, + struct eventd_client *client, + struct ctdb_event_request *request) +{ + struct tevent_req *req; + struct command_run_state *state; + struct pending_event *pending; + enum ctdb_event running_event; + bool running, status; + + req = tevent_req_create(mem_ctx, &state, struct command_run_state); + if (req == NULL) { + return NULL; + } + + state->ev = ev; + state->ectx = ectx; + state->client = client; + + state->event = request->rdata.data.run->event; + state->timeout = request->rdata.data.run->timeout; + state->arg_str = talloc_steal(state, request->rdata.data.run->arg_str); + + state->reply = talloc_zero(state, struct ctdb_event_reply); + if (tevent_req_nomem(state->reply, req)) { + return tevent_req_post(req, ev); + } + + state->reply->rdata.command = request->rdata.command; + + /* + * If monitor event is running, + * Cancel the running monitor event and run new event + * + * If any other event is running, + * If new event is monitor, cancel that event + * Else add new event to the queue + */ + + running = eventd_is_running(ectx, &running_event); + if (running) { + if (running_event == CTDB_EVENT_MONITOR) { + eventd_cancel_running(ectx); + } else if (state->event == CTDB_EVENT_MONITOR) { + state->reply->rdata.result = -ECANCELED; + tevent_req_done(req); + return tevent_req_post(req, ev); + } + } + + pending = talloc_zero(state, struct pending_event); + if (tevent_req_nomem(pending, req)) { + return tevent_req_post(req, ev); + } + + pending->req = req; + DLIST_ADD(client->pending_list, pending); + + status = tevent_queue_add(eventd_queue(ectx), ev, req, + command_run_trigger, pending); + if (! status) { + tevent_req_error(req, ENOMEM); + return tevent_req_post(req, ev); + } + + return req; +} + +static void command_run_trigger(struct tevent_req *req, void *private_data) +{ + struct pending_event *pending = talloc_get_type_abort( + private_data, struct pending_event); + struct command_run_state *state = tevent_req_data( + req, struct command_run_state); + struct tevent_req *subreq; + + DLIST_REMOVE(state->client->pending_list, pending); + + if (pending->req != req) { + tevent_req_error(req, EIO); + return; + } + + talloc_free(pending); + + D_DEBUG("Running event %s with args \"%s\"\n", + ctdb_event_to_string(state->event), state->arg_str); + + subreq = run_event_send(state, state->ev, state->ectx, + state->event, state->arg_str, state->timeout); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, command_run_done, req); +} + +static void command_run_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct command_run_state *state = tevent_req_data( + req, struct command_run_state); + int ret, result; + bool status; + + status = run_event_recv(subreq, &ret, &result); + if (! status) { + tevent_req_error(req, ret); + return; + } + + state->reply->rdata.result = result; + tevent_req_done(req); +} + +static bool command_run_recv(struct tevent_req *req, int *perr, + TALLOC_CTX *mem_ctx, + struct ctdb_event_reply **reply) +{ + struct command_run_state *state = tevent_req_data( + req, struct command_run_state); + int ret; + + if (tevent_req_is_unix_error(req, &ret)) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + if (reply != NULL) { + *reply = talloc_steal(mem_ctx, state->reply); + } + return true; +} + +/* + * Process STATUS command + */ + +struct command_status_state { + struct ctdb_event_reply *reply; +}; + +static struct tevent_req *command_status_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct eventd_context *ectx, + struct eventd_client *client, + struct ctdb_event_request *request) +{ + struct tevent_req *req; + struct command_status_state *state; + enum ctdb_event event; + enum ctdb_event_status_state estate; + + req = tevent_req_create(mem_ctx, &state, struct command_status_state); + if (req == NULL) { + return NULL; + } + + event = request->rdata.data.status->event; + estate = request->rdata.data.status->state; + + state->reply = talloc_zero(state, struct ctdb_event_reply); + if (tevent_req_nomem(state->reply, req)) { + return tevent_req_post(req, ev); + } + + state->reply->rdata.data.status = + talloc(state->reply, struct ctdb_event_reply_status); + if (tevent_req_nomem(state->reply->rdata.data.status, req)) { + return tevent_req_post(req, ev); + } + + state->reply->rdata.command = request->rdata.command; + state->reply->rdata.result = 0; + state->reply->rdata.data.status->status = + eventd_get_result(ectx, event, estate, + &state->reply->rdata.data.status->script_list); + + tevent_req_done(req); + return tevent_req_post(req, ev); +} + +static bool command_status_recv(struct tevent_req *req, int *perr, + TALLOC_CTX *mem_ctx, + struct ctdb_event_reply **reply) +{ + struct command_status_state *state = tevent_req_data( + req, struct command_status_state); + int ret; + + if (tevent_req_is_unix_error(req, &ret)) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + if (reply != NULL) { + *reply = talloc_steal(mem_ctx, state->reply); + } + return true; +} + +/* + * Process SCRIPT_LIST command + */ + +struct command_script_list_state { + struct ctdb_event_reply *reply; +}; + +static struct tevent_req *command_script_list_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct eventd_context *ectx, + struct eventd_client *client, + struct ctdb_event_request *request) +{ + struct tevent_req *req; + struct command_script_list_state *state; + struct ctdb_script_list *script_list; + int ret, i; + + req = tevent_req_create(mem_ctx, &state, + struct command_script_list_state); + if (req == NULL) { + return NULL; + } + + state->reply = talloc_zero(state, struct ctdb_event_reply); + if (tevent_req_nomem(state->reply, req)) { + return tevent_req_post(req, ev); + } + + state->reply->rdata.data.script_list = + talloc(state->reply, struct ctdb_event_reply_script_list); + if (tevent_req_nomem(state->reply->rdata.data.script_list, req)) { + return tevent_req_post(req, ev); + } + + state->reply->rdata.command = request->rdata.command; + + ret = get_script_list(state, eventd_script_dir(ectx), &script_list); + if (ret != 0) { + state->reply->rdata.result = -ret; + state->reply->rdata.data.script_list->script_list = NULL; + + tevent_req_done(req); + return tevent_req_post(req, ev); + } + + for (i=0; inum_scripts; i++) { + struct ctdb_script *script = &script_list->script[i]; + struct stat st; + char *path = NULL; + + path = talloc_asprintf(state, "%s/%s", + eventd_script_dir(ectx), script->name); + if (tevent_req_nomem(path, req)) { + continue; + } + + ret = stat(path, &st); + if (ret != 0) { + TALLOC_FREE(path); + continue; + } + + if (! (st.st_mode & S_IXUSR)) { + script->status = -ENOEXEC; + } + + TALLOC_FREE(path); + } + + state->reply->rdata.data.script_list->script_list = + talloc_steal(state->reply, script_list); + + tevent_req_done(req); + return tevent_req_post(req, ev); +} + +static bool command_script_list_recv(struct tevent_req *req, int *perr, + TALLOC_CTX *mem_ctx, + struct ctdb_event_reply **reply) +{ + struct command_script_list_state *state = tevent_req_data( + req, struct command_script_list_state); + int ret; + + if (tevent_req_is_unix_error(req, &ret)) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + if (reply != NULL) { + *reply = talloc_steal(mem_ctx, state->reply); + } + return true; +} + +/* + * Process SCRIPT_ENABLE command + */ + +struct command_script_enable_state { + struct ctdb_event_reply *reply; +}; + +static struct tevent_req *command_script_enable_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct eventd_context *ectx, + struct eventd_client *client, + struct ctdb_event_request *request) +{ + struct tevent_req *req; + struct command_script_enable_state *state; + const char *script_name; + int ret; + + req = tevent_req_create(mem_ctx, &state, + struct command_script_enable_state); + if (req == NULL) { + return NULL; + } + + script_name = request->rdata.data.script_enable->script_name; + + state->reply = talloc_zero(state, struct ctdb_event_reply); + if (tevent_req_nomem(state->reply, req)) { + return tevent_req_post(req, ev); + } + + state->reply->rdata.command = request->rdata.command; + + ret = script_chmod(state, eventd_script_dir(ectx), script_name, true); + state->reply->rdata.result = -ret; + + tevent_req_done(req); + return tevent_req_post(req, ev); +} + +static bool command_script_enable_recv(struct tevent_req *req, int *perr, + TALLOC_CTX *mem_ctx, + struct ctdb_event_reply **reply) +{ + struct command_script_enable_state *state = tevent_req_data( + req, struct command_script_enable_state); + int ret; + + if (tevent_req_is_unix_error(req, &ret)) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + if (reply != NULL) { + *reply = talloc_steal(mem_ctx, state->reply); + } + return true; +} + +/* + * Process SCRIPT_DISABLE command + */ + +struct command_script_disable_state { + struct ctdb_event_reply *reply; +}; + +static struct tevent_req *command_script_disable_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct eventd_context *ectx, + struct eventd_client *client, + struct ctdb_event_request *request) +{ + struct tevent_req *req; + struct command_script_disable_state *state; + const char *script_name; + int ret; + + req = tevent_req_create(mem_ctx, &state, + struct command_script_disable_state); + if (req == NULL) { + return NULL; + } + + script_name = request->rdata.data.script_disable->script_name; + + state->reply = talloc_zero(state, struct ctdb_event_reply); + if (tevent_req_nomem(state->reply, req)) { + return tevent_req_post(req, ev); + } + + state->reply->rdata.command = request->rdata.command; + + ret = script_chmod(state, eventd_script_dir(ectx), script_name, false); + state->reply->rdata.result = -ret; + + tevent_req_done(req); + return tevent_req_post(req, ev); +} + +static bool command_script_disable_recv(struct tevent_req *req, int *perr, + TALLOC_CTX *mem_ctx, + struct ctdb_event_reply **reply) +{ + struct command_script_disable_state *state = tevent_req_data( + req, struct command_script_disable_state); + int ret; + + if (tevent_req_is_unix_error(req, &ret)) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + if (reply != NULL) { + *reply = talloc_steal(mem_ctx, state->reply); + } + return true; +} + +/* + * Process clients + */ + +static struct eventd_client *client_find(struct eventd_context *ectx, + struct sock_client_context *client_ctx) +{ + struct eventd_client *client; + + for (client = ectx->client_list; + client != NULL; + client = client->next) { + if (client->client_ctx == client_ctx) { + return client; + } + } + + return NULL; +} + +static bool client_connect(struct sock_client_context *client_ctx, + void *private_data) +{ + struct eventd_context *ectx = talloc_get_type_abort( + private_data, struct eventd_context); + struct eventd_client *client; + + client = talloc_zero(ectx, struct eventd_client); + if (client == NULL) { + return false; + } + + client->client_ctx = client_ctx; + + DLIST_ADD(ectx->client_list, client); + return true; +} + +static void client_disconnect(struct sock_client_context *client_ctx, + void *private_data) +{ + struct eventd_context *ectx = talloc_get_type_abort( + private_data, struct eventd_context); + struct eventd_client *client; + struct pending_event *pe; + + client = client_find(ectx, client_ctx); + if (client == NULL) { + return; + } + + /* Get rid of pending events */ + while ((pe = client->pending_list) != NULL) { + DLIST_REMOVE(client->pending_list, pe); + talloc_free(pe->req); + } +} + +struct client_process_state { + struct tevent_context *ev; + + struct eventd_client *client; + struct ctdb_event_request request; +}; + +static void client_run_done(struct tevent_req *subreq); +static void client_status_done(struct tevent_req *subreq); +static void client_script_list_done(struct tevent_req *subreq); +static void client_script_enable_done(struct tevent_req *subreq); +static void client_script_disable_done(struct tevent_req *subreq); +static void client_process_reply(struct tevent_req *req, + struct ctdb_event_reply *reply); +static void client_process_reply_done(struct tevent_req *subreq); + +static struct tevent_req *client_process_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sock_client_context *client_ctx, + uint8_t *buf, size_t buflen, + void *private_data) +{ + struct eventd_context *ectx = talloc_get_type_abort( + private_data, struct eventd_context); + struct tevent_req *req, *subreq; + struct client_process_state *state; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct client_process_state); + if (req == NULL) { + return NULL; + } + + state->ev = ev; + + state->client = client_find(ectx, client_ctx); + if (state->client == NULL) { + tevent_req_error(req, EIO); + return tevent_req_post(req, ev); + } + + ret = ctdb_event_request_pull(buf, buflen, state, &state->request); + if (ret != 0) { + tevent_req_error(req, EPROTO); + return tevent_req_post(req, ev); + } + + switch (state->request.rdata.command) { + case CTDB_EVENT_COMMAND_RUN: + subreq = command_run_send(state, ev, ectx, state->client, + &state->request); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, client_run_done, req); + break; + + case CTDB_EVENT_COMMAND_STATUS: + subreq = command_status_send(state, ev, ectx, state->client, + &state->request); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, client_status_done, req); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_LIST: + subreq = command_script_list_send(state, ev, ectx, + state->client, + &state->request); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, client_script_list_done, req); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_ENABLE: + subreq = command_script_enable_send(state, ev, ectx, + state->client, + &state->request); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, client_script_enable_done, + req); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_DISABLE: + subreq = command_script_disable_send(state, ev, ectx, + state->client, + &state->request); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, client_script_disable_done, + req); + break; + } + + return req; +} + +static void client_run_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct client_process_state *state = tevent_req_data( + req, struct client_process_state); + struct ctdb_event_reply *reply = NULL; + int ret = 0; + bool status; + + status = command_run_recv(subreq, &ret, state, &reply); + TALLOC_FREE(subreq); + if (! status) { + D_ERR("COMMAND_RUN failed\n"); + tevent_req_error(req, ret); + return; + } + + client_process_reply(req, reply); + talloc_free(reply); +} + +static void client_status_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct client_process_state *state = tevent_req_data( + req, struct client_process_state); + struct ctdb_event_reply *reply = NULL; + int ret = 0; + bool status; + + status = command_status_recv(subreq, &ret, state, &reply); + TALLOC_FREE(subreq); + if (! status) { + D_ERR("COMMAND_STATUS failed\n"); + tevent_req_error(req, ret); + return; + } + + client_process_reply(req, reply); + talloc_free(reply); +} + +static void client_script_list_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct client_process_state *state = tevent_req_data( + req, struct client_process_state); + struct ctdb_event_reply *reply = NULL; + int ret = 0; + bool status; + + status = command_script_list_recv(subreq, &ret, state, &reply); + TALLOC_FREE(subreq); + if (! status) { + D_ERR("COMMAND_SCRIPT_LIST failed\n"); + tevent_req_error(req, ret); + return; + } + + client_process_reply(req, reply); + talloc_free(reply); +} + +static void client_script_enable_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct client_process_state *state = tevent_req_data( + req, struct client_process_state); + struct ctdb_event_reply *reply = NULL; + int ret = 0; + bool status; + + status = command_script_enable_recv(subreq, &ret, state, &reply); + TALLOC_FREE(subreq); + if (! status) { + D_ERR("COMMAND_SCRIPT_ENABLE failed\n"); + tevent_req_error(req, ret); + return; + } + + client_process_reply(req, reply); + talloc_free(reply); +} + +static void client_script_disable_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct client_process_state *state = tevent_req_data( + req, struct client_process_state); + struct ctdb_event_reply *reply = NULL; + int ret = 0; + bool status; + + status = command_script_disable_recv(subreq, &ret, state, &reply); + TALLOC_FREE(subreq); + if (! status) { + D_ERR("COMMAND_SCRIPT_DISABLE failed\n"); + tevent_req_error(req, ret); + return; + } + + client_process_reply(req, reply); + talloc_free(reply); +} + +static void client_process_reply(struct tevent_req *req, + struct ctdb_event_reply *reply) +{ + struct client_process_state *state = tevent_req_data( + req, struct client_process_state); + struct tevent_req *subreq; + uint8_t *buf; + size_t buflen; + int ret; + + ctdb_event_header_fill(&reply->header, state->request.header.reqid); + + buflen = ctdb_event_reply_len(reply); + buf = talloc_zero_size(state, buflen); + if (tevent_req_nomem(buf, req)) { + return; + } + + ret = ctdb_event_reply_push(reply, buf, &buflen); + if (ret != 0) { + talloc_free(buf); + tevent_req_error(req, ret); + return; + } + + subreq = sock_socket_write_send(state, state->ev, + state->client->client_ctx, + buf, buflen); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, client_process_reply_done, req); +} + +static void client_process_reply_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + int ret; + bool status; + + status = sock_socket_write_recv(subreq, &ret); + TALLOC_FREE(subreq); + if (! status) { + D_ERR("Sending reply failed\n"); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static bool client_process_recv(struct tevent_req *req, int *perr) +{ + int ret; + + if (tevent_req_is_unix_error(req, &ret)) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + return true; +} + +/* + * Event daemon + */ + +static void eventd_shutdown(void *private_data) +{ + struct eventd_context *ectx = talloc_get_type_abort( + private_data, struct eventd_context); + struct eventd_client *client; + + while ((client = ectx->client_list) != NULL) { + DLIST_REMOVE(ectx->client_list, client); + talloc_free(client); + } +} + +static struct { + const char *debug_script; + const char *script_dir; + const char *logging; + const char *debug_level; + const char *pidfile; + const char *socket; + int pid; +} options = { + .debug_level = "ERR", +}; + +struct poptOption cmdline_options[] = { + POPT_AUTOHELP + { "debug_script", 'D', POPT_ARG_STRING, &options.debug_script, 0, + "debug script", "FILE" }, + { "pid", 'P', POPT_ARG_INT, &options.pid, 0, + "pid to wait for", "PID" }, + { "event_script_dir", 'e', POPT_ARG_STRING, &options.script_dir, 0, + "event script dir", "DIRECTORY" }, + { "logging", 'l', POPT_ARG_STRING, &options.logging, 0, + "logging specification" }, + { "debug", 'd', POPT_ARG_STRING, &options.debug_level, 0, + "debug level" }, + { "pidfile", 'p', POPT_ARG_STRING, &options.pidfile, 0, + "eventd pid file", "FILE" }, + { "socket", 's', POPT_ARG_STRING, &options.socket, 0, + "eventd socket path", "FILE" }, + POPT_TABLEEND +}; + +int main(int argc, const char **argv) +{ + poptContext pc; + TALLOC_CTX *mem_ctx; + struct tevent_context *ev; + struct eventd_context *ectx; + struct sock_daemon_context *sockd; + struct sock_daemon_funcs daemon_funcs; + struct sock_socket_funcs socket_funcs; + struct stat statbuf; + int opt, ret; + + /* Set default options */ + options.pid = -1; + + pc = poptGetContext(argv[0], argc, argv, cmdline_options, + POPT_CONTEXT_KEEP_FIRST); + while ((opt = poptGetNextOpt(pc)) != -1) { + fprintf(stderr, "Invalid options %s: %s\n", + poptBadOption(pc, 0), poptStrerror(opt)); + exit(1); + } + + if (options.socket == NULL) { + fprintf(stderr, "Please specify eventd socket (--socket)\n"); + exit(1); + } + + if (options.script_dir == NULL) { + fprintf(stderr, + "Please specify script dir (--event_script_dir)\n"); + exit(1); + } + + if (options.logging == NULL) { + fprintf(stderr, + "Please specify logging (--logging)\n"); + exit(1); + } + + ret = stat(options.script_dir, &statbuf); + if (ret != 0) { + ret = errno; + fprintf(stderr, "Error reading script_dir %s, ret=%d\n", + options.script_dir, ret); + exit(1); + } + if (! S_ISDIR(statbuf.st_mode)) { + fprintf(stderr, "script_dir %s is not a directory\n", + options.script_dir); + exit(1); + } + + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + exit(1); + } + + ev = tevent_context_init(mem_ctx); + if (ev == NULL) { + ret = 1; + goto fail; + } + + ret = eventd_context_init(mem_ctx, ev, options.script_dir, + options.debug_script, &ectx); + if (ret != 0) { + goto fail; + } + + daemon_funcs = (struct sock_daemon_funcs) { + .shutdown = eventd_shutdown, + }; + + ret = sock_daemon_setup(mem_ctx, "ctdb-eventd", options.logging, + options.debug_level, options.pidfile, + &daemon_funcs, ectx, &sockd); + if (ret != 0) { + goto fail; + } + + socket_funcs = (struct sock_socket_funcs) { + .connect = client_connect, + .disconnect = client_disconnect, + .read_send = client_process_send, + .read_recv = client_process_recv, + }; + + ret = sock_daemon_add_unix(sockd, options.socket, &socket_funcs, ectx); + if (ret != 0) { + goto fail; + } + + ret = sock_daemon_run(ev, sockd, options.pid); + if (ret == EINTR) { + ret = 0; + } + +fail: + talloc_free(mem_ctx); + (void)poptFreeContext(pc); + exit(ret); +} diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_event_helper.c samba-4.6.5+dfsg/ctdb/server/ctdb_event_helper.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_event_helper.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_event_helper.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,158 +0,0 @@ -/* - ctdb event script helper - - Copyright (C) Amitay Isaacs 2013 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . -*/ - -#include "replace.h" -#include "system/filesys.h" -#include "system/network.h" -#include "system/wait.h" - -#include - -#include "lib/util/blocking.h" - -#include "ctdb_private.h" - -#include "common/system.h" - -static char *progname = NULL; - - -/* CTDB sends SIGTERM, when process must die */ -static void sigterm(int sig) -{ - pid_t pid; - - /* all the child processes are running in the same process group */ - pid = getpgrp(); - if (pid == -1) { - kill(-getpid(), SIGKILL); - } else { - kill(-pid, SIGKILL); - } - _exit(0); -} - -static int check_executable(const char *path) -{ - struct stat st; - - if (stat(path, &st) != 0) { - fprintf(stderr, "Failed to access '%s' - %s\n", - path, strerror(errno)); - return errno; - } - - if (!(st.st_mode & S_IXUSR)) { - return ENOEXEC; - } - - return 0; -} - -static void usage(void) -{ - fprintf(stderr, "\n"); - fprintf(stderr, "Usage: %s []\n", - progname); -} - -int main(int argc, char *argv[]) -{ - int log_fd, write_fd; - pid_t pid; - int status, output, ret; - - progname = argv[0]; - - if (argc < 5) { - usage(); - exit(1); - } - - reset_scheduler(); - - log_fd = atoi(argv[1]); - write_fd = atoi(argv[2]); - - set_close_on_exec(write_fd); - - close(STDOUT_FILENO); - close(STDERR_FILENO); - dup2(log_fd, STDOUT_FILENO); - dup2(log_fd, STDERR_FILENO); - close(log_fd); - - if (setpgid(0, 0) != 0) { - fprintf(stderr, "Failed to create process group for event script - %s\n", - strerror(errno)); - exit(1); - } - - signal(SIGTERM, sigterm); - - pid = fork(); - if (pid < 0) { - int save_errno = errno; - fprintf(stderr, "Failed to fork - %s\n", strerror(errno)); - sys_write(write_fd, &save_errno, sizeof(save_errno)); - exit(1); - } - - if (pid == 0) { - ret = check_executable(argv[3]); - if (ret != 0) { - _exit(ret); - } - ret = execv(argv[3], &argv[3]); - if (ret != 0) { - int save_errno = errno; - fprintf(stderr, "Error executing '%s' - %s\n", - argv[3], strerror(save_errno)); - } - /* This should never happen */ - _exit(ENOEXEC); - } - - ret = waitpid(pid, &status, 0); - if (ret == -1) { - output = -errno; - fprintf(stderr, "waitpid() failed - %s\n", strerror(errno)); - sys_write(write_fd, &output, sizeof(output)); - exit(1); - } - if (WIFEXITED(status)) { - output = WEXITSTATUS(status); - /* Only errors should be returned as -ve values */ - if (output == ENOENT || output == ENOEXEC) { - output = -output; - } - sys_write(write_fd, &output, sizeof(output)); - exit(0); - } - if (WIFSIGNALED(status)) { - output = -EINTR; - fprintf(stderr, "Process terminated with signal - %d\n", - WTERMSIG(status)); - sys_write(write_fd, &output, sizeof(output)); - exit(0); - } - - fprintf(stderr, "waitpid() status=%d\n", status); - exit(1); -} diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_fork.c samba-4.6.5+dfsg/ctdb/server/ctdb_fork.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_fork.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_fork.c 2017-01-11 07:55:14.000000000 +0000 @@ -25,6 +25,7 @@ #include #include "lib/util/debug.h" +#include "lib/util/time.h" #include "ctdb_private.h" #include "ctdb_client.h" @@ -34,20 +35,6 @@ #include "common/common.h" #include "common/logging.h" -void ctdb_set_child_info(TALLOC_CTX *mem_ctx, const char *child_name_fmt, ...) -{ - if (child_name_fmt != NULL) { - va_list ap; - char *t; - - va_start(ap, child_name_fmt); - t = talloc_vasprintf(mem_ctx, child_name_fmt, ap); - debug_extra = talloc_asprintf(mem_ctx, "%s:", t); - talloc_free(t); - va_end(ap); - } -} - void ctdb_track_child(struct ctdb_context *ctdb, pid_t pid) { char *process; @@ -68,6 +55,10 @@ pid_t ctdb_fork(struct ctdb_context *ctdb) { pid_t pid; + struct timeval before; + double delta_t; + + before = timeval_current(); pid = fork(); if (pid == -1) { @@ -76,8 +67,6 @@ return -1; } if (pid == 0) { - ctdb_set_child_info(ctdb, NULL); - /* Close the Unix Domain socket and the TCP socket. * This ensures that none of the child processes will * look like the main daemon when it is not running. @@ -104,6 +93,57 @@ return 0; } + delta_t = timeval_elapsed(&before); + if (delta_t > 3.0) { + DEBUG(DEBUG_WARNING, ("fork() took %lf seconds\n", delta_t)); + } + + ctdb_track_child(ctdb, pid); + return pid; +} + +/* + * vfork + exec + */ +pid_t ctdb_vfork_exec(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb, + const char *helper, int helper_argc, + const char **helper_argv) +{ + pid_t pid; + struct timeval before; + double delta_t; + char **argv; + int i; + + argv = talloc_array(mem_ctx, char *, helper_argc + 1); + if (argv == NULL) { + DEBUG(DEBUG_ERR, ("Memory allocation error\n")); + return -1; + } + + argv[0] = discard_const(helper); + for (i=0; i 3.0) { + DEBUG(DEBUG_WARNING, ("vfork() took %lf seconds\n", delta_t)); + } + ctdb_track_child(ctdb, pid); return pid; } diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_lock.c samba-4.6.5+dfsg/ctdb/server/ctdb_lock.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_lock.c 2017-01-30 09:56:26.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_lock.c 2017-01-26 12:19:08.000000000 +0000 @@ -28,10 +28,10 @@ #include "lib/util/dlinklist.h" #include "lib/util/debug.h" #include "lib/util/samba_util.h" +#include "lib/util/sys_rw.h" #include "ctdb_private.h" -#include "common/system.h" #include "common/common.h" #include "common/logging.h" @@ -392,8 +392,10 @@ void *private_data) { static char debug_locks[PATH_MAX+1] = ""; + static struct timeval last_debug_time; struct lock_context *lock_ctx; struct ctdb_context *ctdb; + struct timeval now; pid_t pid; double elapsed_time; int new_timer; @@ -401,12 +403,6 @@ lock_ctx = talloc_get_type_abort(private_data, struct lock_context); ctdb = lock_ctx->ctdb; - /* If a node stopped/banned, don't spam the logs */ - if (ctdb->nodes[ctdb->pnn]->flags & NODE_FLAGS_INACTIVE) { - lock_ctx->ttimer = NULL; - return; - } - elapsed_time = timeval_elapsed(&lock_ctx->start_time); if (lock_ctx->ctdb_db) { DEBUG(DEBUG_WARNING, @@ -419,6 +415,19 @@ elapsed_time)); } + /* If a node stopped/banned, don't spam the logs */ + if (ctdb->nodes[ctdb->pnn]->flags & NODE_FLAGS_INACTIVE) { + goto skip_lock_debug; + } + + /* Restrict log debugging to once per second */ + now = timeval_current(); + if (last_debug_time.tv_sec == now.tv_sec) { + goto skip_lock_debug; + } + + last_debug_time.tv_sec = now.tv_sec; + if (ctdb_set_helper("lock debugging helper", debug_locks, sizeof(debug_locks), "CTDB_DEBUG_LOCKS", @@ -435,6 +444,8 @@ " Unable to setup lock debugging\n")); } +skip_lock_debug: + /* Back-off logging if lock is not obtained for a long time */ if (elapsed_time < 100.0) { new_timer = 10; @@ -650,9 +661,9 @@ return; } - if (!ctdb_vfork_with_logging(lock_ctx, ctdb, "lock_helper", - prog, argc, (const char **)args, - NULL, NULL, &lock_ctx->child)) { + lock_ctx->child = ctdb_vfork_exec(lock_ctx, ctdb, prog, argc, + (const char **)args); + if (lock_ctx->child == -1) { DEBUG(DEBUG_ERR, ("Failed to create a child in ctdb_lock_schedule\n")); close(lock_ctx->fd[0]); close(lock_ctx->fd[1]); diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_lock_helper.c samba-4.6.5+dfsg/ctdb/server/ctdb_lock_helper.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_lock_helper.c 2017-01-30 09:56:26.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_lock_helper.c 2017-01-26 12:19:08.000000000 +0000 @@ -24,10 +24,12 @@ #include #include +#include +#include "lib/util/sys_rw.h" #include "lib/util/tevent_unix.h" -#include "ctdb_private.h" +#include "protocol/protocol.h" #include "common/system.h" @@ -55,8 +57,7 @@ realtime = set_scheduler(); if (! realtime) { fprintf(stderr, - "%s: Unable to set real-time scheduler priority\n", - progname); + "locking: Unable to set real-time scheduler priority\n"); } } @@ -79,10 +80,8 @@ static void usage(void) { fprintf(stderr, "\n"); - fprintf(stderr, "Usage: %s RECORD \n", - progname); - fprintf(stderr, " %s DB \n", - progname); + fprintf(stderr, "Usage: %s RECORD \n", progname); + fprintf(stderr, " %s DB \n", progname); } static uint8_t *hex_decode_talloc(TALLOC_CTX *mem_ctx, @@ -121,16 +120,15 @@ state->tdb = tdb_open(dbpath, 0, tdb_flags, O_RDWR, 0600); if (state->tdb == NULL) { - fprintf(stderr, "%s: Error opening database %s\n", - progname, dbpath); + fprintf(stderr, "locking: Error opening database %s\n", dbpath); return 1; } set_priority(); if (tdb_chainlock(state->tdb, state->key) < 0) { - fprintf(stderr, "%s: Error getting record lock (%s)\n", - progname, tdb_errorstr(state->tdb)); + fprintf(stderr, "locking: Error getting record lock (%s)\n", + tdb_errorstr(state->tdb)); return 1; } @@ -150,16 +148,15 @@ state->tdb = tdb_open(dbpath, 0, tdb_flags, O_RDWR, 0600); if (state->tdb == NULL) { - fprintf(stderr, "%s: Error opening database %s\n", - progname, dbpath); + fprintf(stderr, "locking: Error opening database %s\n", dbpath); return 1; } set_priority(); if (tdb_lockall(state->tdb) < 0) { - fprintf(stderr, "%s: Error getting db lock (%s)\n", - progname, tdb_errorstr(state->tdb)); + fprintf(stderr, "locking: Error getting db lock (%s)\n", + tdb_errorstr(state->tdb)); return 1; } @@ -271,7 +268,7 @@ struct tevent_signal *se; struct tevent_req *req; struct lock_state state = { 0 }; - int write_fd, log_fd; + int write_fd; char result = 0; int ppid; const char *lock_type; @@ -281,21 +278,14 @@ progname = argv[0]; - if (argc < 5) { + if (argc < 4) { usage(); exit(1); } - log_fd = atoi(argv[1]); - close(STDOUT_FILENO); - close(STDERR_FILENO); - dup2(log_fd, STDOUT_FILENO); - dup2(log_fd, STDERR_FILENO); - close(log_fd); - - ppid = atoi(argv[2]); - write_fd = atoi(argv[3]); - lock_type = argv[4]; + ppid = atoi(argv[1]); + write_fd = atoi(argv[2]); + lock_type = argv[3]; ev = tevent_context_init(NULL); if (ev == NULL) { @@ -312,26 +302,27 @@ } if (strcmp(lock_type, "RECORD") == 0) { - if (argc != 8) { - fprintf(stderr, "%s: Invalid number of arguments (%d)\n", - progname, argc); + if (argc != 7) { + fprintf(stderr, + "locking: Invalid number of arguments (%d)\n", + argc); usage(); exit(1); } - result = lock_record(argv[5], argv[6], argv[7], &state); + result = lock_record(argv[4], argv[5], argv[6], &state); } else if (strcmp(lock_type, "DB") == 0) { - if (argc != 7) { + if (argc != 6) { fprintf(stderr, "locking: Invalid number of arguments (%d)\n", argc); usage(); exit(1); } - result = lock_db(argv[5], argv[6], &state); + result = lock_db(argv[4], argv[5], &state); } else { - fprintf(stderr, "%s: Invalid lock-type '%s'\n", progname, lock_type); + fprintf(stderr, "locking: Invalid lock-type '%s'\n", lock_type); usage(); exit(1); } diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_logging.c samba-4.6.5+dfsg/ctdb/server/ctdb_logging.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_logging.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_logging.c 2017-01-11 07:55:14.000000000 +0000 @@ -20,7 +20,6 @@ #include "replace.h" #include "system/filesys.h" #include "system/network.h" -#include "system/syslog.h" #include "system/time.h" #include @@ -29,22 +28,15 @@ #include "lib/util/dlinklist.h" #include "lib/util/debug.h" #include "lib/util/blocking.h" +#include "lib/util/sys_rw.h" +#include "lib/util/time.h" #include "ctdb_private.h" #include "ctdb_client.h" -#include "common/system.h" #include "common/common.h" #include "common/logging.h" -const char *debug_extra = ""; - -struct ctdb_log_backend { - struct ctdb_log_backend *prev, *next; - const char *prefix; - ctdb_log_setup_fn_t setup; -}; - struct ctdb_log_state { const char *prefix; int fd, pfd; @@ -52,62 +44,28 @@ uint16_t buf_used; void (*logfn)(const char *, uint16_t, void *); void *logfn_private; - struct ctdb_log_backend *backends; }; /* Used by ctdb_set_child_logging() */ static struct ctdb_log_state *log_state; -void ctdb_log_register_backend(const char *prefix, ctdb_log_setup_fn_t setup) -{ - struct ctdb_log_backend *b; - - b = talloc_zero(log_state, struct ctdb_log_backend); - if (b == NULL) { - printf("Failed to register backend \"%s\" - no memory\n", - prefix); - return; - } - - b->prefix = prefix; - b->setup = setup; - - DLIST_ADD_END(log_state->backends, b); -} - - /* Initialise logging */ -bool ctdb_logging_init(TALLOC_CTX *mem_ctx, const char *logging) +bool ctdb_logging_init(TALLOC_CTX *mem_ctx, const char *logging, + const char *debug_level) { - struct ctdb_log_backend *b; int ret; log_state = talloc_zero(mem_ctx, struct ctdb_log_state); if (log_state == NULL) { - printf("talloc_zero failed\n"); - abort(); + return false; } - ctdb_log_init_file(); - ctdb_log_init_syslog(); - - for (b = log_state->backends; b != NULL; b = b->next) { - size_t l = strlen(b->prefix); - /* Exact match with prefix or prefix followed by ':' */ - if (strncmp(b->prefix, logging, l) == 0 && - (logging[l] == '\0' || logging[l] == ':')) { - ret = b->setup(mem_ctx, logging, "ctdbd"); - if (ret == 0) { - return true; - } - printf("Log init for \"%s\" failed with \"%s\"\n", - logging, strerror(ret)); - return false; - } + ret = logging_init(mem_ctx, logging, debug_level, "ctdbd"); + if (ret != 0) { + return false; } - printf("Unable to find log backend for \"%s\"\n", logging); - return false; + return true; } /* Note that do_debug always uses the global log state. */ @@ -200,6 +158,8 @@ struct tevent_fd *fde; char **argv; int i; + struct timeval before; + double delta_t; log = talloc_zero(mem_ctx, struct ctdb_log_state); CTDB_NO_MEMORY_NULL(ctdb, log); @@ -230,6 +190,8 @@ argv[i+2] = discard_const(helper_argv[i]); } + before = timeval_current(); + *pid = vfork(); if (*pid == 0) { execv(helper, argv); @@ -243,6 +205,11 @@ goto free_log; } + delta_t = timeval_elapsed(&before); + if (delta_t > 3.0) { + DEBUG(DEBUG_WARNING, ("vfork() took %lf seconds\n", delta_t)); + } + ctdb_track_child(ctdb, *pid); log->pfd = p[0]; @@ -269,11 +236,6 @@ int old_stdout, old_stderr; struct tevent_fd *fde; - if (log_state->fd == STDOUT_FILENO) { - /* not needed for stdout logging */ - return 0; - } - /* setup a pipe to catch IO from subprocesses */ if (pipe(p) != 0) { DEBUG(DEBUG_ERR,(__location__ " Failed to setup for child logging pipe\n")); @@ -320,48 +282,3 @@ return 0; } - - -/* - * set up a log handler to catch logging from TEVENT - */ -static void ctdb_tevent_logging(void *private_data, - enum tevent_debug_level level, - const char *fmt, - va_list ap) PRINTF_ATTRIBUTE(3, 0); -static void ctdb_tevent_logging(void *private_data, - enum tevent_debug_level level, - const char *fmt, - va_list ap) -{ - enum debug_level lvl = DEBUG_CRIT; - - switch (level) { - case TEVENT_DEBUG_FATAL: - lvl = DEBUG_CRIT; - break; - case TEVENT_DEBUG_ERROR: - lvl = DEBUG_ERR; - break; - case TEVENT_DEBUG_WARNING: - lvl = DEBUG_WARNING; - break; - case TEVENT_DEBUG_TRACE: - lvl = DEBUG_DEBUG; - break; - } - - if (lvl <= DEBUGLEVEL) { - dbgtext_va(fmt, ap); - } -} - -int ctdb_init_tevent_logging(struct ctdb_context *ctdb) -{ - int ret; - - ret = tevent_set_debug(ctdb->ev, - ctdb_tevent_logging, - ctdb); - return ret; -} diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_logging_file.c samba-4.6.5+dfsg/ctdb/server/ctdb_logging_file.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_logging_file.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_logging_file.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,121 +0,0 @@ -/* - ctdb logging code - - Copyright (C) Andrew Tridgell 2008 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . -*/ - -#include "replace.h" -#include "system/time.h" -#include "system/filesys.h" -#include "system/network.h" - -#include - -#include "lib/util/debug.h" -#include "lib/util/time_basic.h" - -#include "ctdb_private.h" -#include "ctdb_client.h" - -#include "common/system.h" - -#define CTDB_LOG_FILE_PREFIX "file" - -struct file_state { - int fd; -}; - -/* - log file logging function - */ -static void ctdb_log_to_file(void *private_ptr, int dbglevel, const char *s) -{ - struct file_state *state = talloc_get_type( - private_ptr, struct file_state); - struct timeval tv; - struct timeval_buf tvbuf; - char *s2 = NULL; - int ret; - - GetTimeOfDay(&tv); - timeval_str_buf(&tv, false, true, &tvbuf); - - ret = asprintf(&s2, "%s [%s%5u]: %s\n", - tvbuf.buf, - debug_extra, (unsigned)getpid(), s); - if (ret == -1) { - const char *errstr = "asprintf failed\n"; - sys_write(state->fd, errstr, strlen(errstr)); - return; - } - if (s2) { - sys_write(state->fd, s2, strlen(s2)); - free(s2); - } -} - -static int file_state_destructor(struct file_state *state) -{ - close(state->fd); - state->fd = -1; - return 0; -} - -static int ctdb_log_setup_file(TALLOC_CTX *mem_ctx, - const char *logging, - const char *app_name) -{ - struct file_state *state; - const char *logfile; - size_t l; - - l = strlen(CTDB_LOG_FILE_PREFIX); - if (logging[l] != ':') { - return EINVAL; - } - logfile = &logging[0] + l + 1; - - state = talloc_zero(mem_ctx, struct file_state); - if (state == NULL) { - return ENOMEM; - } - - if (logfile == NULL || strcmp(logfile, "-") == 0) { - int ret; - - state->fd = 1; - /* also catch stderr of subcommands to stdout */ - ret = dup2(1, 2); - if (ret == -1) { - return errno; - } - } else { - state->fd = open(logfile, O_WRONLY|O_APPEND|O_CREAT, 0666); - if (state->fd == -1) { - return errno; - } - } - - talloc_set_destructor(state, file_state_destructor); - debug_set_callback(state, ctdb_log_to_file); - - return 0; -} - -void ctdb_log_init_file(void) -{ - ctdb_log_register_backend(CTDB_LOG_FILE_PREFIX, ctdb_log_setup_file); -} diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_logging_syslog.c samba-4.6.5+dfsg/ctdb/server/ctdb_logging_syslog.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_logging_syslog.c 2016-09-13 08:21:35.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_logging_syslog.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,328 +0,0 @@ -/* - ctdb logging code - syslog backend - - Copyright (C) Andrew Tridgell 2008 - Copyright (C) Martin Schwenke 2014 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . -*/ - -#include "replace.h" -#include "system/network.h" -#include "system/syslog.h" - -#include "lib/util/debug.h" -#include "lib/util/blocking.h" -#include "lib/util/time_basic.h" -#include "lib/util/samba_util.h" /* get_myname */ - -#include "ctdb_private.h" - -#include "common/logging.h" - -/* Linux and FreeBSD define this appropriately - try good old /dev/log - * for anything that doesn't... */ -#ifndef _PATH_LOG -#define _PATH_LOG "/dev/log" -#endif - -#define CTDB_LOG_SYSLOG_PREFIX "syslog" -#define CTDB_SYSLOG_FACILITY LOG_USER - -struct ctdb_syslog_sock_state { - int fd; - const char *app_name; - const char *hostname; - int (*format)(int dbglevel, struct ctdb_syslog_sock_state *state, - const char *str, char *buf, int bsize); -}; - -/**********************************************************************/ - -static int ctdb_debug_to_syslog_level(int dbglevel) -{ - int level; - - switch (dbglevel) { - case DEBUG_ERR: - level = LOG_ERR; - break; - case DEBUG_WARNING: - level = LOG_WARNING; - break; - case DEBUG_NOTICE: - level = LOG_NOTICE; - break; - case DEBUG_INFO: - level = LOG_INFO; - break; - default: - level = LOG_DEBUG; - break; - } - - return level; -} - -/**********************************************************************/ - -/* Format messages as per RFC3164. */ - -/* It appears that some syslog daemon implementations do not allow a - * hostname when messages are sent via a Unix domain socket, so omit - * it. Similarly, syslogd on FreeBSD does not understand the hostname - * part of the header, even when logging via UDP. Note that most - * implementations will log messages against "localhost" when logging - * via UDP. A timestamp could be sent but rsyslogd on Linux limits - * the timestamp logged to the precision that was received on - * /dev/log. It seems sane to send degenerate RFC3164 messages - * without a header at all, so that the daemon will generate high - * resolution timestamps if configured. */ -static int format_rfc3164(int dbglevel, struct ctdb_syslog_sock_state *state, - const char *str, char *buf, int bsize) -{ - int pri; - int len; - - pri = CTDB_SYSLOG_FACILITY | ctdb_debug_to_syslog_level(dbglevel); - len = snprintf(buf, bsize, "<%d>%s[%u]: %s%s", - pri, state->app_name, getpid(), debug_extra, str); - len = MIN(len, bsize - 1); - - return len; -} - -/* Format messages as per RFC5424 - * - * <165>1 2003-08-24T05:14:15.000003-07:00 192.0.2.1 - * myproc 8710 - - %% It's time to make the do-nuts. - */ -static int format_rfc5424(int dbglevel, struct ctdb_syslog_sock_state *state, - const char *str, char *buf, int bsize) -{ - int pri; - struct timeval tv; - struct timeval_buf tvbuf; - int len, s; - - /* Header */ - pri = CTDB_SYSLOG_FACILITY | ctdb_debug_to_syslog_level(dbglevel); - GetTimeOfDay(&tv); - len = snprintf(buf, bsize, - "<%d>1 %s %s %s %u - - ", - pri, timeval_str_buf(&tv, true, true, &tvbuf), - state->hostname, state->app_name, getpid()); - /* A truncated header is not useful... */ - if (len >= bsize) { - return -1; - } - - /* Message */ - s = snprintf(&buf[len], bsize - len, "%s %s", debug_extra, str); - len = MIN(len + s, bsize - 1); - - return len; -} - -/**********************************************************************/ - -/* Non-blocking logging */ - -static void ctdb_log_to_syslog_sock(void *private_ptr, - int dbglevel, const char *str) -{ - struct ctdb_syslog_sock_state *state = talloc_get_type( - private_ptr, struct ctdb_syslog_sock_state); - - /* RFC3164 says: The total length of the packet MUST be 1024 - bytes or less. */ - char buf[1024]; - int n; - - n = state->format(dbglevel, state, str, buf, sizeof(buf)); - if (n == -1) { - fprintf(stderr, "Failed to format syslog message %s\n", str); - return; - } - - /* Could extend this to count failures, which probably - * indicate dropped messages due to EAGAIN or EWOULDBLOCK */ - (void)send(state->fd, buf, n, 0); -} - -static int -ctdb_syslog_sock_state_destructor(struct ctdb_syslog_sock_state *state) -{ - if (state->fd != -1) { - close(state->fd); - state->fd = -1; - } - return 0; -} - -static struct ctdb_syslog_sock_state * -ctdb_log_setup_syslog_common(TALLOC_CTX *mem_ctx, - const char *app_name) -{ - struct ctdb_syslog_sock_state *state; - - state = talloc_zero(mem_ctx, struct ctdb_syslog_sock_state); - if (state == NULL) { - return NULL; - } - state->fd = -1; - state->app_name = app_name; - talloc_set_destructor(state, ctdb_syslog_sock_state_destructor); - - return state; -} - -static int ctdb_log_setup_syslog_un(TALLOC_CTX *mem_ctx, - const char *app_name) -{ - struct ctdb_syslog_sock_state *state; - struct sockaddr_un dest; - int ret; - - state = ctdb_log_setup_syslog_common(mem_ctx, app_name); - if (state == NULL) { - return ENOMEM; - } - - state->fd = socket(AF_UNIX, SOCK_DGRAM, 0); - if (state->fd == -1) { - int save_errno = errno; - talloc_free(state); - return save_errno; - } - - dest.sun_family = AF_UNIX; - strncpy(dest.sun_path, _PATH_LOG, sizeof(dest.sun_path)-1); - ret = connect(state->fd, - (struct sockaddr *)&dest, sizeof(dest)); - if (ret == -1) { - int save_errno = errno; - talloc_free(state); - return save_errno; - } - - ret = set_blocking(state->fd, false); - if (ret != 0) { - int save_errno = errno; - talloc_free(state); - return save_errno; - } - - state->hostname = NULL; /* Make this explicit */ - state->format = format_rfc3164; - - debug_set_callback(state, ctdb_log_to_syslog_sock); - - return 0; -} - -static int ctdb_log_setup_syslog_udp(TALLOC_CTX *mem_ctx, - const char *app_name, - bool rfc5424) -{ - struct ctdb_syslog_sock_state *state; - struct sockaddr_in dest; - int ret; - - state = ctdb_log_setup_syslog_common(mem_ctx, app_name); - if (state == NULL) { - return ENOMEM; - } - - state->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (state->fd == -1) { - int save_errno = errno; - talloc_free(state); - return save_errno; - } - - dest.sin_family = AF_INET; - dest.sin_port = htons(514); - dest.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - ret = connect(state->fd, - (struct sockaddr *)&dest, sizeof(dest)); - if (ret == -1) { - int save_errno = errno; - talloc_free(state); - return save_errno; - } - - state->hostname = get_myname(state); - if (state->hostname == NULL) { - /* Use a fallback instead of failing initialisation */ - state->hostname = "localhost"; - } - if (rfc5424) { - state->format = format_rfc5424; - } else { - state->format = format_rfc3164; - } - - debug_set_callback(state, ctdb_log_to_syslog_sock); - - return 0; -} - -/**********************************************************************/ - -static void ctdb_log_to_syslog(void *private_ptr, int dbglevel, const char *s) -{ - syslog(ctdb_debug_to_syslog_level(dbglevel), - "%s%s", debug_extra, s); -} - -static int ctdb_log_setup_syslog(TALLOC_CTX *mem_ctx, - const char *logging, - const char *app_name) -{ - size_t l = strlen(CTDB_LOG_SYSLOG_PREFIX); - - if (logging[l] != '\0') { - /* Handle non-blocking extensions here */ - const char *method; - - if (logging[l] != ':') { - return EINVAL; - } - method = &logging[0] + l + 1; - if (strcmp(method, "nonblocking") == 0) { - ctdb_log_setup_syslog_un(mem_ctx, app_name); - return 0; - } - if (strcmp(method, "udp") == 0) { - ctdb_log_setup_syslog_udp(mem_ctx, app_name, false); - return 0; - } - if (strcmp(method, "udp-rfc5424") == 0) { - ctdb_log_setup_syslog_udp(mem_ctx, app_name, true); - return 0; - } - - return EINVAL; - } - - debug_set_callback(NULL, ctdb_log_to_syslog); - return 0; -} - -void ctdb_log_init_syslog(void) -{ - ctdb_log_register_backend(CTDB_LOG_SYSLOG_PREFIX, - ctdb_log_setup_syslog); -} diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_ltdb_server.c samba-4.6.5+dfsg/ctdb/server/ctdb_ltdb_server.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_ltdb_server.c 2016-09-13 08:21:35.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_ltdb_server.c 2017-01-11 07:55:14.000000000 +0000 @@ -57,7 +57,8 @@ TDB_DATA data) { struct ctdb_context *ctdb = ctdb_db->ctdb; - TDB_DATA rec; + TDB_DATA rec[2]; + uint32_t hsize = sizeof(struct ctdb_ltdb_header); int ret; bool seqnum_suppressed = false; bool keep = false; @@ -66,14 +67,21 @@ uint32_t lmaster; if (ctdb->flags & CTDB_FLAG_TORTURE) { + TDB_DATA old; struct ctdb_ltdb_header *h2; - rec = tdb_fetch(ctdb_db->ltdb->tdb, key); - h2 = (struct ctdb_ltdb_header *)rec.dptr; - if (rec.dptr && rec.dsize >= sizeof(h2) && h2->rsn > header->rsn) { - DEBUG(DEBUG_CRIT,("RSN regression! %llu %llu\n", - (unsigned long long)h2->rsn, (unsigned long long)header->rsn)); + + old = tdb_fetch(ctdb_db->ltdb->tdb, key); + h2 = (struct ctdb_ltdb_header *)old.dptr; + if (old.dptr != NULL && + old.dsize >= hsize && + h2->rsn > header->rsn) { + DEBUG(DEBUG_ERR, + ("RSN regression! %"PRIu64" %"PRIu64"\n", + h2->rsn, header->rsn)); + } + if (old.dptr) { + free(old.dptr); } - if (rec.dptr) free(rec.dptr); } if (ctdb->vnn_map == NULL) { @@ -178,12 +186,11 @@ */ header->flags &= ~CTDB_REC_FLAG_AUTOMATIC; - rec.dsize = sizeof(*header) + data.dsize; - rec.dptr = talloc_size(ctdb, rec.dsize); - CTDB_NO_MEMORY(ctdb, rec.dptr); + rec[0].dsize = hsize; + rec[0].dptr = (uint8_t *)header; - memcpy(rec.dptr, header, sizeof(*header)); - memcpy(rec.dptr + sizeof(*header), data.dptr, data.dsize); + rec[1].dsize = data.dsize; + rec[1].dptr = data.dptr; /* Databases with seqnum updates enabled only get their seqnum changes when/if we modify the data */ @@ -191,14 +198,14 @@ TDB_DATA old; old = tdb_fetch(ctdb_db->ltdb->tdb, key); - if ( (old.dsize == rec.dsize) - && !memcmp(old.dptr+sizeof(struct ctdb_ltdb_header), - rec.dptr+sizeof(struct ctdb_ltdb_header), - rec.dsize-sizeof(struct ctdb_ltdb_header)) ) { + if ((old.dsize == hsize + data.dsize) && + memcmp(old.dptr + hsize, data.dptr, data.dsize) == 0) { tdb_remove_flags(ctdb_db->ltdb->tdb, TDB_SEQNUM); seqnum_suppressed = true; } - if (old.dptr) free(old.dptr); + if (old.dptr != NULL) { + free(old.dptr); + } } DEBUG(DEBUG_DEBUG, (__location__ " db[%s]: %s record: hash[0x%08x]\n", @@ -207,7 +214,7 @@ ctdb_hash(&key))); if (keep) { - ret = tdb_store(ctdb_db->ltdb->tdb, key, rec, TDB_REPLACE); + ret = tdb_storev(ctdb_db->ltdb->tdb, key, rec, 2, TDB_REPLACE); } else { ret = tdb_delete(ctdb_db->ltdb->tdb, key); } @@ -234,8 +241,6 @@ tdb_add_flags(ctdb_db->ltdb->tdb, TDB_SEQNUM); } - talloc_free(rec.dptr); - if (schedule_for_deletion) { int ret2; ret2 = ctdb_local_schedule_for_deletion(ctdb_db, header, key); diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_monitor.c samba-4.6.5+dfsg/ctdb/server/ctdb_monitor.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_monitor.c 2016-09-13 08:21:35.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_monitor.c 2017-01-11 07:55:14.000000000 +0000 @@ -106,7 +106,6 @@ int ret; prctl_set_comment("ctdb_notification"); - debug_extra = talloc_asprintf(NULL, "notification-%s:", event); ret = ctdb_run_notification_script_child(ctdb, event); if (ret != 0) { DEBUG(DEBUG_ERR,(__location__ " Notification script failed\n")); @@ -529,11 +528,6 @@ ctdb_daemon_send_message(ctdb, ctdb->pnn, CTDB_SRVID_SET_NODE_FLAGS, indata); - /* if we have become banned, we should go into recovery mode */ - if ((node->flags & NODE_FLAGS_BANNED) && !(c->old_flags & NODE_FLAGS_BANNED) && (node->pnn == ctdb->pnn)) { - ctdb_local_node_got_banned(ctdb); - } - return 0; } diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_mutex_fcntl_helper.c samba-4.6.5+dfsg/ctdb/server/ctdb_mutex_fcntl_helper.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_mutex_fcntl_helper.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_mutex_fcntl_helper.c 2017-01-11 07:55:14.000000000 +0000 @@ -21,6 +21,8 @@ #include "system/filesys.h" #include "system/network.h" +#include "lib/util/sys_rw.h" + /* protocol.h is just needed for ctdb_sock_addr, which is used in system.h */ #include "protocol/protocol.h" #include "common/system.h" diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_recoverd.c samba-4.6.5+dfsg/ctdb/server/ctdb_recoverd.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_recoverd.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_recoverd.c 2017-01-11 07:55:14.000000000 +0000 @@ -32,13 +32,13 @@ #include "lib/util/dlinklist.h" #include "lib/util/debug.h" #include "lib/util/samba_util.h" +#include "lib/util/sys_rw.h" #include "lib/util/util_process.h" #include "ctdb_private.h" #include "ctdb_client.h" #include "common/system.h" -#include "common/cmdline.h" #include "common/common.h" #include "common/logging.h" @@ -385,21 +385,13 @@ return 0; } -static void set_recmode_fail_callback(struct ctdb_context *ctdb, uint32_t node_pnn, int32_t res, TDB_DATA outdata, void *callback_data) -{ - struct ctdb_recoverd *rec = talloc_get_type(callback_data, struct ctdb_recoverd); - - DEBUG(DEBUG_ERR,("Failed to freeze node %u during recovery. Set it as ban culprit for %d credits\n", node_pnn, rec->nodemap->num)); - ctdb_set_culprit_count(rec, node_pnn, rec->nodemap->num); -} - /* change recovery mode on all nodes */ static int set_recovery_mode(struct ctdb_context *ctdb, struct ctdb_recoverd *rec, struct ctdb_node_map_old *nodemap, - uint32_t rec_mode, bool freeze) + uint32_t rec_mode) { TDB_DATA data; uint32_t *nodes; @@ -424,25 +416,6 @@ return -1; } - /* freeze all nodes */ - if (freeze && rec_mode == CTDB_RECOVERY_ACTIVE) { - int i; - - for (i=1; i<=NUM_DB_PRIORITIES; i++) { - if (ctdb_client_async_control(ctdb, CTDB_CONTROL_FREEZE, - nodes, i, - CONTROL_TIMEOUT(), - false, tdb_null, - NULL, - set_recmode_fail_callback, - rec) != 0) { - DEBUG(DEBUG_ERR, (__location__ " Unable to freeze nodes. Recovery failed.\n")); - talloc_free(tmp_ctx); - return -1; - } - } - } - talloc_free(tmp_ctx); return 0; } @@ -1022,6 +995,153 @@ } } +struct helper_state { + int fd[2]; + pid_t pid; + int result; + bool done; +}; + +static void helper_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *private_data) +{ + struct helper_state *state = talloc_get_type_abort( + private_data, struct helper_state); + int ret; + + ret = sys_read(state->fd[0], &state->result, sizeof(state->result)); + if (ret != sizeof(state->result)) { + state->result = EPIPE; + } + + state->done = true; +} + +static int helper_run(struct ctdb_recoverd *rec, TALLOC_CTX *mem_ctx, + const char *prog, const char *arg, const char *type) +{ + struct helper_state *state; + struct tevent_fd *fde; + const char **args; + int nargs, ret; + + state = talloc_zero(mem_ctx, struct helper_state); + if (state == NULL) { + DEBUG(DEBUG_ERR, (__location__ " memory error\n")); + return -1; + } + + state->pid = -1; + + ret = pipe(state->fd); + if (ret != 0) { + DEBUG(DEBUG_ERR, + ("Failed to create pipe for %s helper\n", type)); + goto fail; + } + + set_close_on_exec(state->fd[0]); + + nargs = 4; + args = talloc_array(state, const char *, nargs); + if (args == NULL) { + DEBUG(DEBUG_ERR, (__location__ " memory error\n")); + goto fail; + } + + args[0] = talloc_asprintf(args, "%d", state->fd[1]); + if (args[0] == NULL) { + DEBUG(DEBUG_ERR, (__location__ " memory error\n")); + goto fail; + } + args[1] = rec->ctdb->daemon.name; + args[2] = arg; + args[3] = NULL; + + if (args[2] == NULL) { + nargs = 3; + } + + state->pid = ctdb_vfork_exec(state, rec->ctdb, prog, nargs, args); + if (state->pid == -1) { + DEBUG(DEBUG_ERR, + ("Failed to create child for %s helper\n", type)); + goto fail; + } + + close(state->fd[1]); + state->fd[1] = -1; + + state->done = false; + + fde = tevent_add_fd(rec->ctdb->ev, rec->ctdb, state->fd[0], + TEVENT_FD_READ, helper_handler, state); + if (fde == NULL) { + goto fail; + } + tevent_fd_set_auto_close(fde); + + while (!state->done) { + tevent_loop_once(rec->ctdb->ev); + } + + close(state->fd[0]); + state->fd[0] = -1; + + if (state->result != 0) { + goto fail; + } + + ctdb_kill(rec->ctdb, state->pid, SIGKILL); + talloc_free(state); + return 0; + +fail: + if (state->fd[0] != -1) { + close(state->fd[0]); + } + if (state->fd[1] != -1) { + close(state->fd[1]); + } + if (state->pid != -1) { + ctdb_kill(rec->ctdb, state->pid, SIGKILL); + } + talloc_free(state); + return -1; +} + + +static int ctdb_takeover(struct ctdb_recoverd *rec, + uint32_t *force_rebalance_nodes) +{ + static char prog[PATH_MAX+1] = ""; + char *arg; + int i; + + if (!ctdb_set_helper("takeover_helper", prog, sizeof(prog), + "CTDB_TAKEOVER_HELPER", CTDB_HELPER_BINDIR, + "ctdb_takeover_helper")) { + ctdb_die(rec->ctdb, "Unable to set takeover helper\n"); + } + + arg = NULL; + for (i = 0; i < talloc_array_length(force_rebalance_nodes); i++) { + uint32_t pnn = force_rebalance_nodes[i]; + if (arg == NULL) { + arg = talloc_asprintf(rec, "%u", pnn); + } else { + arg = talloc_asprintf_append(arg, ",%u", pnn); + } + if (arg == NULL) { + DEBUG(DEBUG_ERR, (__location__ " memory error\n")); + return -1; + } + } + + return helper_run(rec, rec, prog, arg, "takeover"); +} + static bool do_takeover_run(struct ctdb_recoverd *rec, struct ctdb_node_map_old *nodemap) { @@ -1075,8 +1195,7 @@ } } - ret = ctdb_takeover_run(rec->ctdb, nodemap, - rec->force_rebalance_nodes); + ret = ctdb_takeover(rec, rec->force_rebalance_nodes); /* Reenable takeover runs and IP checks on other nodes */ dtr.timeout = 0; @@ -1111,37 +1230,10 @@ return ok; } -struct recovery_helper_state { - int fd[2]; - pid_t pid; - int result; - bool done; -}; - -static void ctdb_recovery_handler(struct tevent_context *ev, - struct tevent_fd *fde, - uint16_t flags, void *private_data) -{ - struct recovery_helper_state *state = talloc_get_type_abort( - private_data, struct recovery_helper_state); - int ret; - - ret = sys_read(state->fd[0], &state->result, sizeof(state->result)); - if (ret != sizeof(state->result)) { - state->result = EPIPE; - } - - state->done = true; -} - - static int db_recovery_parallel(struct ctdb_recoverd *rec, TALLOC_CTX *mem_ctx) { static char prog[PATH_MAX+1] = ""; - const char **args; - struct recovery_helper_state *state; - struct tevent_fd *fde; - int nargs, ret; + const char *arg; if (!ctdb_set_helper("recovery_helper", prog, sizeof(prog), "CTDB_RECOVERY_HELPER", CTDB_HELPER_BINDIR, @@ -1149,88 +1241,15 @@ ctdb_die(rec->ctdb, "Unable to set recovery helper\n"); } - state = talloc_zero(mem_ctx, struct recovery_helper_state); - if (state == NULL) { + arg = talloc_asprintf(mem_ctx, "%u", new_generation()); + if (arg == NULL) { DEBUG(DEBUG_ERR, (__location__ " memory error\n")); return -1; } - state->pid = -1; - - ret = pipe(state->fd); - if (ret != 0) { - DEBUG(DEBUG_ERR, - ("Failed to create pipe for recovery helper\n")); - goto fail; - } - - set_close_on_exec(state->fd[0]); - - nargs = 4; - args = talloc_array(state, const char *, nargs); - if (args == NULL) { - DEBUG(DEBUG_ERR, (__location__ " memory error\n")); - goto fail; - } - - args[0] = talloc_asprintf(args, "%d", state->fd[1]); - args[1] = rec->ctdb->daemon.name; - args[2] = talloc_asprintf(args, "%u", new_generation()); - args[3] = NULL; - - if (args[0] == NULL || args[2] == NULL) { - DEBUG(DEBUG_ERR, (__location__ " memory error\n")); - goto fail; - } - setenv("CTDB_DBDIR_STATE", rec->ctdb->db_directory_state, 1); - if (!ctdb_vfork_with_logging(state, rec->ctdb, "recovery", prog, nargs, - args, NULL, NULL, &state->pid)) { - DEBUG(DEBUG_ERR, - ("Failed to create child for recovery helper\n")); - goto fail; - } - - close(state->fd[1]); - state->fd[1] = -1; - - state->done = false; - - fde = tevent_add_fd(rec->ctdb->ev, rec->ctdb, state->fd[0], - TEVENT_FD_READ, ctdb_recovery_handler, state); - if (fde == NULL) { - goto fail; - } - tevent_fd_set_auto_close(fde); - - while (!state->done) { - tevent_loop_once(rec->ctdb->ev); - } - - close(state->fd[0]); - state->fd[0] = -1; - - if (state->result != 0) { - goto fail; - } - - ctdb_kill(rec->ctdb, state->pid, SIGKILL); - talloc_free(state); - return 0; - -fail: - if (state->fd[0] != -1) { - close(state->fd[0]); - } - if (state->fd[1] != -1) { - close(state->fd[1]); - } - if (state->pid != -1) { - ctdb_kill(rec->ctdb, state->pid, SIGKILL); - } - talloc_free(state); - return -1; + return helper_run(rec, mem_ctx, prog, arg, "recovery"); } /* @@ -1901,7 +1920,7 @@ DEBUG(DEBUG_INFO,(__location__ " Force an election\n")); /* set all nodes to recovery mode to stop all internode traffic */ - ret = set_recovery_mode(ctdb, rec, nodemap, CTDB_RECOVERY_ACTIVE, false); + ret = set_recovery_mode(ctdb, rec, nodemap, CTDB_RECOVERY_ACTIVE); if (ret != 0) { DEBUG(DEBUG_ERR, (__location__ " Unable to set recovery mode to active on cluster\n")); return; @@ -2360,16 +2379,9 @@ } else { if (ctdb_sys_have_ip(&ips->ips[j].addr)) { DEBUG(DEBUG_ERR, - ("IP %s incorrectly on an interface - releasing\n", + ("IP %s incorrectly on an interface\n", ctdb_addr_to_str(&ips->ips[j].addr))); - ret = ctdb_ctrl_release_ip(ctdb, - CONTROL_TIMEOUT(), - CTDB_CURRENT_NODE, - &ips->ips[j]); - if (ret != 0) { - DEBUG(DEBUG_ERR, - ("Failed to release IP address\n")); - } + need_takeover_run = true; } } } @@ -2970,6 +2982,7 @@ struct ctdb_recoverd *rec = talloc_get_type_abort( private_data, struct ctdb_recoverd); + DEBUG(DEBUG_ERR, ("Received SIGTERM, exiting\n")); ctdb_recovery_unlock(rec); exit(0); } @@ -3144,6 +3157,7 @@ int fd[2]; struct tevent_signal *se; struct tevent_fd *fde; + int ret; if (pipe(fd) != 0) { return -1; @@ -3170,8 +3184,13 @@ srandom(getpid() ^ time(NULL)); + ret = logging_init(ctdb, NULL, NULL, "ctdb-recoverd"); + if (ret != 0) { + return -1; + } + prctl_set_comment("ctdb_recovered"); - if (switch_from_server_to_client(ctdb, "recoverd") != 0) { + if (switch_from_server_to_client(ctdb) != 0) { DEBUG(DEBUG_CRIT, (__location__ "ERROR: failed to switch recovery daemon into client mode. shutting down.\n")); exit(1); } diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_recovery_helper.c samba-4.6.5+dfsg/ctdb/server/ctdb_recovery_helper.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_recovery_helper.c 2016-12-05 08:18:44.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_recovery_helper.c 2017-01-11 07:55:14.000000000 +0000 @@ -27,6 +27,7 @@ #include #include "lib/tdb_wrap/tdb_wrap.h" +#include "lib/util/sys_rw.h" #include "lib/util/time.h" #include "lib/util/tevent_unix.h" @@ -34,41 +35,20 @@ #include "protocol/protocol_api.h" #include "client/client.h" +#include "common/logging.h" + static int recover_timeout = 30; #define NUM_RETRIES 3 #define TIMEOUT() timeval_current_ofs(recover_timeout, 0) -static void LOG(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2); - -static void LOG(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); -} +#define LOG(...) DEBUG(DEBUG_NOTICE, (__VA_ARGS__)) /* * Utility functions */ -static ssize_t sys_write(int fd, const void *buf, size_t count) -{ - ssize_t ret; - - do { - ret = write(fd, buf, count); -#if defined(EWOULDBLOCK) - } while (ret == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)); -#else - } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); -#endif - return ret; -} - static bool generic_recv(struct tevent_req *req, int *perr) { int err; @@ -1705,7 +1685,7 @@ return; } - if (state->persistent && state->tun_list->recover_pdb_by_seqnum != 0) { + if (state->persistent) { subreq = collect_highseqnum_db_send( state, state->ev, state->client, state->pnn_list, state->count, state->caps, @@ -1734,7 +1714,7 @@ int ret; bool status; - if (state->persistent && state->tun_list->recover_pdb_by_seqnum != 0) { + if (state->persistent) { status = collect_highseqnum_db_recv(subreq, &ret); } else { status = collect_all_db_recv(subreq, &ret); @@ -2733,7 +2713,7 @@ static void usage(const char *progname) { - fprintf(stderr, "\nUsage: %s \n", + fprintf(stderr, "\nUsage: %s \n", progname); } @@ -2743,7 +2723,7 @@ */ int main(int argc, char *argv[]) { - int log_fd, write_fd; + int write_fd; const char *sockpath; TALLOC_CTX *mem_ctx; struct tevent_context *ev; @@ -2752,27 +2732,24 @@ struct tevent_req *req; uint32_t generation; - if (argc != 5) { + if (argc != 4) { usage(argv[0]); exit(1); } - log_fd = atoi(argv[1]); - if (log_fd != STDOUT_FILENO && log_fd != STDERR_FILENO) { - close(STDOUT_FILENO); - close(STDERR_FILENO); - dup2(log_fd, STDOUT_FILENO); - dup2(log_fd, STDERR_FILENO); - } - close(log_fd); - - write_fd = atoi(argv[2]); - sockpath = argv[3]; - generation = (uint32_t)strtoul(argv[4], NULL, 0); + write_fd = atoi(argv[1]); + sockpath = argv[2]; + generation = (uint32_t)strtoul(argv[3], NULL, 0); mem_ctx = talloc_new(NULL); if (mem_ctx == NULL) { - LOG("talloc_new() failed\n"); + fprintf(stderr, "recovery: talloc_new() failed\n"); + goto failed; + } + + ret = logging_init(mem_ctx, NULL, NULL, "ctdb-recovery"); + if (ret != 0) { + fprintf(stderr, "recovery: Unable to initialize logging\n"); goto failed; } @@ -2810,6 +2787,6 @@ return 0; failed: - talloc_free(mem_ctx); + TALLOC_FREE(mem_ctx); return 1; } diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_takeover.c samba-4.6.5+dfsg/ctdb/server/ctdb_takeover.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_takeover.c 2016-10-24 19:37:30.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_takeover.c 2017-01-11 07:55:14.000000000 +0000 @@ -30,6 +30,7 @@ #include "lib/util/dlinklist.h" #include "lib/util/debug.h" #include "lib/util/samba_util.h" +#include "lib/util/sys_rw.h" #include "lib/util/util_process.h" #include "ctdb_private.h" @@ -55,12 +56,17 @@ uint32_t references; }; +struct vnn_interface { + struct vnn_interface *prev, *next; + struct ctdb_interface *iface; +}; + /* state associated with a public ip address */ struct ctdb_vnn { struct ctdb_vnn *prev, *next; struct ctdb_interface *iface; - const char **ifaces; + struct vnn_interface *ifaces; ctdb_sock_addr public_address; uint8_t public_netmask_bits; @@ -88,51 +94,62 @@ bool delete_pending; }; -static const char *ctdb_vnn_iface_string(const struct ctdb_vnn *vnn) +static const char *iface_string(const struct ctdb_interface *iface) { - if (vnn->iface) { - return vnn->iface->name; - } + return (iface != NULL ? iface->name : "__none__"); +} - return "__none__"; +static const char *ctdb_vnn_iface_string(const struct ctdb_vnn *vnn) +{ + return iface_string(vnn->iface); } -static int ctdb_add_local_iface(struct ctdb_context *ctdb, const char *iface) +static struct ctdb_interface *ctdb_find_iface(struct ctdb_context *ctdb, + const char *iface); + +static struct ctdb_interface * +ctdb_add_local_iface(struct ctdb_context *ctdb, const char *iface) { struct ctdb_interface *i; if (strlen(iface) > CTDB_IFACE_SIZE) { DEBUG(DEBUG_ERR, ("Interface name too long \"%s\"\n", iface)); - return -1; + return NULL; } /* Verify that we don't have an entry for this ip yet */ - for (i=ctdb->ifaces;i;i=i->next) { - if (strcmp(i->name, iface) == 0) { - return 0; - } + i = ctdb_find_iface(ctdb, iface); + if (i != NULL) { + return i; } /* create a new structure for this interface */ i = talloc_zero(ctdb, struct ctdb_interface); - CTDB_NO_MEMORY_FATAL(ctdb, i); + if (i == NULL) { + DEBUG(DEBUG_ERR, (__location__ " out of memory\n")); + return NULL; + } i->name = talloc_strdup(i, iface); - CTDB_NO_MEMORY(ctdb, i->name); + if (i->name == NULL) { + DEBUG(DEBUG_ERR, (__location__ " out of memory\n")); + talloc_free(i); + return NULL; + } i->link_up = true; DLIST_ADD(ctdb->ifaces, i); - return 0; + return i; } -static bool vnn_has_interface_with_name(struct ctdb_vnn *vnn, - const char *name) +static bool vnn_has_interface(struct ctdb_vnn *vnn, + const struct ctdb_interface *iface) { - int n; + struct vnn_interface *i; - for (n = 0; vnn->ifaces[n] != NULL; n++) { - if (strcmp(name, vnn->ifaces[n]) == 0) { + for (i = vnn->ifaces; i != NULL; i = i->next) { + if (iface == i->iface) { return true; } } @@ -163,14 +180,14 @@ next = i->next; /* Only consider interfaces named in the given VNN. */ - if (!vnn_has_interface_with_name(vnn, i->name)) { + if (!vnn_has_interface(vnn, i)) { continue; } /* Search for a vnn with this interface. */ found = false; for (tv=ctdb->vnn; tv; tv=tv->next) { - if (vnn_has_interface_with_name(tv, i->name)) { + if (vnn_has_interface(tv, i)) { found = true; break; } @@ -202,16 +219,13 @@ static struct ctdb_interface *ctdb_vnn_best_iface(struct ctdb_context *ctdb, struct ctdb_vnn *vnn) { - int i; + struct vnn_interface *i; struct ctdb_interface *cur = NULL; struct ctdb_interface *best = NULL; - for (i=0; vnn->ifaces[i]; i++) { + for (i = vnn->ifaces; i != NULL; i = i->next) { - cur = ctdb_find_iface(ctdb, vnn->ifaces[i]); - if (cur == NULL) { - continue; - } + cur = i->iface; if (!cur->link_up) { continue; @@ -284,7 +298,7 @@ static bool ctdb_vnn_available(struct ctdb_context *ctdb, struct ctdb_vnn *vnn) { - int i; + struct vnn_interface *i; /* Nodes that are not RUNNING can not host IPs */ if (ctdb->runstate != CTDB_RUNSTATE_RUNNING) { @@ -299,15 +313,8 @@ return true; } - for (i=0; vnn->ifaces[i]; i++) { - struct ctdb_interface *cur; - - cur = ctdb_find_iface(ctdb, vnn->ifaces[i]); - if (cur == NULL) { - continue; - } - - if (cur->link_up) { + for (i = vnn->ifaces; i != NULL; i = i->next) { + if (i->iface->link_up) { return true; } } @@ -575,10 +582,11 @@ if (status == -ETIME) { ctdb_ban_self(ctdb); } - DEBUG(DEBUG_ERR,(__location__ " Failed to move IP %s from interface %s to %s\n", - ctdb_addr_to_str(&state->vnn->public_address), - state->old->name, - ctdb_vnn_iface_string(state->vnn))); + DEBUG(DEBUG_ERR, + ("Failed update of IP %s from interface %s to %s\n", + ctdb_addr_to_str(&state->vnn->public_address), + iface_string(state->old), + ctdb_vnn_iface_string(state->vnn))); /* * All we can do is reset the old interface @@ -626,6 +634,7 @@ int ret; struct ctdb_do_updateip_state *state; struct ctdb_interface *old = vnn->iface; + const char *old_name = iface_string(old); const char *new_name; if (vnn->update_in_flight) { @@ -639,16 +648,15 @@ ctdb_vnn_unassign_iface(ctdb, vnn); ret = ctdb_vnn_assign_iface(ctdb, vnn); if (ret != 0) { - DEBUG(DEBUG_ERR,("update of IP %s/%u failed to " - "assin a usable interface (old iface '%s')\n", + DEBUG(DEBUG_ERR,("Update of IP %s/%u failed to " + "assign a usable interface (old iface '%s')\n", ctdb_addr_to_str(&vnn->public_address), vnn->public_netmask_bits, - old->name)); + old_name)); return -1; } - new_name = ctdb_vnn_iface_string(vnn); - if (old->name != NULL && new_name != NULL && !strcmp(old->name, new_name)) { + if (old == vnn->iface) { /* A benign update from one interface onto itself. * no need to run the eventscripts in this case, just return * success. @@ -667,11 +675,12 @@ vnn->update_in_flight = true; talloc_set_destructor(state, ctdb_updateip_destructor); + new_name = ctdb_vnn_iface_string(vnn); DEBUG(DEBUG_NOTICE,("Update of IP %s/%u from " "interface %s to %s\n", ctdb_addr_to_str(&vnn->public_address), vnn->public_netmask_bits, - old->name, + old_name, new_name)); ret = ctdb_event_script_callback(ctdb, @@ -680,14 +689,15 @@ state, CTDB_EVENT_UPDATE_IP, "%s %s %s %u", - state->old->name, + old_name, new_name, ctdb_addr_to_str(&vnn->public_address), vnn->public_netmask_bits); if (ret != 0) { - DEBUG(DEBUG_ERR,(__location__ " Failed update IP %s from interface %s to %s\n", - ctdb_addr_to_str(&vnn->public_address), - old->name, new_name)); + DEBUG(DEBUG_ERR, + ("Failed update IP %s from interface %s to %s\n", + ctdb_addr_to_str(&vnn->public_address), + old_name, new_name)); talloc_free(state); return -1; } @@ -759,19 +769,6 @@ return -1; } - if (vnn->iface == NULL && vnn->pnn == -1 && have_ip && best_iface != NULL) { - DEBUG(DEBUG_ERR,("Taking over newly created ip\n")); - have_ip = false; - } - - - if (vnn->iface == NULL && have_ip) { - DEBUG(DEBUG_CRIT,(__location__ " takeoverip of IP %s is known to the kernel, " - "but we have no interface assigned, has someone manually configured it? Ignore for now.\n", - ctdb_addr_to_str(&vnn->public_address))); - return 0; - } - if (vnn->pnn != ctdb->pnn && have_ip && vnn->pnn != -1) { DEBUG(DEBUG_CRIT,(__location__ " takeoverip of IP %s is known to the kernel, " "and we have it on iface[%s], but it was assigned to node %d" @@ -783,12 +780,16 @@ } if (vnn->pnn == -1 && have_ip) { - vnn->pnn = ctdb->pnn; - DEBUG(DEBUG_CRIT,(__location__ " takeoverip of IP %s is known to the kernel, " - "and we already have it on iface[%s], update local daemon\n", - ctdb_addr_to_str(&vnn->public_address), - ctdb_vnn_iface_string(vnn))); - return 0; + /* This will cause connections to be reset and + * reestablished. However, this is a very unusual + * situation and doing this will completely repair the + * inconsistency in the VNN. + */ + DEBUG(DEBUG_WARNING, + (__location__ + " Doing updateip for IP %s already on an interface\n", + ctdb_addr_to_str(&vnn->public_address))); + do_updateip = true; } if (vnn->iface) { @@ -932,7 +933,7 @@ struct release_ip_callback_state *state; struct ctdb_public_ip *pip = (struct ctdb_public_ip *)indata.dptr; struct ctdb_vnn *vnn; - char *iface; + const char *iface; /* update our vnn list */ vnn = find_public_ip_vnn(ctdb, &pip->addr); @@ -989,7 +990,7 @@ return -1; } - iface = strdup(ctdb_vnn_iface_string(vnn)); + iface = ctdb_vnn_iface_string(vnn); DEBUG(DEBUG_NOTICE,("Release of IP %s/%u on interface %s node:%d\n", ctdb_addr_to_str(&pip->addr), @@ -1001,7 +1002,6 @@ if (state == NULL) { ctdb_set_error(ctdb, "Out of memory at %s:%d", __FILE__, __LINE__); - free(iface); return -1; } @@ -1010,7 +1010,6 @@ if (state->addr == NULL) { ctdb_set_error(ctdb, "Out of memory at %s:%d", __FILE__, __LINE__); - free(iface); talloc_free(state); return -1; } @@ -1028,7 +1027,6 @@ iface, ctdb_addr_to_str(&pip->addr), vnn->public_netmask_bits); - free(iface); if (ret != 0) { DEBUG(DEBUG_ERR,(__location__ " Failed to release IP %s on interface %s\n", ctdb_addr_to_str(&pip->addr), @@ -1049,66 +1047,66 @@ bool check_address) { struct ctdb_vnn *vnn; - uint32_t num = 0; char *tmp; const char *iface; - int i; - int ret; - tmp = strdup(ifaces); - for (iface = strtok(tmp, ","); iface; iface = strtok(NULL, ",")) { - if (!ctdb_sys_check_iface_exists(iface)) { - DEBUG(DEBUG_CRIT,("Interface %s does not exist. Can not add public-address : %s\n", iface, ctdb_addr_to_str(addr))); - free(tmp); - return -1; - } - } - free(tmp); - - /* Verify that we don't have an entry for this ip yet */ - for (vnn=ctdb->vnn;vnn;vnn=vnn->next) { + /* Verify that we don't have an entry for this IP yet */ + for (vnn = ctdb->vnn; vnn != NULL; vnn = vnn->next) { if (ctdb_same_sockaddr(addr, &vnn->public_address)) { - DEBUG(DEBUG_CRIT,("Same ip '%s' specified multiple times in the public address list \n", - ctdb_addr_to_str(addr))); + DEBUG(DEBUG_ERR, + ("Duplicate public IP address '%s'\n", + ctdb_addr_to_str(addr))); return -1; - } + } } - /* create a new vnn structure for this ip address */ + /* Create a new VNN structure for this IP address */ vnn = talloc_zero(ctdb, struct ctdb_vnn); - CTDB_NO_MEMORY_FATAL(ctdb, vnn); - vnn->ifaces = talloc_array(vnn, const char *, num + 2); + if (vnn == NULL) { + DEBUG(DEBUG_ERR, (__location__ " out of memory\n")); + return -1; + } tmp = talloc_strdup(vnn, ifaces); - CTDB_NO_MEMORY_FATAL(ctdb, tmp); - for (iface = strtok(tmp, ","); iface; iface = strtok(NULL, ",")) { - vnn->ifaces = talloc_realloc(vnn, vnn->ifaces, const char *, num + 2); - CTDB_NO_MEMORY_FATAL(ctdb, vnn->ifaces); - vnn->ifaces[num] = talloc_strdup(vnn, iface); - CTDB_NO_MEMORY_FATAL(ctdb, vnn->ifaces[num]); - num++; + if (tmp == NULL) { + DEBUG(DEBUG_ERR, (__location__ " out of memory\n")); + talloc_free(vnn); + return -1; } - talloc_free(tmp); - vnn->ifaces[num] = NULL; - vnn->public_address = *addr; - vnn->public_netmask_bits = mask; - vnn->pnn = -1; - if (check_address) { - if (ctdb_sys_have_ip(addr)) { - DEBUG(DEBUG_ERR,("We are already hosting public address '%s'. setting PNN to ourself:%d\n", ctdb_addr_to_str(addr), ctdb->pnn)); - vnn->pnn = ctdb->pnn; + for (iface = strtok(tmp, ","); iface; iface = strtok(NULL, ",")) { + struct vnn_interface *vnn_iface; + struct ctdb_interface *i; + if (!ctdb_sys_check_iface_exists(iface)) { + DEBUG(DEBUG_ERR, + ("Unknown interface %s for public address %s\n", + iface, ctdb_addr_to_str(addr))); + talloc_free(vnn); + return -1; } - } - for (i=0; vnn->ifaces[i]; i++) { - ret = ctdb_add_local_iface(ctdb, vnn->ifaces[i]); - if (ret != 0) { - DEBUG(DEBUG_CRIT, (__location__ " failed to add iface[%s] " - "for public_address[%s]\n", - vnn->ifaces[i], ctdb_addr_to_str(addr))); + i = ctdb_add_local_iface(ctdb, iface); + if (i == NULL) { + DEBUG(DEBUG_ERR, + ("Failed to add interface '%s' " + "for public address %s\n", + iface, ctdb_addr_to_str(addr))); talloc_free(vnn); return -1; } + + vnn_iface = talloc_zero(vnn, struct vnn_interface); + if (vnn_iface == NULL) { + DEBUG(DEBUG_ERR, (__location__ " out of memory\n")); + talloc_free(vnn); + return -1; + } + + vnn_iface->iface = i; + DLIST_ADD_END(vnn->ifaces, vnn_iface); } + talloc_free(tmp); + vnn->public_address = *addr; + vnn->public_netmask_bits = mask; + vnn->pnn = -1; DLIST_ADD(ctdb->vnn, vnn); @@ -1182,584 +1180,6 @@ return 0; } -static struct ctdb_public_ip_list * -ctdb_fetch_remote_public_ips(struct ctdb_context *ctdb, - TALLOC_CTX *mem_ctx, - struct ctdb_node_map_old *nodemap, - uint32_t public_ip_flags) -{ - int j, ret; - struct ctdb_public_ip_list_old *ip_list; - struct ctdb_public_ip_list *public_ips; - - public_ips = talloc_zero_array(mem_ctx, - struct ctdb_public_ip_list, - nodemap->num); - if (public_ips == NULL) { - DEBUG(DEBUG_ERR, (__location__ " out of memory\n")); - return NULL; - } - - for (j = 0; j < nodemap->num; j++) { - if (nodemap->nodes[j].flags & NODE_FLAGS_INACTIVE) { - continue; - } - - /* Retrieve the list of public IPs from the - * node. Flags says whether it is known or - * available. */ - ret = ctdb_ctrl_get_public_ips_flags( - ctdb, TAKEOVER_TIMEOUT(), j, public_ips, - public_ip_flags, &ip_list); - if (ret != 0) { - DEBUG(DEBUG_ERR, - ("Failed to read public IPs from node: %u\n", j)); - talloc_free(public_ips); - return NULL; - } - public_ips[j].num = ip_list->num; - if (ip_list->num == 0) { - talloc_free(ip_list); - continue; - } - public_ips[j].ip = talloc_zero_array(public_ips, - struct ctdb_public_ip, - ip_list->num); - if (public_ips[j].ip == NULL) { - DEBUG(DEBUG_ERR, (__location__ " out of memory\n")); - talloc_free(public_ips); - return NULL; - } - memcpy(public_ips[j].ip, &ip_list->ips[0], - sizeof(struct ctdb_public_ip) * ip_list->num); - talloc_free(ip_list); - } - - return public_ips; -} - -struct get_tunable_callback_data { - const char *tunable; - uint32_t *out; - bool fatal; -}; - -static void get_tunable_callback(struct ctdb_context *ctdb, uint32_t pnn, - int32_t res, TDB_DATA outdata, - void *callback) -{ - struct get_tunable_callback_data *cd = - (struct get_tunable_callback_data *)callback; - int size; - - if (res != 0) { - /* Already handled in fail callback */ - return; - } - - if (outdata.dsize != sizeof(uint32_t)) { - DEBUG(DEBUG_ERR,("Wrong size of returned data when reading \"%s\" tunable from node %d. Expected %d bytes but received %d bytes\n", - cd->tunable, pnn, (int)sizeof(uint32_t), - (int)outdata.dsize)); - cd->fatal = true; - return; - } - - size = talloc_array_length(cd->out); - if (pnn >= size) { - DEBUG(DEBUG_ERR,("Got %s reply from node %d but nodemap only has %d entries\n", - cd->tunable, pnn, size)); - return; - } - - - cd->out[pnn] = *(uint32_t *)outdata.dptr; -} - -static void get_tunable_fail_callback(struct ctdb_context *ctdb, uint32_t pnn, - int32_t res, TDB_DATA outdata, - void *callback) -{ - struct get_tunable_callback_data *cd = - (struct get_tunable_callback_data *)callback; - - switch (res) { - case -ETIME: - DEBUG(DEBUG_ERR, - ("Timed out getting tunable \"%s\" from node %d\n", - cd->tunable, pnn)); - cd->fatal = true; - break; - case -EINVAL: - case -1: - DEBUG(DEBUG_WARNING, - ("Tunable \"%s\" not implemented on node %d\n", - cd->tunable, pnn)); - break; - default: - DEBUG(DEBUG_ERR, - ("Unexpected error getting tunable \"%s\" from node %d\n", - cd->tunable, pnn)); - cd->fatal = true; - } -} - -static uint32_t *get_tunable_from_nodes(struct ctdb_context *ctdb, - TALLOC_CTX *tmp_ctx, - struct ctdb_node_map_old *nodemap, - const char *tunable, - uint32_t default_value) -{ - TDB_DATA data; - struct ctdb_control_get_tunable *t; - uint32_t *nodes; - uint32_t *tvals; - struct get_tunable_callback_data callback_data; - int i; - - tvals = talloc_array(tmp_ctx, uint32_t, nodemap->num); - CTDB_NO_MEMORY_NULL(ctdb, tvals); - for (i=0; inum; i++) { - tvals[i] = default_value; - } - - callback_data.out = tvals; - callback_data.tunable = tunable; - callback_data.fatal = false; - - data.dsize = offsetof(struct ctdb_control_get_tunable, name) + strlen(tunable) + 1; - data.dptr = talloc_size(tmp_ctx, data.dsize); - t = (struct ctdb_control_get_tunable *)data.dptr; - t->length = strlen(tunable)+1; - memcpy(t->name, tunable, t->length); - nodes = list_of_connected_nodes(ctdb, nodemap, tmp_ctx, true); - if (ctdb_client_async_control(ctdb, CTDB_CONTROL_GET_TUNABLE, - nodes, 0, TAKEOVER_TIMEOUT(), - false, data, - get_tunable_callback, - get_tunable_fail_callback, - &callback_data) != 0) { - if (callback_data.fatal) { - talloc_free(tvals); - tvals = NULL; - } - } - talloc_free(nodes); - talloc_free(data.dptr); - - return tvals; -} - -static struct ctdb_node_map * -ctdb_node_map_old_to_new(TALLOC_CTX *mem_ctx, - const struct ctdb_node_map_old *old) -{ - struct ctdb_node_map *new; - - new = talloc(mem_ctx, struct ctdb_node_map); - if (new == NULL) { - DEBUG(DEBUG_ERR, (__location__ " out of memory\n")); - return NULL; - } - new->num = old->num; - new->node = talloc_zero_array(new, - struct ctdb_node_and_flags, new->num); - memcpy(new->node, &old->nodes[0], - sizeof(struct ctdb_node_and_flags) * new->num); - - return new; -} - - -static bool set_ipflags(struct ctdb_context *ctdb, - struct ipalloc_state *ipalloc_state, - struct ctdb_node_map_old *nodemap) -{ - uint32_t *tval_noiptakeover; - uint32_t *tval_noiphostonalldisabled; - struct ctdb_node_map *new; - - tval_noiptakeover = get_tunable_from_nodes(ctdb, ipalloc_state, nodemap, - "NoIPTakeover", 0); - if (tval_noiptakeover == NULL) { - return false; - } - - tval_noiphostonalldisabled = - get_tunable_from_nodes(ctdb, ipalloc_state, nodemap, - "NoIPHostOnAllDisabled", 0); - if (tval_noiphostonalldisabled == NULL) { - /* Caller frees tmp_ctx */ - return false; - } - - new = ctdb_node_map_old_to_new(ipalloc_state, nodemap); - if (new == NULL) { - return false; - } - - ipalloc_set_node_flags(ipalloc_state, new, - tval_noiptakeover, - tval_noiphostonalldisabled); - - talloc_free(tval_noiptakeover); - talloc_free(tval_noiphostonalldisabled); - talloc_free(new); - - return true; -} - -static enum ipalloc_algorithm -determine_algorithm(const struct ctdb_tunable_list *tunables) -{ - if (1 == tunables->lcp2_public_ip_assignment) { - return IPALLOC_LCP2; - } else if (1 == tunables->deterministic_public_ips) { - return IPALLOC_DETERMINISTIC; - } else { - return IPALLOC_NONDETERMINISTIC; - } -} - -struct takeover_callback_data { - uint32_t num_nodes; - unsigned int *fail_count; -}; - -static struct takeover_callback_data * -takeover_callback_data_init(TALLOC_CTX *mem_ctx, - uint32_t num_nodes) -{ - static struct takeover_callback_data *takeover_data; - - takeover_data = talloc_zero(mem_ctx, struct takeover_callback_data); - if (takeover_data == NULL) { - DEBUG(DEBUG_ERR, (__location__ " out of memory\n")); - return NULL; - } - - takeover_data->fail_count = talloc_zero_array(takeover_data, - unsigned int, num_nodes); - if (takeover_data->fail_count == NULL) { - DEBUG(DEBUG_ERR, (__location__ " out of memory\n")); - talloc_free(takeover_data); - return NULL; - } - - takeover_data->num_nodes = num_nodes; - - return takeover_data; -} - -static void takeover_run_fail_callback(struct ctdb_context *ctdb, - uint32_t node_pnn, int32_t res, - TDB_DATA outdata, void *callback_data) -{ - struct takeover_callback_data *cd = - talloc_get_type_abort(callback_data, - struct takeover_callback_data); - - if (node_pnn >= cd->num_nodes) { - DEBUG(DEBUG_ERR, (__location__ " invalid PNN %u\n", node_pnn)); - return; - } - - if (cd->fail_count[node_pnn] == 0) { - DEBUG(DEBUG_ERR, - ("Node %u failed the takeover run\n", node_pnn)); - } - - cd->fail_count[node_pnn]++; -} - -static void takeover_run_process_failures(struct ctdb_context *ctdb, - struct takeover_callback_data *tcd) -{ - unsigned int max_fails = 0; - uint32_t max_pnn = -1; - uint32_t i; - - for (i = 0; i < tcd->num_nodes; i++) { - if (tcd->fail_count[i] > max_fails) { - max_pnn = i; - max_fails = tcd->fail_count[i]; - } - } - - if (max_fails > 0) { - int ret; - TDB_DATA data; - - DEBUG(DEBUG_ERR, - ("Sending banning credits to %u with fail count %u\n", - max_pnn, max_fails)); - - data.dptr = (uint8_t *)&max_pnn; - data.dsize = sizeof(uint32_t); - ret = ctdb_client_send_message(ctdb, - CTDB_BROADCAST_CONNECTED, - CTDB_SRVID_BANNING, - data); - if (ret != 0) { - DEBUG(DEBUG_ERR, - ("Failed to set banning credits for node %u\n", - max_pnn)); - } - } -} - -/* - * Recalculate the allocation of public IPs to nodes and have the - * nodes host their allocated addresses. - * - * - Initialise IP allocation state. Pass: - + algorithm to be used; - + whether IP rebalancing ("failback") should be done (this uses a - cluster-wide configuration variable and only the value form the - master node is used); and - * + list of nodes to force rebalance (internal structure, currently - * no way to fetch, only used by LCP2 for nodes that have had new - * IP addresses added). - * - Set IP flags for IP allocation based on node map and tunables - * NoIPTakeover/NoIPHostOnAllDisabled from all connected nodes - * (tunable fetching done separately so values can be faked in unit - * testing) - * - Retrieve known and available IP addresses (done separately so - * values can be faked in unit testing) - * - Use ipalloc_set_public_ips() to set known and available IP - addresses for allocation - * - If cluster can't host IP addresses then early exit - * - Run IP allocation algorithm - * - Send RELEASE_IP to all nodes for IPs they should not host - * - Send TAKE_IP to all nodes for IPs they should host - * - Send IPREALLOCATED to all nodes (with backward compatibility hack) - */ -int ctdb_takeover_run(struct ctdb_context *ctdb, struct ctdb_node_map_old *nodemap, - uint32_t *force_rebalance_nodes) -{ - int i, ret; - struct ctdb_public_ip ip; - uint32_t *nodes; - struct public_ip_list *all_ips, *tmp_ip; - TDB_DATA data; - struct timeval timeout; - struct client_async_data *async_data; - struct ctdb_client_control_state *state; - TALLOC_CTX *tmp_ctx = talloc_new(ctdb); - struct ipalloc_state *ipalloc_state; - struct ctdb_public_ip_list *known_ips, *available_ips; - struct takeover_callback_data *takeover_data; - - /* Initialise fail callback data to be used with - * takeover_run_fail_callback(). A failure in any of the - * following steps will cause an early return, so this can be - * reused for each of those steps without re-initialising. */ - takeover_data = takeover_callback_data_init(tmp_ctx, - nodemap->num); - if (takeover_data == NULL) { - talloc_free(tmp_ctx); - return -1; - } - - /* Default timeout for early jump to IPREALLOCATED. See below - * for explanation of 3 times... */ - timeout = timeval_current_ofs(3 * ctdb->tunable.takeover_timeout, 0); - - /* - * ip failover is completely disabled, just send out the - * ipreallocated event. - */ - if (ctdb->tunable.disable_ip_failover != 0) { - goto ipreallocated; - } - - ipalloc_state = ipalloc_state_init(tmp_ctx, ctdb->num_nodes, - determine_algorithm(&ctdb->tunable), - (ctdb->tunable.no_ip_failback != 0), - force_rebalance_nodes); - if (ipalloc_state == NULL) { - talloc_free(tmp_ctx); - return -1; - } - - if (!set_ipflags(ctdb, ipalloc_state, nodemap)) { - DEBUG(DEBUG_ERR, - ("Failed to set IP flags - aborting takeover run\n")); - talloc_free(tmp_ctx); - return -1; - } - - /* Fetch known/available public IPs from each active node */ - /* Fetch lists of known public IPs from all nodes */ - known_ips = ctdb_fetch_remote_public_ips(ctdb, ipalloc_state, - nodemap, 0); - if (known_ips == NULL) { - DEBUG(DEBUG_ERR, ("Failed to read known public IPs\n")); - talloc_free(tmp_ctx); - return -1; - } - available_ips = ctdb_fetch_remote_public_ips( - ctdb, ipalloc_state, nodemap, - CTDB_PUBLIC_IP_FLAGS_ONLY_AVAILABLE); - if (available_ips == NULL) { - DEBUG(DEBUG_ERR, ("Failed to read available public IPs\n")); - talloc_free(tmp_ctx); - return -1; - } - - ipalloc_set_public_ips(ipalloc_state, known_ips, available_ips); - - if (! ipalloc_can_host_ips(ipalloc_state)) { - DEBUG(DEBUG_WARNING,("No nodes available to host public IPs yet\n")); - goto ipreallocated; - } - - /* Do the IP reassignment calculations */ - all_ips = ipalloc(ipalloc_state); - if (all_ips == NULL) { - talloc_free(tmp_ctx); - return -1; - } - - /* Now tell all nodes to release any public IPs should not - * host. This will be a NOOP on nodes that don't currently - * hold the given IP. - */ - async_data = talloc_zero(tmp_ctx, struct client_async_data); - CTDB_NO_MEMORY_FATAL(ctdb, async_data); - - async_data->fail_callback = takeover_run_fail_callback; - async_data->callback_data = takeover_data; - - ZERO_STRUCT(ip); /* Avoid valgrind warnings for union */ - - /* Each of the following stages (RELEASE_IP, TAKEOVER_IP, - * IPREALLOCATED) notionally has a timeout of TakeoverTimeout - * seconds. However, RELEASE_IP can take longer due to TCP - * connection killing, so sometimes needs more time. - * Therefore, use a cumulative timeout of TakeoverTimeout * 3 - * seconds across all 3 stages. No explicit expiry checks are - * needed before each stage because tevent is smart enough to - * fire the timeouts even if they are in the past. Initialise - * this here so it explicitly covers the stages we're - * interested in but, in particular, not the time taken by the - * ipalloc(). - */ - timeout = timeval_current_ofs(3 * ctdb->tunable.takeover_timeout, 0); - - /* Send a RELEASE_IP to all nodes that should not be hosting - * each IP. For each IP, all but one of these will be - * redundant. However, the redundant ones are used to tell - * nodes which node should be hosting the IP so that commands - * like "ctdb ip" can display a particular nodes idea of who - * is hosting what. */ - for (i=0;inum;i++) { - /* don't talk to unconnected nodes, but do talk to banned nodes */ - if (nodemap->nodes[i].flags & NODE_FLAGS_DISCONNECTED) { - continue; - } - - for (tmp_ip=all_ips;tmp_ip;tmp_ip=tmp_ip->next) { - if (tmp_ip->pnn == nodemap->nodes[i].pnn) { - /* This node should be serving this - vnn so don't tell it to release the ip - */ - continue; - } - ip.pnn = tmp_ip->pnn; - ip.addr = tmp_ip->addr; - - data.dsize = sizeof(ip); - data.dptr = (uint8_t *)&ip; - state = ctdb_control_send(ctdb, nodemap->nodes[i].pnn, - 0, CTDB_CONTROL_RELEASE_IP, 0, - data, async_data, - &timeout, NULL); - if (state == NULL) { - DEBUG(DEBUG_ERR,(__location__ " Failed to call async control CTDB_CONTROL_RELEASE_IP to node %u\n", nodemap->nodes[i].pnn)); - talloc_free(tmp_ctx); - return -1; - } - - ctdb_client_async_add(async_data, state); - } - } - if (ctdb_client_async_wait(ctdb, async_data) != 0) { - DEBUG(DEBUG_ERR, - ("Async control CTDB_CONTROL_RELEASE_IP failed\n")); - goto fail; - } - talloc_free(async_data); - - - /* For each IP, send a TAKOVER_IP to the node that should be - * hosting it. Many of these will often be redundant (since - * the allocation won't have changed) but they can be useful - * to recover from inconsistencies. */ - async_data = talloc_zero(tmp_ctx, struct client_async_data); - CTDB_NO_MEMORY_FATAL(ctdb, async_data); - - async_data->fail_callback = takeover_run_fail_callback; - async_data->callback_data = takeover_data; - - for (tmp_ip=all_ips;tmp_ip;tmp_ip=tmp_ip->next) { - if (tmp_ip->pnn == -1) { - /* this IP won't be taken over */ - continue; - } - - ip.pnn = tmp_ip->pnn; - ip.addr = tmp_ip->addr; - - data.dsize = sizeof(ip); - data.dptr = (uint8_t *)&ip; - state = ctdb_control_send(ctdb, tmp_ip->pnn, - 0, CTDB_CONTROL_TAKEOVER_IP, 0, - data, async_data, &timeout, NULL); - if (state == NULL) { - DEBUG(DEBUG_ERR,(__location__ " Failed to call async control CTDB_CONTROL_TAKEOVER_IP to node %u\n", tmp_ip->pnn)); - talloc_free(tmp_ctx); - return -1; - } - - ctdb_client_async_add(async_data, state); - } - if (ctdb_client_async_wait(ctdb, async_data) != 0) { - DEBUG(DEBUG_ERR, - ("Async control CTDB_CONTROL_TAKEOVER_IP failed\n")); - goto fail; - } - -ipreallocated: - /* - * Tell all nodes to run eventscripts to process the - * "ipreallocated" event. This can do a lot of things, - * including restarting services to reconfigure them if public - * IPs have moved. Once upon a time this event only used to - * update natgw. - */ - nodes = list_of_connected_nodes(ctdb, nodemap, tmp_ctx, true); - ret = ctdb_client_async_control(ctdb, CTDB_CONTROL_IPREALLOCATED, - nodes, 0, timeout, - false, tdb_null, - NULL, takeover_run_fail_callback, - takeover_data); - if (ret != 0) { - DEBUG(DEBUG_ERR, - ("Async CTDB_CONTROL_IPREALLOCATED control failed\n")); - goto fail; - } - - talloc_free(tmp_ctx); - return ret; - -fail: - takeover_run_process_failures(ctdb, takeover_data); - talloc_free(tmp_ctx); - return -1; -} - - /* destroy a ctdb_client_ip structure */ @@ -2254,6 +1674,7 @@ ctdb_sock_addr *addr; struct ctdb_public_ip_info_old *info; struct ctdb_vnn *vnn; + struct vnn_interface *iface; addr = (ctdb_sock_addr *)indata.dptr; @@ -2267,7 +1688,7 @@ /* count how many public ip structures we have */ num = 0; - for (;vnn->ifaces[num];) { + for (iface = vnn->ifaces; iface != NULL; iface = iface->next) { num++; } @@ -2280,15 +1701,11 @@ info->ip.pnn = vnn->pnn; info->active_idx = 0xFFFFFFFF; - for (i=0; vnn->ifaces[i]; i++) { + i = 0; + for (iface = vnn->ifaces; iface != NULL; iface = iface->next) { struct ctdb_interface *cur; - cur = ctdb_find_iface(ctdb, vnn->ifaces[i]); - if (cur == NULL) { - DEBUG(DEBUG_CRIT, (__location__ " internal error iface[%s] unknown\n", - vnn->ifaces[i])); - return -1; - } + cur = iface->iface; if (vnn->iface == cur) { info->active_idx = i; } @@ -2297,6 +1714,8 @@ info->ifaces[i].name[sizeof(info->ifaces[i].name)-1] = '\0'; info->ifaces[i].link_state = cur->link_up; info->ifaces[i].references = cur->references; + + i++; } info->num = i; len = offsetof(struct ctdb_public_ip_info_old, ifaces) + @@ -2998,7 +2417,7 @@ struct ctdb_addr_info_old *pub; const char *ifaces = NULL; uint32_t len; - int iface = 0; + struct vnn_interface *iface = NULL; DEBUG(DEBUG_NOTICE, ("New IP %s configured, adding it\n", @@ -3022,12 +2441,12 @@ first_add = false; } - ifaces = vnn->ifaces[0]; - iface = 1; - while (vnn->ifaces[iface] != NULL) { + ifaces = vnn->ifaces->iface->name; + iface = vnn->ifaces->next; + while (iface != NULL) { ifaces = talloc_asprintf(vnn, "%s,%s", ifaces, - vnn->ifaces[iface]); - iface++; + iface->iface->name); + iface = iface->next; } len = strlen(ifaces) + 1; @@ -3114,10 +2533,9 @@ signed char res = 0; close(h->fd[0]); - debug_extra = talloc_asprintf(NULL, "reloadips:"); prctl_set_comment("ctdb_reloadips"); - if (switch_from_server_to_client(ctdb, "reloadips-child") != 0) { + if (switch_from_server_to_client(ctdb) != 0) { DEBUG(DEBUG_CRIT,("ERROR: Failed to switch reloadips child into client mode\n")); res = -1; } else { diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_takeover_helper.c samba-4.6.5+dfsg/ctdb/server/ctdb_takeover_helper.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_takeover_helper.c 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_takeover_helper.c 2017-01-26 12:19:08.000000000 +0000 @@ -0,0 +1,1225 @@ +/* + CTDB IP takeover helper + + Copyright (C) Martin Schwenke 2016 + + Based on ctdb_recovery_helper.c + Copyright (C) Amitay Isaacs 2015 + + and ctdb_takeover.c + Copyright (C) Ronnie Sahlberg 2007 + Copyright (C) Andrew Tridgell 2007 + Copyright (C) Martin Schwenke 2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include "replace.h" +#include "system/network.h" +#include "system/filesys.h" + +#include +#include +#include + +#include "lib/util/debug.h" +#include "lib/util/strv.h" +#include "lib/util/strv_util.h" +#include "lib/util/sys_rw.h" +#include "lib/util/time.h" +#include "lib/util/tevent_unix.h" + +#include "protocol/protocol.h" +#include "protocol/protocol_api.h" +#include "client/client.h" + +#include "common/logging.h" + +#include "server/ipalloc.h" + +static int takeover_timeout = 9; + +#define TIMEOUT() timeval_current_ofs(takeover_timeout, 0) + +/* + * Utility functions + */ + +static bool generic_recv(struct tevent_req *req, int *perr) +{ + int err; + + if (tevent_req_is_unix_error(req, &err)) { + if (perr != NULL) { + *perr = err; + } + return false; + } + + return true; +} + +static enum ipalloc_algorithm +determine_algorithm(const struct ctdb_tunable_list *tunables) +{ + switch (tunables->ip_alloc_algorithm) { + case 0: + return IPALLOC_DETERMINISTIC; + case 1: + return IPALLOC_NONDETERMINISTIC; + case 2: + return IPALLOC_LCP2; + default: + return IPALLOC_LCP2; + }; +} + +/**********************************************************************/ + +struct get_public_ips_state { + struct tevent_context *ev; + struct ctdb_client_context *client; + uint32_t *pnns; + int count, num_nodes; + struct ctdb_public_ip_list *ips; +}; + +static void get_public_ips_done(struct tevent_req *subreq); + +static struct tevent_req *get_public_ips_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ctdb_client_context *client, + uint32_t *pnns, + int count, int num_nodes, + bool available_only) +{ + struct tevent_req *req, *subreq; + struct get_public_ips_state *state; + struct ctdb_req_control request; + + req = tevent_req_create(mem_ctx, &state, struct get_public_ips_state); + if (req == NULL) { + return NULL; + } + + state->pnns = pnns; + state->count = count; + state->num_nodes = num_nodes; + state->ips = NULL; + + ctdb_req_control_get_public_ips(&request, available_only); + subreq = ctdb_client_control_multi_send(mem_ctx, ev, client, + state->pnns, + state->count, + TIMEOUT(), &request); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, get_public_ips_done, req); + + return req; +} + +static void get_public_ips_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct get_public_ips_state *state = tevent_req_data( + req, struct get_public_ips_state); + struct ctdb_reply_control **reply; + int *err_list; + int ret, i; + bool status; + + status = ctdb_client_control_multi_recv(subreq, &ret, state, &err_list, + &reply); + TALLOC_FREE(subreq); + if (! status) { + int ret2; + uint32_t pnn; + + ret2 = ctdb_client_control_multi_error(state->pnns, + state->count, + err_list, &pnn); + if (ret2 != 0) { + D_ERR("control GET_PUBLIC_IPS failed on " + "node %u, ret=%d\n", pnn, ret2); + } else { + D_ERR("control GET_PUBLIC_IPS failed, " + "ret=%d\n", ret); + } + tevent_req_error(req, ret); + return; + } + + state->ips = talloc_zero_array(state, struct ctdb_public_ip_list, + state->num_nodes); + if (tevent_req_nomem(state->ips, req)) { + return; + } + + for (i = 0; i < state->count; i++) { + uint32_t pnn; + struct ctdb_public_ip_list *ips; + + pnn = state->pnns[i]; + ret = ctdb_reply_control_get_public_ips(reply[i], state->ips, + &ips); + if (ret != 0) { + D_ERR("control GET_PUBLIC_IPS failed on " + "node %u\n", pnn); + tevent_req_error(req, EIO); + return; + } + state->ips[pnn] = *ips; + } + + talloc_free(reply); + + tevent_req_done(req); +} + +static bool get_public_ips_recv(struct tevent_req *req, int *perr, + TALLOC_CTX *mem_ctx, + struct ctdb_public_ip_list **ips) +{ + struct get_public_ips_state *state = tevent_req_data( + req, struct get_public_ips_state); + int err; + + if (tevent_req_is_unix_error(req, &err)) { + if (perr != NULL) { + *perr = err; + } + return false; + } + + *ips = talloc_steal(mem_ctx, state->ips); + + return true; +} + +/**********************************************************************/ + +struct release_ip_state { + int num_sent; + int num_replies; + int num_fails; + int err_any; + uint32_t *ban_credits; +}; + +struct release_ip_one_state { + struct tevent_req *req; + uint32_t *pnns; + int count; + const char *ip_str; +}; + +static void release_ip_done(struct tevent_req *subreq); + +static struct tevent_req *release_ip_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ctdb_client_context *client, + uint32_t *pnns, + int count, + struct timeval timeout, + struct public_ip_list *all_ips, + uint32_t *ban_credits) +{ + struct tevent_req *req, *subreq; + struct release_ip_state *state; + struct ctdb_req_control request; + struct public_ip_list *tmp_ip; + + req = tevent_req_create(mem_ctx, &state, struct release_ip_state); + if (req == NULL) { + return NULL; + } + + state->num_sent = 0; + state->num_replies = 0; + state->num_fails = 0; + state->ban_credits = ban_credits; + + /* Send a RELEASE_IP to all nodes that should not be hosting + * each IP. For each IP, all but one of these will be + * redundant. However, the redundant ones are used to tell + * nodes which node should be hosting the IP so that commands + * like "ctdb ip" can display a particular nodes idea of who + * is hosting what. */ + for (tmp_ip = all_ips; tmp_ip != NULL; tmp_ip = tmp_ip->next) { + struct release_ip_one_state *substate; + struct ctdb_public_ip ip; + int i; + + substate = talloc_zero(state, struct release_ip_one_state); + if (tevent_req_nomem(substate, req)) { + return tevent_req_post(req, ev); + } + + substate->pnns = talloc_zero_array(substate, uint32_t, count); + if (tevent_req_nomem(substate->pnns, req)) { + return tevent_req_post(req, ev); + } + + substate->count = 0; + substate->req = req; + + substate->ip_str = ctdb_sock_addr_to_string(substate, + &tmp_ip->addr); + if (tevent_req_nomem(substate->ip_str, req)) { + return tevent_req_post(req, ev); + } + + for (i = 0; i < count; i++) { + uint32_t pnn = pnns[i]; + /* If pnn is not the node that should be + * hosting the IP then add it to the list of + * nodes that need to do a release. */ + if (tmp_ip->pnn != pnn) { + substate->pnns[substate->count] = pnn; + substate->count++; + } + } + + if (substate->count == 0) { + /* No releases to send for this address... */ + TALLOC_FREE(substate); + continue; + } + + ip.pnn = tmp_ip->pnn; + ip.addr = tmp_ip->addr; + ctdb_req_control_release_ip(&request, &ip); + subreq = ctdb_client_control_multi_send(state, ev, client, + substate->pnns, + substate->count, + timeout,/* cumulative */ + &request); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, release_ip_done, substate); + + state->num_sent++; + } + + /* None sent, finished... */ + if (state->num_sent == 0) { + tevent_req_done(req); + return tevent_req_post(req, ev); + } + + return req; +} + +static void release_ip_done(struct tevent_req *subreq) +{ + struct release_ip_one_state *substate = tevent_req_callback_data( + subreq, struct release_ip_one_state); + struct tevent_req *req = substate->req; + struct release_ip_state *state = tevent_req_data( + req, struct release_ip_state); + int ret, i; + int *err_list; + bool status, found_errors; + + status = ctdb_client_control_multi_recv(subreq, &ret, state, + &err_list, NULL); + TALLOC_FREE(subreq); + + if (status) { + D_INFO("RELEASE_IP %s succeeded on %d nodes\n", + substate->ip_str, substate->count); + goto done; + } + + /* Get some clear error messages out of err_list and count + * banning credits + */ + found_errors = false; + for (i = 0; i < substate->count; i++) { + int err = err_list[i]; + if (err != 0) { + uint32_t pnn = substate->pnns[i]; + + D_ERR("RELEASE_IP %s failed on node %u, " + "ret=%d\n", substate->ip_str, pnn, err); + + state->ban_credits[pnn]++; + state->err_any = err; + found_errors = true; + } + } + if (! found_errors) { + D_ERR("RELEASE_IP %s internal error, ret=%d\n", + substate->ip_str, ret); + state->err_any = EIO; + } + + state->num_fails++; + +done: + talloc_free(substate); + + state->num_replies++; + + if (state->num_replies < state->num_sent) { + /* Not all replies received, don't go further */ + return; + } + + if (state->num_fails > 0) { + tevent_req_error(req, state->err_any); + return; + } + + tevent_req_done(req); +} + +static bool release_ip_recv(struct tevent_req *req, int *perr) +{ + return generic_recv(req, perr); +} + +/**********************************************************************/ + +struct take_ip_state { + int num_sent; + int num_replies; + int num_fails; + int err_any; + uint32_t *ban_credits; +}; + +struct take_ip_one_state { + struct tevent_req *req; + uint32_t pnn; + const char *ip_str; +}; + +static void take_ip_done(struct tevent_req *subreq); + +static struct tevent_req *take_ip_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ctdb_client_context *client, + struct timeval timeout, + struct public_ip_list *all_ips, + uint32_t *ban_credits) +{ + struct tevent_req *req, *subreq; + struct take_ip_state *state; + struct ctdb_req_control request; + struct public_ip_list *tmp_ip; + + req = tevent_req_create(mem_ctx, &state, struct take_ip_state); + if (req == NULL) { + return NULL; + } + + state->num_sent = 0; + state->num_replies = 0; + state->num_fails = 0; + state->ban_credits = ban_credits; + + /* For each IP, send a TAKOVER_IP to the node that should be + * hosting it. Many of these will often be redundant (since + * the allocation won't have changed) but they can be useful + * to recover from inconsistencies. */ + for (tmp_ip = all_ips; tmp_ip != NULL; tmp_ip = tmp_ip->next) { + struct take_ip_one_state *substate; + struct ctdb_public_ip ip; + + if (tmp_ip->pnn == -1) { + /* IP will be unassigned */ + continue; + } + + substate = talloc_zero(state, struct take_ip_one_state); + if (tevent_req_nomem(substate, req)) { + return tevent_req_post(req, ev); + } + + substate->req = req; + substate->pnn = tmp_ip->pnn; + + substate->ip_str = ctdb_sock_addr_to_string(substate, + &tmp_ip->addr); + if (tevent_req_nomem(substate->ip_str, req)) { + return tevent_req_post(req, ev); + } + + ip.pnn = tmp_ip->pnn; + ip.addr = tmp_ip->addr; + ctdb_req_control_takeover_ip(&request, &ip); + subreq = ctdb_client_control_send( + state, ev, client, tmp_ip->pnn, + timeout, /* cumulative */ + &request); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, take_ip_done, substate); + + state->num_sent++; + } + + /* None sent, finished... */ + if (state->num_sent == 0) { + tevent_req_done(req); + return tevent_req_post(req, ev); + } + + return req; +} + +static void take_ip_done(struct tevent_req *subreq) +{ + struct take_ip_one_state *substate = tevent_req_callback_data( + subreq, struct take_ip_one_state); + struct tevent_req *req = substate->req; + struct ctdb_reply_control *reply; + struct take_ip_state *state = tevent_req_data( + req, struct take_ip_state); + int ret = 0; + bool status; + + status = ctdb_client_control_recv(subreq, &ret, state, &reply); + TALLOC_FREE(subreq); + + if (! status) { + D_ERR("TAKEOVER_IP %s failed to node %u, ret=%d\n", + substate->ip_str, substate->pnn, ret); + goto fail; + } + + ret = ctdb_reply_control_takeover_ip(reply); + if (ret != 0) { + D_ERR("TAKEOVER_IP %s failed on node %u, ret=%d\n", + substate->ip_str, substate->pnn, ret); + goto fail; + } + + D_INFO("TAKEOVER_IP %s succeeded on node %u\n", + substate->ip_str, substate->pnn); + goto done; + +fail: + state->ban_credits[substate->pnn]++; + state->num_fails++; + state->err_any = ret; + +done: + talloc_free(substate); + + state->num_replies++; + + if (state->num_replies < state->num_sent) { + /* Not all replies received, don't go further */ + return; + } + + if (state->num_fails > 0) { + tevent_req_error(req, state->err_any); + return; + } + + tevent_req_done(req); +} + +static bool take_ip_recv(struct tevent_req *req, int *perr) +{ + return generic_recv(req, perr); +} + +/**********************************************************************/ + +struct ipreallocated_state { + uint32_t *pnns; + int count; + uint32_t *ban_credits; +}; + +static void ipreallocated_done(struct tevent_req *subreq); + +static struct tevent_req *ipreallocated_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ctdb_client_context *client, + uint32_t *pnns, + int count, + struct timeval timeout, + uint32_t *ban_credits) +{ + struct tevent_req *req, *subreq; + struct ipreallocated_state *state; + struct ctdb_req_control request; + + req = tevent_req_create(mem_ctx, &state, struct ipreallocated_state); + if (req == NULL) { + return NULL; + } + + state->pnns = pnns; + state->count = count; + state->ban_credits = ban_credits; + + ctdb_req_control_ipreallocated(&request); + subreq = ctdb_client_control_multi_send(state, ev, client, + pnns, count, + timeout, /* cumulative */ + &request); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, ipreallocated_done, req); + + return req; +} + +static void ipreallocated_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct ipreallocated_state *state = tevent_req_data( + req, struct ipreallocated_state); + int *err_list = NULL; + int ret, i; + bool status, found_errors; + + status = ctdb_client_control_multi_recv(subreq, &ret, state, + &err_list, NULL); + TALLOC_FREE(subreq); + + if (status) { + D_INFO("IPREALLOCATED succeeded on %d nodes\n", state->count); + tevent_req_done(req); + return; + } + + /* Get some clear error messages out of err_list and count + * banning credits + */ + found_errors = false; + for (i = 0; i < state->count; i++) { + int err = err_list[i]; + if (err != 0) { + uint32_t pnn = state->pnns[i]; + + D_ERR("IPREALLOCATED failed on node %u, ret=%d\n", + pnn, err); + + state->ban_credits[pnn]++; + found_errors = true; + } + } + + if (! found_errors) { + D_ERR("IPREALLOCATED internal error, ret=%d\n", ret); + } + + tevent_req_error(req, ret); +} + +static bool ipreallocated_recv(struct tevent_req *req, int *perr) +{ + return generic_recv(req, perr); +} + +/**********************************************************************/ + +/* + * Recalculate the allocation of public IPs to nodes and have the + * nodes host their allocated addresses. + * + * - Get tunables + * - Get nodemap + * - Initialise IP allocation state. Pass: + * + algorithm to be used; + * + various tunables (NoIPTakeover, NoIPFailback, NoIPHostOnAllDisabled) + * + list of nodes to force rebalance (internal structure, currently + * no way to fetch, only used by LCP2 for nodes that have had new + * IP addresses added). + * - Set IP flags for IP allocation based on node map + * - Retrieve known and available IP addresses (done separately so + * values can be faked in unit testing) + * - Use ipalloc_set_public_ips() to set known and available IP + * addresses for allocation + * - If cluster can't host IP addresses then jump to IPREALLOCATED + * - Run IP allocation algorithm + * - Send RELEASE_IP to all nodes for IPs they should not host + * - Send TAKE_IP to all nodes for IPs they should host + * - Send IPREALLOCATED to all nodes + */ + +struct takeover_state { + struct tevent_context *ev; + struct ctdb_client_context *client; + struct timeval timeout; + int num_nodes; + uint32_t *pnns_connected; + int num_connected; + uint32_t *pnns_active; + int num_active; + uint32_t destnode; + uint32_t *force_rebalance_nodes; + struct ctdb_tunable_list *tun_list; + struct ipalloc_state *ipalloc_state; + struct ctdb_public_ip_list *known_ips; + struct public_ip_list *all_ips; + uint32_t *ban_credits; +}; + +static void takeover_tunables_done(struct tevent_req *subreq); +static void takeover_nodemap_done(struct tevent_req *subreq); +static void takeover_known_ips_done(struct tevent_req *subreq); +static void takeover_avail_ips_done(struct tevent_req *subreq); +static void takeover_release_ip_done(struct tevent_req *subreq); +static void takeover_take_ip_done(struct tevent_req *subreq); +static void takeover_ipreallocated(struct tevent_req *req); +static void takeover_ipreallocated_done(struct tevent_req *subreq); +static void takeover_failed(struct tevent_req *subreq, int ret); +static void takeover_failed_done(struct tevent_req *subreq); + +static struct tevent_req *takeover_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ctdb_client_context *client, + uint32_t *force_rebalance_nodes) +{ + struct tevent_req *req, *subreq; + struct takeover_state *state; + struct ctdb_req_control request; + + req = tevent_req_create(mem_ctx, &state, struct takeover_state); + if (req == NULL) { + return NULL; + } + + state->ev = ev; + state->client = client; + state->force_rebalance_nodes = force_rebalance_nodes; + state->destnode = ctdb_client_pnn(client); + + ctdb_req_control_get_all_tunables(&request); + subreq = ctdb_client_control_send(state, state->ev, state->client, + state->destnode, TIMEOUT(), + &request); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, takeover_tunables_done, req); + + return req; +} + +static void takeover_tunables_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct takeover_state *state = tevent_req_data( + req, struct takeover_state); + struct ctdb_reply_control *reply; + struct ctdb_req_control request; + int ret; + bool status; + + status = ctdb_client_control_recv(subreq, &ret, state, &reply); + TALLOC_FREE(subreq); + if (! status) { + D_ERR("control GET_ALL_TUNABLES failed, ret=%d\n", ret); + tevent_req_error(req, ret); + return; + } + + ret = ctdb_reply_control_get_all_tunables(reply, state, + &state->tun_list); + if (ret != 0) { + D_ERR("control GET_ALL_TUNABLES failed, ret=%d\n", ret); + tevent_req_error(req, ret); + return; + } + + talloc_free(reply); + + takeover_timeout = state->tun_list->takeover_timeout; + + ctdb_req_control_get_nodemap(&request); + subreq = ctdb_client_control_send(state, state->ev, state->client, + state->destnode, TIMEOUT(), + &request); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, takeover_nodemap_done, req); +} + +static void takeover_nodemap_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct takeover_state *state = tevent_req_data( + req, struct takeover_state); + struct ctdb_reply_control *reply; + bool status; + int ret; + struct ctdb_node_map *nodemap; + + status = ctdb_client_control_recv(subreq, &ret, state, &reply); + TALLOC_FREE(subreq); + if (! status) { + D_ERR("control GET_NODEMAP failed to node %u, ret=%d\n", + state->destnode, ret); + tevent_req_error(req, ret); + return; + } + + ret = ctdb_reply_control_get_nodemap(reply, state, &nodemap); + if (ret != 0) { + D_ERR("control GET_NODEMAP failed, ret=%d\n", ret); + tevent_req_error(req, ret); + return; + } + + state->num_nodes = nodemap->num; + + state->num_connected = list_of_connected_nodes(nodemap, + CTDB_UNKNOWN_PNN, state, + &state->pnns_connected); + if (state->num_connected <= 0) { + tevent_req_error(req, ENOMEM); + return; + } + + state->num_active = list_of_active_nodes(nodemap, + CTDB_UNKNOWN_PNN, state, + &state->pnns_active); + if (state->num_active <= 0) { + tevent_req_error(req, ENOMEM); + return; + } + + /* Default timeout for early jump to IPREALLOCATED. See below + * for explanation of 3 times... + */ + state->timeout = timeval_current_ofs(3 * takeover_timeout, 0); + + state->ban_credits = talloc_zero_array(state, uint32_t, + state->num_nodes); + if (tevent_req_nomem(state->ban_credits, req)) { + return; + } + + if (state->tun_list->disable_ip_failover != 0) { + /* IP failover is completely disabled so just send out + * ipreallocated event. + */ + takeover_ipreallocated(req); + return; + } + + state->ipalloc_state = + ipalloc_state_init( + state, state->num_nodes, + determine_algorithm(state->tun_list), + (state->tun_list->no_ip_takeover != 0), + (state->tun_list->no_ip_failback != 0), + (state->tun_list->no_ip_host_on_all_disabled != 0), + state->force_rebalance_nodes); + if (tevent_req_nomem(state->ipalloc_state, req)) { + return; + } + + ipalloc_set_node_flags(state->ipalloc_state, nodemap); + + subreq = get_public_ips_send(state, state->ev, state->client, + state->pnns_active, state->num_active, + state->num_nodes, false); + if (tevent_req_nomem(subreq, req)) { + return; + } + + tevent_req_set_callback(subreq, takeover_known_ips_done, req); +} + +static void takeover_known_ips_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct takeover_state *state = tevent_req_data( + req, struct takeover_state); + int ret; + bool status; + + status = get_public_ips_recv(subreq, &ret, state, &state->known_ips); + TALLOC_FREE(subreq); + + if (! status) { + D_ERR("Failed to fetch known public IPs\n"); + tevent_req_error(req, ret); + return; + } + + subreq = get_public_ips_send(state, state->ev, state->client, + state->pnns_active, state->num_active, + state->num_nodes, true); + if (tevent_req_nomem(subreq, req)) { + return; + } + + tevent_req_set_callback(subreq, takeover_avail_ips_done, req); +} + +static void takeover_avail_ips_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct takeover_state *state = tevent_req_data( + req, struct takeover_state); + bool status; + int ret; + struct ctdb_public_ip_list *available_ips; + + status = get_public_ips_recv(subreq, &ret, state, &available_ips); + TALLOC_FREE(subreq); + + if (! status) { + D_ERR("Failed to fetch available public IPs\n"); + tevent_req_error(req, ret); + return; + } + + ipalloc_set_public_ips(state->ipalloc_state, + state->known_ips, available_ips); + + if (! ipalloc_can_host_ips(state->ipalloc_state)) { + D_NOTICE("No nodes available to host public IPs yet\n"); + takeover_ipreallocated(req); + return; + } + + /* Do the IP reassignment calculations */ + state->all_ips = ipalloc(state->ipalloc_state); + if (tevent_req_nomem(state->all_ips, req)) { + return; + } + + /* Each of the following stages (RELEASE_IP, TAKEOVER_IP, + * IPREALLOCATED) notionally has a timeout of TakeoverTimeout + * seconds. However, RELEASE_IP can take longer due to TCP + * connection killing, so sometimes needs more time. + * Therefore, use a cumulative timeout of TakeoverTimeout * 3 + * seconds across all 3 stages. No explicit expiry checks are + * needed before each stage because tevent is smart enough to + * fire the timeouts even if they are in the past. Initialise + * this here so it explicitly covers the stages we're + * interested in but, in particular, not the time taken by the + * ipalloc(). + */ + state->timeout = timeval_current_ofs(3 * takeover_timeout, 0); + + subreq = release_ip_send(state, state->ev, state->client, + state->pnns_connected, state->num_connected, + state->timeout, state->all_ips, + state->ban_credits); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, takeover_release_ip_done, req); +} + +static void takeover_release_ip_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct takeover_state *state = tevent_req_data( + req, struct takeover_state); + int ret; + bool status; + + status = release_ip_recv(subreq, &ret); + TALLOC_FREE(subreq); + + if (! status) { + takeover_failed(req, ret); + return; + } + + /* All released, now for takeovers */ + + subreq = take_ip_send(state, state->ev, state->client, + state->timeout, state->all_ips, + state->ban_credits); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, takeover_take_ip_done, req); +} + +static void takeover_take_ip_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + int ret = 0; + bool status; + + status = take_ip_recv(subreq, &ret); + TALLOC_FREE(subreq); + + if (! status) { + takeover_failed(req, ret); + return; + } + + takeover_ipreallocated(req); +} + +static void takeover_ipreallocated(struct tevent_req *req) +{ + struct takeover_state *state = tevent_req_data( + req, struct takeover_state); + struct tevent_req *subreq; + + subreq = ipreallocated_send(state, state->ev, state->client, + state->pnns_connected, + state->num_connected, + state->timeout, + state->ban_credits); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, takeover_ipreallocated_done, req); +} + +static void takeover_ipreallocated_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + int ret; + bool status; + + status = ipreallocated_recv(subreq, &ret); + TALLOC_FREE(subreq); + + if (! status) { + takeover_failed(req, ret); + return; + } + + tevent_req_done(req); +} + +struct takeover_failed_state { + struct tevent_req *req; + int ret; +}; + +void takeover_failed(struct tevent_req *req, int ret) +{ + struct takeover_state *state = tevent_req_data( + req, struct takeover_state); + struct tevent_req *subreq; + uint32_t max_pnn = CTDB_UNKNOWN_PNN; + int max_credits = 0; + int pnn; + + /* Check that bans are enabled */ + if (state->tun_list->enable_bans == 0) { + tevent_req_error(req, ret); + return; + } + + for (pnn = 0; pnn < state->num_nodes; pnn++) { + if (state->ban_credits[pnn] > max_credits) { + max_pnn = pnn; + max_credits = state->ban_credits[pnn]; + } + } + + if (max_credits > 0) { + struct ctdb_req_message message; + struct takeover_failed_state *substate; + + D_WARNING("Assigning banning credits to node %u\n", max_pnn); + + substate = talloc_zero(state, struct takeover_failed_state); + if (tevent_req_nomem(substate, req)) { + return; + } + substate->req = req; + substate->ret = ret; + + message.srvid = CTDB_SRVID_BANNING; + message.data.pnn = max_pnn; + + subreq = ctdb_client_message_send( + state, state->ev, state->client, + ctdb_client_pnn(state->client), + &message); + if (subreq == NULL) { + D_ERR("failed to assign banning credits\n"); + tevent_req_error(req, ret); + return; + } + tevent_req_set_callback(subreq, takeover_failed_done, substate); + } else { + tevent_req_error(req, ret); + } +} + +static void takeover_failed_done(struct tevent_req *subreq) +{ + struct takeover_failed_state *substate = tevent_req_callback_data( + subreq, struct takeover_failed_state); + struct tevent_req *req = substate->req; + int ret; + bool status; + + status = ctdb_client_message_recv(subreq, &ret); + TALLOC_FREE(subreq); + if (! status) { + D_ERR("failed to assign banning credits, ret=%d\n", ret); + } + + ret = substate->ret; + talloc_free(substate); + tevent_req_error(req, ret); +} + +static void takeover_recv(struct tevent_req *req, int *perr) +{ + generic_recv(req, perr); +} + +static uint32_t *parse_node_list(TALLOC_CTX *mem_ctx, const char* s) +{ + char *strv = NULL; + int num, i, ret; + char *t; + uint32_t *nodes; + + ret = strv_split(mem_ctx, &strv, s, ","); + if (ret != 0) { + D_ERR("out of memory\n"); + return NULL; + } + + num = strv_count(strv); + + nodes = talloc_array(mem_ctx, uint32_t, num); + if (nodes == NULL) { + D_ERR("out of memory\n"); + return NULL; + } + + t = NULL; + for (i = 0; i < num; i++) { + t = strv_next(strv, t); + nodes[i] = atoi(t); + } + + return nodes; +} + +static void usage(const char *progname) +{ + fprintf(stderr, + "\nUsage: %s " + "[]\n", + progname); +} + +/* + * Arguments - write fd, socket path + */ +int main(int argc, const char *argv[]) +{ + int write_fd; + const char *sockpath; + TALLOC_CTX *mem_ctx; + struct tevent_context *ev; + struct ctdb_client_context *client; + int ret; + struct tevent_req *req; + uint32_t *force_rebalance_nodes = NULL; + + if (argc < 3 || argc > 4) { + usage(argv[0]); + exit(1); + } + + write_fd = atoi(argv[1]); + sockpath = argv[2]; + + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + fprintf(stderr, "talloc_new() failed\n"); + ret = ENOMEM; + goto done; + } + + if (argc == 4) { + force_rebalance_nodes = parse_node_list(mem_ctx, argv[3]); + if (force_rebalance_nodes == NULL) { + usage(argv[0]); + ret = EINVAL; + goto done; + } + } + + ret = logging_init(mem_ctx, NULL, NULL, "ctdb-takeover"); + if (ret != 0) { + fprintf(stderr, + "ctdb-takeover: Unable to initialize logging\n"); + goto done; + } + + ev = tevent_context_init(mem_ctx); + if (ev == NULL) { + D_ERR("tevent_context_init() failed\n"); + ret = ENOMEM; + goto done; + } + + ret = ctdb_client_init(mem_ctx, ev, sockpath, &client); + if (ret != 0) { + D_ERR("ctdb_client_init() failed, ret=%d\n", ret); + goto done; + } + + req = takeover_send(mem_ctx, ev, client, force_rebalance_nodes); + if (req == NULL) { + D_ERR("takeover_send() failed\n"); + ret = 1; + goto done; + } + + if (! tevent_req_poll(req, ev)) { + D_ERR("tevent_req_poll() failed\n"); + ret = 1; + goto done; + } + + takeover_recv(req, &ret); + TALLOC_FREE(req); + if (ret != 0) { + D_ERR("takeover run failed, ret=%d\n", ret); + } + +done: + sys_write_v(write_fd, &ret, sizeof(ret)); + + talloc_free(mem_ctx); + return ret; +} diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_traverse.c samba-4.6.5+dfsg/ctdb/server/ctdb_traverse.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_traverse.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_traverse.c 2017-01-11 07:55:14.000000000 +0000 @@ -30,6 +30,7 @@ #include "lib/util/dlinklist.h" #include "lib/util/debug.h" #include "lib/util/samba_util.h" +#include "lib/util/sys_rw.h" #include "lib/util/util_process.h" #include "ctdb_private.h" @@ -219,8 +220,7 @@ close(h->fd[0]); prctl_set_comment("ctdb_traverse"); - if (switch_from_server_to_client(ctdb, "traverse_local-%s:", - ctdb_db->db_name) != 0) { + if (switch_from_server_to_client(ctdb) != 0) { DEBUG(DEBUG_CRIT, ("Failed to switch traverse child into client mode\n")); _exit(0); } diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_update_record.c samba-4.6.5+dfsg/ctdb/server/ctdb_update_record.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_update_record.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_update_record.c 2017-01-11 07:55:14.000000000 +0000 @@ -28,6 +28,7 @@ #include "lib/tdb_wrap/tdb_wrap.h" #include "lib/util/debug.h" #include "lib/util/samba_util.h" +#include "lib/util/sys_rw.h" #include "lib/util/util_process.h" #include "ctdb_private.h" @@ -267,7 +268,6 @@ close(result->fd[0]); prctl_set_comment("ctdb_write_persistent"); - debug_extra = talloc_asprintf(NULL, "childwrite-%s:", ctdb_db->db_name); ret = ctdb_persistent_store(state); if (ret != 0) { DEBUG(DEBUG_ERR, (__location__ " Failed to write persistent data\n")); diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_vacuum.c samba-4.6.5+dfsg/ctdb/server/ctdb_vacuum.c --- samba-4.5.8+dfsg/ctdb/server/ctdb_vacuum.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ctdb_vacuum.c 2017-01-11 07:55:14.000000000 +0000 @@ -31,13 +31,13 @@ #include "lib/util/dlinklist.h" #include "lib/util/debug.h" #include "lib/util/samba_util.h" +#include "lib/util/sys_rw.h" #include "lib/util/util_process.h" #include "ctdb_private.h" #include "ctdb_client.h" #include "common/rb_tree.h" -#include "common/system.h" #include "common/common.h" #include "common/logging.h" @@ -1507,7 +1507,7 @@ DEBUG(DEBUG_INFO,("Vacuuming child process %d for db %s started\n", getpid(), ctdb_db->db_name)); prctl_set_comment("ctdb_vacuum"); - if (switch_from_server_to_client(ctdb, "vacuum-%s", ctdb_db->db_name) != 0) { + if (switch_from_server_to_client(ctdb) != 0) { DEBUG(DEBUG_CRIT, (__location__ "ERROR: failed to switch vacuum daemon into client mode. Shutting down.\n")); _exit(1); } diff -Nru samba-4.5.8+dfsg/ctdb/server/eventscript.c samba-4.6.5+dfsg/ctdb/server/eventscript.c --- samba-4.5.8+dfsg/ctdb/server/eventscript.c 2016-09-13 08:21:35.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/eventscript.c 2017-01-26 12:19:08.000000000 +0000 @@ -24,6 +24,7 @@ #include "system/dir.h" #include "system/locale.h" #include "system/time.h" +#include "system/dir.h" #include #include @@ -31,632 +32,619 @@ #include "lib/util/dlinklist.h" #include "lib/util/debug.h" #include "lib/util/samba_util.h" +#include "lib/util/sys_rw.h" #include "ctdb_private.h" #include "common/rb_tree.h" -#include "common/system.h" #include "common/common.h" #include "common/logging.h" +#include "common/reqid.h" +#include "common/sock_io.h" +#include "protocol/protocol_api.h" -static void ctdb_event_script_timeout(struct tevent_context *ev, - struct tevent_timer *te, - struct timeval t, void *p); - -/* This is attached to the event script state. */ -struct event_script_callback { - struct event_script_callback *next, *prev; - struct ctdb_context *ctdb; +/* + * Setting up event daemon + */ - /* Warning: this can free us! */ - void (*fn)(struct ctdb_context *, int, void *); - void *private_data; +struct eventd_context { + struct tevent_context *ev; + const char *path; + const char *script_dir; + const char *pidfile; + const char *socket; + const char *debug_hung_script; + + /* server state */ + pid_t eventd_pid; + struct tevent_fd *eventd_fde; + + /* client state */ + struct reqid_context *idr; + struct sock_queue *queue; + struct eventd_client_state *calls; }; -struct ctdb_event_script_state { - struct ctdb_context *ctdb; - struct event_script_callback *callback; - pid_t child; - int fd[2]; - enum ctdb_event call; - const char *options; - struct timeval timeout; +static bool eventd_context_init(TALLOC_CTX *mem_ctx, + struct ctdb_context *ctdb, + struct eventd_context **out) +{ + struct eventd_context *ectx; + const char *eventd = CTDB_HELPER_BINDIR "/ctdb_eventd"; + const char *debug_hung_script = CTDB_ETCDIR "/debug-hung-script.sh"; + const char *value; + char *socket; + int ret; - unsigned int current; - struct ctdb_script_list_old *scripts; -}; + ectx = talloc_zero(mem_ctx, struct eventd_context); + if (ectx == NULL) { + return false; + } -static struct ctdb_script *get_current_script(struct ctdb_event_script_state *state) -{ - return &state->scripts->scripts[state->current]; -} + ectx->ev = ctdb->ev; -/* called from ctdb_logging when we have received output on STDERR from - * one of the eventscripts - */ -static void log_event_script_output(const char *str, uint16_t len, void *p) -{ - struct ctdb_event_script_state *state - = talloc_get_type(p, struct ctdb_event_script_state); - struct ctdb_script *current; - unsigned int slen, min; - - /* We may have been aborted to run something else. Discard */ - if (state->scripts == NULL) { - return; + value = getenv("CTDB_EVENTD"); + if (value != NULL) { + eventd = value; } - current = get_current_script(state); - - /* Append, but don't overfill buffer. It starts zero-filled. */ - slen = strlen(current->output); - min = MIN(len, sizeof(current->output) - slen - 1); + ectx->path = talloc_strdup(ectx, eventd); + if (ectx->path == NULL) { + talloc_free(ectx); + return false; + } - memcpy(current->output + slen, str, min); -} + ectx->script_dir = ctdb->event_script_dir; -int32_t ctdb_control_get_event_script_status(struct ctdb_context *ctdb, - uint32_t call_type, - TDB_DATA *outdata) -{ - if (call_type >= CTDB_EVENT_MAX) { - return -1; + socket = talloc_strdup(ectx, ctdb_get_socketname(ctdb)); + if (socket == NULL) { + talloc_free(ectx); + return NULL; } - if (ctdb->last_status[call_type] == NULL) { - /* If it's never been run, return nothing so they can tell. */ - outdata->dsize = 0; - } else { - outdata->dsize = talloc_get_size(ctdb->last_status[call_type]); - outdata->dptr = (uint8_t *)ctdb->last_status[call_type]; + ectx->socket = talloc_asprintf(ectx, "%s/eventd.sock", + dirname(socket)); + if (ectx->socket == NULL) { + talloc_free(ectx); + return false; } - return 0; -} -/* To ignore directory entry return 0, else return non-zero */ -static int script_filter(const struct dirent *de) -{ - int namelen = strlen(de->d_name); + talloc_free(socket); - /* Ignore . and .. */ - if (namelen < 3) { - return 0; + value = getenv("CTDB_DEBUG_HUNG_SCRIPT"); + if (value != NULL) { + if (value[0] == '\0') { + debug_hung_script = NULL; + } else { + debug_hung_script = value; + } } - /* Skip temporary files left behind by emacs */ - if (de->d_name[namelen-1] == '~') { - return 0; + if (debug_hung_script != NULL) { + ectx->debug_hung_script = talloc_strdup(ectx, + debug_hung_script); + if (ectx->debug_hung_script == NULL) { + talloc_free(ectx); + return false; + } } - /* Filename should start with [0-9][0-9]. */ - if (!isdigit(de->d_name[0]) || !isdigit(de->d_name[1]) || - de->d_name[2] != '.') { - return 0; + ret = reqid_init(ectx, 1, &ectx->idr); + if (ret != 0) { + talloc_free(ectx); + return false; } - if (namelen > MAX_SCRIPT_NAME) { - return 0; - } + ectx->eventd_pid = -1; - return 1; + *out = ectx; + return true; } -/* Return true if OK, otherwise set errno. */ -static bool check_executable(const char *dir, const char *name) -{ - char *full; - struct stat st; +/* + * Start and stop event daemon + */ - full = talloc_asprintf(NULL, "%s/%s", dir, name); - if (!full) - return false; +static bool eventd_client_connect(struct eventd_context *ectx); +static void eventd_dead_handler(struct tevent_context *ev, + struct tevent_fd *fde, uint16_t flags, + void *private_data); - if (stat(full, &st) != 0) { - DEBUG(DEBUG_ERR,("Could not stat event script %s: %s\n", - full, strerror(errno))); - talloc_free(full); - return false; - } +int ctdb_start_eventd(struct ctdb_context *ctdb) +{ + struct eventd_context *ectx; + const char **argv; + int fd[2]; + pid_t pid; + int ret, i; + bool status; - if (!(st.st_mode & S_IXUSR)) { - DEBUG(DEBUG_DEBUG,("Event script %s is not executable. Ignoring this event script\n", full)); - errno = ENOEXEC; - talloc_free(full); - return false; + if (ctdb->ectx == NULL) { + status = eventd_context_init(ctdb, ctdb, &ctdb->ectx); + if (! status) { + DEBUG(DEBUG_ERR, + ("Failed to initialize eventd context\n")); + return -1; + } } - talloc_free(full); - return true; -} + ectx = ctdb->ectx; -static struct ctdb_script_list_old *ctdb_get_script_list( - struct ctdb_context *ctdb, - TALLOC_CTX *mem_ctx) -{ - struct dirent **namelist; - struct ctdb_script_list_old *scripts; - int i, count; - - /* scan all directory entries and insert all valid scripts into the - tree - */ - count = scandir(ctdb->event_script_dir, &namelist, script_filter, alphasort); - if (count == -1) { - DEBUG(DEBUG_CRIT, ("Failed to read event script directory '%s' - %s\n", - ctdb->event_script_dir, strerror(errno))); - return NULL; + ret = unlink(ectx->socket); + if (ret == 0) { + D_WARNING("Removed stale eventd socket %s\n", ectx->socket); + } else if (errno != ENOENT) { + D_ERR("Failed to remove stale eventd socket %s\n", + ectx->socket); + return -1; } - /* Overallocates by one, but that's OK */ - scripts = talloc_zero_size(mem_ctx, - sizeof(*scripts) - + sizeof(scripts->scripts[0]) * count); - if (scripts == NULL) { - DEBUG(DEBUG_ERR, (__location__ " Failed to allocate scripts\n")); - goto done; - } - scripts->num_scripts = count; - - for (i = 0; i < count; i++) { - struct ctdb_script *s = &scripts->scripts[i]; - - if (strlcpy(s->name, namelist[i]->d_name, sizeof(s->name)) >= - sizeof(s->name)) { - s->status = -ENAMETOOLONG; - continue; - } + argv = talloc_array(ectx, const char *, 14); + if (argv == NULL) { + return -1; + } - s->status = 0; - if (!check_executable(ctdb->event_script_dir, - namelist[i]->d_name)) { - s->status = -errno; - } + argv[0] = ectx->path; + argv[1] = "-e"; + argv[2] = ectx->script_dir; + argv[3] = "-s"; + argv[4] = ectx->socket; + argv[5] = "-P"; + argv[6] = talloc_asprintf(argv, "%d", ctdb->ctdbd_pid); + argv[7] = "-l"; + argv[8] = getenv("CTDB_LOGGING"); + argv[9] = "-d"; + argv[10] = debug_level_to_string(DEBUGLEVEL); + if (ectx->debug_hung_script == NULL) { + argv[11] = NULL; + argv[12] = NULL; + } else { + argv[11] = "-D"; + argv[12] = ectx->debug_hung_script; } + argv[13] = NULL; -done: - for (i=0; ievent_script_dir, current->name); - tmp[2] = talloc_asprintf(tmp, "%s", ctdb_eventscript_call_names[call]); - n = 3; - - /* Split options into individual arguments */ - opt = talloc_strdup(mem_ctx, options); - if (opt == NULL) { - goto failed; - } - - t = strtok_r(opt, " ", &saveptr); - while (t != NULL) { - tmp[n++] = talloc_strdup(tmp, t); - if (n > MAX_HELPER_ARGS) { - goto args_failed; - } - t = strtok_r(NULL, " ", &saveptr); + pid = ctdb_fork(ctdb); + if (pid == -1) { + close(fd[0]); + close(fd[1]); + return -1; } - for (i=0; ieventd_fde = tevent_add_fd(ctdb->ev, ectx, fd[0], + TEVENT_FD_READ, + eventd_dead_handler, ectx); + if (ectx->eventd_fde == NULL) { + ctdb_kill(ctdb, pid, SIGKILL); + close(fd[0]); + return -1; + } + tevent_fd_set_auto_close(ectx->eventd_fde); + ectx->eventd_pid = pid; -args_failed: - DEBUG(DEBUG_ERR, (__location__ " too many arguments '%s' to eventscript '%s'\n", - options, ctdb_eventscript_call_names[call])); - -failed: - if (tmp) { - talloc_free(tmp); + /* Wait to connect to eventd */ + for (i=0; i<10; i++) { + status = eventd_client_connect(ectx); + if (status) { + break; + } + sleep(1); } - return false; -} - -static void ctdb_event_script_handler(struct tevent_context *ev, - struct tevent_fd *fde, - uint16_t flags, void *p); + if (! status) { + DEBUG(DEBUG_ERR, ("Failed to initialize event daemon\n")); + ctdb_stop_eventd(ctdb); + return -1; + } -static char helper_prog[PATH_MAX+1] = ""; + return 0; +} -static int fork_child_for_script(struct ctdb_context *ctdb, - struct ctdb_event_script_state *state) +static void eventd_dead_handler(struct tevent_context *ev, + struct tevent_fd *fde, uint16_t flags, + void *private_data) { - int r; - struct tevent_fd *fde; - struct ctdb_script *current = get_current_script(state); - int argc; - const char **argv; + struct eventd_context *ectx = talloc_get_type_abort( + private_data, struct eventd_context); - if (!ctdb_set_helper("event helper", helper_prog, sizeof(helper_prog), - "CTDB_EVENT_HELPER", - CTDB_HELPER_BINDIR, "ctdb_event_helper")) { - ctdb_die(ctdb, __location__ - " Unable to set event helper\n"); - } - - current->start = timeval_current(); - - r = pipe(state->fd); - if (r != 0) { - DEBUG(DEBUG_ERR, (__location__ " pipe failed for child eventscript process\n")); - return -errno; - } - - /* Arguments for helper */ - if (!child_helper_args(state, ctdb, state->call, state->options, current, - state->fd[1], &argc, &argv)) { - DEBUG(DEBUG_ERR, (__location__ " failed to create arguments for eventscript helper\n")); - r = -ENOMEM; - close(state->fd[0]); - close(state->fd[1]); - return r; - } - - if (!ctdb_vfork_with_logging(state, ctdb, current->name, - helper_prog, argc, argv, - log_event_script_output, - state, &state->child)) { - talloc_free(argv); - r = -errno; - close(state->fd[0]); - close(state->fd[1]); - return r; - } + DEBUG(DEBUG_ERR, ("Eventd went away\n")); - talloc_free(argv); + TALLOC_FREE(ectx->eventd_fde); + ectx->eventd_pid = -1; +} - close(state->fd[1]); - set_close_on_exec(state->fd[0]); +void ctdb_stop_eventd(struct ctdb_context *ctdb) +{ + struct eventd_context *ectx = ctdb->ectx; - /* Set ourselves up to be called when that's done. */ - fde = tevent_add_fd(ctdb->ev, state, state->fd[0], TEVENT_FD_READ, - ctdb_event_script_handler, state); - tevent_fd_set_auto_close(fde); + if (ectx == NULL) { + return; + } - return 0; + TALLOC_FREE(ectx->eventd_fde); + if (ectx->eventd_pid != -1) { + kill(ectx->eventd_pid, SIGTERM); + ectx->eventd_pid = -1; + } + TALLOC_FREE(ctdb->ectx); } /* - Summarize status of this run of scripts. + * Connect to event daemon */ -static int script_status(struct ctdb_script_list_old *scripts) + +struct eventd_client_state { + struct eventd_client_state *prev, *next; + + struct eventd_context *ectx; + void (*callback)(struct ctdb_event_reply *reply, void *private_data); + void *private_data; + + uint32_t reqid; + uint8_t *buf; + size_t buflen; +}; + +static void eventd_client_read(uint8_t *buf, size_t buflen, + void *private_data); +static int eventd_client_state_destructor(struct eventd_client_state *state); + +static bool eventd_client_connect(struct eventd_context *ectx) { - unsigned int i; + int fd; - for (i = 0; i < scripts->num_scripts; i++) { - switch (scripts->scripts[i].status) { - case -ENAMETOOLONG: - case -ENOENT: - case -ENOEXEC: - /* Disabled or missing; that's OK. */ - break; - case 0: - /* No problem. */ - break; - default: - return scripts->scripts[i].status; - } + if (ectx->queue != NULL) { + return true; } - /* All OK! */ - return 0; + fd = sock_connect(ectx->socket); + if (fd == -1) { + return false; + } + + ectx->queue = sock_queue_setup(ectx, ectx->ev, fd, + eventd_client_read, ectx); + if (ectx->queue == NULL) { + close(fd); + return false; + } + + return true; } -/* called when child is finished */ -static void ctdb_event_script_handler(struct tevent_context *ev, - struct tevent_fd *fde, - uint16_t flags, void *p) +static int eventd_client_write(struct eventd_context *ectx, + TALLOC_CTX *mem_ctx, + struct ctdb_event_request *request, + void (*callback)(struct ctdb_event_reply *reply, + void *private_data), + void *private_data) { - struct ctdb_event_script_state *state = - talloc_get_type(p, struct ctdb_event_script_state); - struct ctdb_script *current = get_current_script(state); - struct ctdb_context *ctdb = state->ctdb; - int r, status; + struct eventd_client_state *state; + int ret; - if (ctdb == NULL) { - DEBUG(DEBUG_ERR,("Eventscript finished but ctdb is NULL\n")); - return; + if (! eventd_client_connect(ectx)) { + return -1; } - r = sys_read(state->fd[0], ¤t->status, sizeof(current->status)); - if (r < 0) { - current->status = -errno; - } else if (r == 0) { - current->status = -EINTR; - } else if (r != sizeof(current->status)) { - current->status = -EIO; - } - - current->finished = timeval_current(); - /* valgrind gets overloaded if we run next script as it's still doing - * post-execution analysis, so kill finished child here. */ - if (ctdb->valgrinding) { - ctdb_kill(ctdb, state->child, SIGKILL); - } - - state->child = 0; - - status = script_status(state->scripts); - - /* Aborted or finished all scripts? We're done. */ - if (status != 0 || state->current+1 == state->scripts->num_scripts) { - if (status != 0) { - DEBUG(DEBUG_INFO, - ("Eventscript %s %s finished with state %d\n", - ctdb_eventscript_call_names[state->call], - state->options, status)); - } + state = talloc_zero(mem_ctx, struct eventd_client_state); + if (state == NULL) { + return -1; + } + + state->ectx = ectx; + state->callback = callback; + state->private_data = private_data; + state->reqid = reqid_new(ectx->idr, state); + if (state->reqid == REQID_INVALID) { talloc_free(state); - return; + return -1; } - /* Forget about that old fd. */ - talloc_free(fde); + talloc_set_destructor(state, eventd_client_state_destructor); - /* Next script! */ - state->current++; - current++; - current->status = fork_child_for_script(ctdb, state); - if (current->status != 0) { - /* This calls the callback. */ + ctdb_event_header_fill(&request->header, state->reqid); + state->buflen = ctdb_event_request_len(request); + state->buf = talloc_size(state, state->buflen); + if (state->buf == NULL) { talloc_free(state); + return -1; } -} -struct debug_hung_script_state { - struct ctdb_context *ctdb; - pid_t child; - enum ctdb_event call; -}; + ret = ctdb_event_request_push(request, state->buf, &state->buflen); + if (ret != 0) { + talloc_free(state); + return -1; + } -static int debug_hung_script_state_destructor(struct debug_hung_script_state *state) -{ - if (state->child) { - ctdb_kill(state->ctdb, state->child, SIGKILL); + ret = sock_queue_write(ectx->queue, state->buf, state->buflen); + if (ret != 0) { + talloc_free(state); + return -1; } - return 0; -} -static void debug_hung_script_timeout(struct tevent_context *ev, struct tevent_timer *te, - struct timeval t, void *p) -{ - struct debug_hung_script_state *state = - talloc_get_type(p, struct debug_hung_script_state); + DLIST_ADD(ectx->calls, state); - talloc_free(state); + return 0; } -static void debug_hung_script_done(struct tevent_context *ev, struct tevent_fd *fde, - uint16_t flags, void *p) +static int eventd_client_state_destructor(struct eventd_client_state *state) { - struct debug_hung_script_state *state = - talloc_get_type(p, struct debug_hung_script_state); + struct eventd_context *ectx = state->ectx; - talloc_free(state); + reqid_remove(ectx->idr, state->reqid); + DLIST_REMOVE(ectx->calls, state); + return 0; } -static void ctdb_run_debug_hung_script(struct ctdb_context *ctdb, struct debug_hung_script_state *state) +static void eventd_client_read(uint8_t *buf, size_t buflen, + void *private_data) { - pid_t pid; - const char * debug_hung_script = CTDB_ETCDIR "/debug-hung-script.sh"; - int fd[2]; - struct tevent_timer *ttimer; - struct tevent_fd *tfd; - const char **argv; - int i; + struct eventd_context *ectx = talloc_get_type_abort( + private_data, struct eventd_context); + struct eventd_client_state *state; + struct ctdb_event_reply *reply; + int ret; - if (pipe(fd) < 0) { - DEBUG(DEBUG_ERR,("Failed to create pipe fd for debug hung script\n")); + if (buf == NULL) { + /* connection lost */ + TALLOC_FREE(ectx->queue); return; } - if (getenv("CTDB_DEBUG_HUNG_SCRIPT") != NULL) { - debug_hung_script = getenv("CTDB_DEBUG_HUNG_SCRIPT"); + reply = talloc_zero(ectx, struct ctdb_event_reply); + if (reply == NULL) { + return; } - argv = talloc_array(state, const char *, 5); - - argv[0] = talloc_asprintf(argv, "%d", fd[1]); - argv[1] = talloc_strdup(argv, debug_hung_script); - argv[2] = talloc_asprintf(argv, "%d", state->child); - argv[3] = talloc_strdup(argv, ctdb_eventscript_call_names[state->call]); - argv[4] = NULL; - - for (i=0; i<4; i++) { - if (argv[i] == NULL) { - close(fd[0]); - close(fd[1]); - talloc_free(argv); - return; - } + ret = ctdb_event_reply_pull(buf, buflen, reply, reply); + if (ret != 0) { + D_ERR("Invalid packet received, ret=%d\n", ret); + talloc_free(reply); + return; } - - if (!ctdb_vfork_with_logging(state, ctdb, "Hung-script", - helper_prog, 5, argv, NULL, NULL, &pid)) { - DEBUG(DEBUG_ERR,("Failed to fork a child to track hung event script\n")); - talloc_free(argv); - close(fd[0]); - close(fd[1]); + if (buflen != reply->header.length) { + D_ERR("Packet size mismatch %zu != %"PRIu32"\n", + buflen, reply->header.length); + talloc_free(reply); return; } - talloc_free(argv); - close(fd[1]); - - ttimer = tevent_add_timer(ctdb->ev, state, - timeval_current_ofs(ctdb->tunable.script_timeout, 0), - debug_hung_script_timeout, state); - if (ttimer == NULL) { - close(fd[0]); + state = reqid_find(ectx->idr, reply->header.reqid, + struct eventd_client_state); + if (state == NULL) { + talloc_free(reply); return; } - tfd = tevent_add_fd(ctdb->ev, state, fd[0], TEVENT_FD_READ, - debug_hung_script_done, state); - if (tfd == NULL) { - talloc_free(ttimer); - close(fd[0]); + if (state->reqid != reply->header.reqid) { + talloc_free(reply); return; } - tevent_fd_set_auto_close(tfd); + + state = talloc_steal(reply, state); + state->callback(reply, state->private_data); + talloc_free(reply); } -/* called when child times out */ -static void ctdb_event_script_timeout(struct tevent_context *ev, - struct tevent_timer *te, - struct timeval t, void *p) -{ - struct ctdb_event_script_state *state = talloc_get_type(p, struct ctdb_event_script_state); - struct ctdb_context *ctdb = state->ctdb; - struct ctdb_script *current = get_current_script(state); - struct debug_hung_script_state *debug_state; - - DEBUG(DEBUG_ERR,("Event script '%s %s %s' timed out after %.1fs, pid: %d\n", - current->name, ctdb_eventscript_call_names[state->call], state->options, - timeval_elapsed(¤t->start), - state->child)); +/* + * Run an event + */ - /* ignore timeouts for these events */ - switch (state->call) { - case CTDB_EVENT_START_RECOVERY: - case CTDB_EVENT_RECOVERED: - case CTDB_EVENT_TAKE_IP: - case CTDB_EVENT_RELEASE_IP: - state->scripts->scripts[state->current].status = 0; - DEBUG(DEBUG_ERR,("Ignoring hung script for %s call %d\n", state->options, state->call)); - break; - default: - state->scripts->scripts[state->current].status = -ETIME; +struct eventd_client_run_state { + struct eventd_context *ectx; + void (*callback)(int result, void *private_data); + void *private_data; +}; + +static void eventd_client_run_done(struct ctdb_event_reply *reply, + void *private_data); + +static int eventd_client_run(struct eventd_context *ectx, + TALLOC_CTX *mem_ctx, + void (*callback)(int result, + void *private_data), + void *private_data, + enum ctdb_event event, + const char *arg_str, + uint32_t timeout) +{ + struct eventd_client_run_state *state; + struct ctdb_event_request request; + struct ctdb_event_request_run rdata; + int ret; + + state = talloc_zero(mem_ctx, struct eventd_client_run_state); + if (state == NULL) { + return -1; } - debug_state = talloc_zero(ctdb, struct debug_hung_script_state); - if (debug_state == NULL) { + state->ectx = ectx; + state->callback = callback; + state->private_data = private_data; + + rdata.event = event; + rdata.timeout = timeout; + rdata.arg_str = arg_str; + + request.rdata.command = CTDB_EVENT_COMMAND_RUN; + request.rdata.data.run = &rdata; + + ret = eventd_client_write(ectx, state, &request, + eventd_client_run_done, state); + if (ret != 0) { talloc_free(state); - return; + return ret; } - /* Save information useful for running debug hung script, so - * eventscript state can be freed. - */ - debug_state->ctdb = ctdb; - debug_state->child = state->child; - debug_state->call = state->call; + return 0; +} - /* This destructor will actually kill the hung event script */ - talloc_set_destructor(debug_state, debug_hung_script_state_destructor); +static void eventd_client_run_done(struct ctdb_event_reply *reply, + void *private_data) +{ + struct eventd_client_run_state *state = talloc_get_type_abort( + private_data, struct eventd_client_run_state); - state->child = 0; + state = talloc_steal(state->ectx, state); + state->callback(reply->rdata.result, state->private_data); talloc_free(state); - - ctdb_run_debug_hung_script(ctdb, debug_state); } /* - destroy an event script: kill it if ->child != 0. + * CTDB event script functions */ -static int event_script_destructor(struct ctdb_event_script_state *state) -{ - int status; - struct event_script_callback *callback; - if (state->child) { - DEBUG(DEBUG_ERR,(__location__ " Sending SIGTERM to child pid:%d\n", state->child)); +int ctdb_event_script_run(struct ctdb_context *ctdb, + TALLOC_CTX *mem_ctx, + void (*callback)(struct ctdb_context *ctdb, + int result, void *private_data), + void *private_data, + enum ctdb_event event, + const char *fmt, va_list ap) + PRINTF_ATTRIBUTE(6,0); - if (ctdb_kill(state->ctdb, state->child, SIGTERM) != 0) { - DEBUG(DEBUG_ERR,("Failed to kill child process for eventscript, errno %s(%d)\n", strerror(errno), errno)); - } +struct ctdb_event_script_run_state { + struct ctdb_context *ctdb; + void (*callback)(struct ctdb_context *ctdb, int result, + void *private_data); + void *private_data; + enum ctdb_event event; +}; + +static bool event_allowed_during_recovery(enum ctdb_event event); +static void ctdb_event_script_run_done(int result, void *private_data); +static bool check_options(enum ctdb_event call, const char *options); + +int ctdb_event_script_run(struct ctdb_context *ctdb, + TALLOC_CTX *mem_ctx, + void (*callback)(struct ctdb_context *ctdb, + int result, void *private_data), + void *private_data, + enum ctdb_event event, + const char *fmt, va_list ap) +{ + struct ctdb_event_script_run_state *state; + char *arg_str; + int ret; + + if ( (ctdb->recovery_mode != CTDB_RECOVERY_NORMAL) && + (! event_allowed_during_recovery(event)) ) { + DEBUG(DEBUG_ERR, + ("Refusing to run event '%s' while in recovery\n", + ctdb_eventscript_call_names[event])); } - /* If we were the current monitor, we no longer are. */ - if (state->ctdb->current_monitor == state) { - state->ctdb->current_monitor = NULL; + state = talloc_zero(mem_ctx, struct ctdb_event_script_run_state); + if (state == NULL) { + return -1; } - /* Save our scripts as the last executed status, if we have them. - * See ctdb_event_script_callback_v where we abort monitor event. */ - if (state->scripts) { - talloc_free(state->ctdb->last_status[state->call]); - state->ctdb->last_status[state->call] = state->scripts; - if (state->current < state->ctdb->last_status[state->call]->num_scripts) { - state->ctdb->last_status[state->call]->num_scripts = state->current+1; + state->ctdb = ctdb; + state->callback = callback; + state->private_data = private_data; + state->event = event; + + if (fmt != NULL) { + arg_str = talloc_vasprintf(state, fmt, ap); + if (arg_str == NULL) { + talloc_free(state); + return -1; } + } else { + arg_str = NULL; } - /* Use last status as result, or "OK" if none. */ - if (state->ctdb->last_status[state->call]) { - status = script_status(state->ctdb->last_status[state->call]); - } else { - status = 0; + if (! check_options(event, arg_str)) { + DEBUG(DEBUG_ERR, + ("Bad event script arguments '%s' for '%s'\n", + arg_str, ctdb_eventscript_call_names[event])); + talloc_free(arg_str); + return -1; } - state->ctdb->active_events--; - if (state->ctdb->active_events < 0) { - ctdb_fatal(state->ctdb, "Active events < 0"); - } - - /* This is allowed to free us; talloc will prevent double free anyway, - * but beware if you call this outside the destructor! - * the callback hangs off a different context so we walk the list - * of "active" callbacks until we find the one state points to. - * if we cant find it it means the callback has been removed. - */ - for (callback = state->ctdb->script_callbacks; callback != NULL; callback = callback->next) { - if (callback == state->callback) { - break; - } + ret = eventd_client_run(ctdb->ectx, state, + ctdb_event_script_run_done, state, + event, arg_str, ctdb->tunable.script_timeout); + if (ret != 0) { + talloc_free(state); + return ret; } - state->callback = NULL; + DEBUG(DEBUG_INFO, + (__location__ " Running event %s with arguments %s\n", + ctdb_eventscript_call_names[event], arg_str)); + + talloc_free(arg_str); + return 0; +} + +static void ctdb_event_script_run_done(int result, void *private_data) +{ + struct ctdb_event_script_run_state *state = talloc_get_type_abort( + private_data, struct ctdb_event_script_run_state); - if (callback) { - /* Make sure destructor doesn't free itself! */ - talloc_steal(NULL, callback); - callback->fn(state->ctdb, status, callback->private_data); - talloc_free(callback); + if (result == -ETIME) { + switch (state->event) { + case CTDB_EVENT_START_RECOVERY: + case CTDB_EVENT_RECOVERED: + case CTDB_EVENT_TAKE_IP: + case CTDB_EVENT_RELEASE_IP: + DEBUG(DEBUG_ERR, + ("Ignoring hung script for %s event\n", + ctdb_eventscript_call_names[state->event])); + result = 0; + break; + + default: + break; + } } - return 0; + state = talloc_steal(state->ctdb, state); + state->callback(state->ctdb, result, state->private_data); + talloc_free(state); } + static unsigned int count_words(const char *options) { unsigned int words = 0; + if (options == NULL) { + return 0; + } + options += strspn(options, " \t"); while (*options) { words++; @@ -693,226 +681,28 @@ } } -static int remove_callback(struct event_script_callback *callback) -{ - DLIST_REMOVE(callback->ctdb->script_callbacks, callback); - return 0; -} - -struct schedule_callback_state { - struct ctdb_context *ctdb; - void (*callback)(struct ctdb_context *, int, void *); - void *private_data; - int status; - struct tevent_immediate *im; -}; - -static void schedule_callback_handler(struct tevent_context *ctx, - struct tevent_immediate *im, - void *private_data) -{ - struct schedule_callback_state *state = - talloc_get_type_abort(private_data, - struct schedule_callback_state); - - if (state->callback != NULL) { - state->callback(state->ctdb, state->status, - state->private_data); - } - talloc_free(state); -} - -static int -schedule_callback_immediate(struct ctdb_context *ctdb, - void (*callback)(struct ctdb_context *, - int, void *), - void *private_data, - int status) -{ - struct schedule_callback_state *state; - struct tevent_immediate *im; - - state = talloc_zero(ctdb, struct schedule_callback_state); - if (state == NULL) { - DEBUG(DEBUG_ERR, (__location__ " out of memory\n")); - return -1; - } - im = tevent_create_immediate(state); - if (im == NULL) { - DEBUG(DEBUG_ERR, (__location__ " out of memory\n")); - talloc_free(state); - return -1; - } - - state->ctdb = ctdb; - state->callback = callback; - state->private_data = private_data; - state->status = status; - state->im = im; - - tevent_schedule_immediate(im, ctdb->ev, - schedule_callback_handler, state); - return 0; -} - -/* - run the event script in the background, calling the callback when - finished - */ -static int ctdb_event_script_callback_v(struct ctdb_context *ctdb, - const void *mem_ctx, - void (*callback)(struct ctdb_context *, int, void *), - void *private_data, - enum ctdb_event call, - const char *fmt, va_list ap) - PRINTF_ATTRIBUTE(6,0); - -static int ctdb_event_script_callback_v(struct ctdb_context *ctdb, - const void *mem_ctx, - void (*callback)(struct ctdb_context *, int, void *), - void *private_data, - enum ctdb_event call, - const char *fmt, va_list ap) +/* only specific events are allowed while in recovery */ +static bool event_allowed_during_recovery(enum ctdb_event event) { - struct ctdb_event_script_state *state; - - if (ctdb->recovery_mode != CTDB_RECOVERY_NORMAL) { - /* we guarantee that only some specifically allowed event scripts are run - while in recovery */ - const enum ctdb_event allowed_calls[] = { - CTDB_EVENT_INIT, - CTDB_EVENT_SETUP, - CTDB_EVENT_START_RECOVERY, - CTDB_EVENT_SHUTDOWN, - CTDB_EVENT_RELEASE_IP, - CTDB_EVENT_IPREALLOCATED, - }; - int i; - for (i=0;iactive_events > 0 && - ctdb->current_monitor == NULL) { - if (callback != NULL) { - callback(ctdb, -ECANCELED, private_data); - } - return 0; - } - - /* Kill off any running monitor events to run this event. */ - if (ctdb->current_monitor) { - struct ctdb_event_script_state *ms = talloc_get_type(ctdb->current_monitor, struct ctdb_event_script_state); - - /* Cancel current monitor callback state only if monitoring - * context ctdb->monitor->monitor_context has not been freed */ - if (ms->callback != NULL && !ctdb_stopped_monitoring(ctdb)) { - ms->callback->fn(ctdb, -ECANCELED, ms->callback->private_data); - talloc_free(ms->callback); - } - - /* Discard script status so we don't save to last_status */ - talloc_free(ctdb->current_monitor->scripts); - ctdb->current_monitor->scripts = NULL; - talloc_free(ctdb->current_monitor); - ctdb->current_monitor = NULL; - } - - state = talloc(ctdb->event_script_ctx, struct ctdb_event_script_state); - CTDB_NO_MEMORY(ctdb, state); - - /* The callback isn't done if the context is freed. */ - state->callback = talloc(mem_ctx, struct event_script_callback); - CTDB_NO_MEMORY(ctdb, state->callback); - DLIST_ADD(ctdb->script_callbacks, state->callback); - talloc_set_destructor(state->callback, remove_callback); - state->callback->ctdb = ctdb; - state->callback->fn = callback; - state->callback->private_data = private_data; - - state->ctdb = ctdb; - state->call = call; - state->options = talloc_vasprintf(state, fmt, ap); - state->timeout = timeval_set(ctdb->tunable.script_timeout, 0); - state->scripts = NULL; - if (state->options == NULL) { - DEBUG(DEBUG_ERR, (__location__ " could not allocate state->options\n")); - talloc_free(state); - return -1; - } - if (!check_options(state->call, state->options)) { - DEBUG(DEBUG_ERR, ("Bad eventscript options '%s' for '%s'\n", - state->options, - ctdb_eventscript_call_names[state->call])); - talloc_free(state); - return -1; - } - - DEBUG(DEBUG_INFO,(__location__ " Starting eventscript %s %s\n", - ctdb_eventscript_call_names[state->call], - state->options)); - - /* This is not a child of state, since we save it in destructor. */ - state->scripts = ctdb_get_script_list(ctdb, ctdb); - if (state->scripts == NULL) { - talloc_free(state); - return -1; - } - state->current = 0; - state->child = 0; + const enum ctdb_event allowed_events[] = { + CTDB_EVENT_INIT, + CTDB_EVENT_SETUP, + CTDB_EVENT_START_RECOVERY, + CTDB_EVENT_SHUTDOWN, + CTDB_EVENT_RELEASE_IP, + CTDB_EVENT_IPREALLOCATED, + }; + int i; - /* Nothing to do? */ - if (state->scripts->num_scripts == 0) { - int ret = schedule_callback_immediate(ctdb, callback, - private_data, 0); - talloc_free(state); - if (ret != 0) { - DEBUG(DEBUG_ERR, - ("Unable to schedule callback for 0 scripts\n")); - return 1; + for (i = 0; i < ARRAY_SIZE(allowed_events); i++) { + if (event == allowed_events[i]) { + return true; } - return 0; - } - - state->scripts->scripts[0].status = fork_child_for_script(ctdb, state); - if (state->scripts->scripts[0].status != 0) { - talloc_free(state); - return -1; - } - - if (call == CTDB_EVENT_MONITOR) { - ctdb->current_monitor = state; } - ctdb->active_events++; - - talloc_set_destructor(state, event_script_destructor); - - if (!timeval_is_zero(&state->timeout)) { - tevent_add_timer(ctdb->ev, state, - timeval_current_ofs(state->timeout.tv_sec, - state->timeout.tv_usec), - ctdb_event_script_timeout, state); - } else { - DEBUG(DEBUG_ERR, (__location__ " eventscript %s %s called with no timeout\n", - ctdb_eventscript_call_names[state->call], - state->options)); - } - - return 0; + return false; } - /* run the event script in the background, calling the callback when finished. If mem_ctx is freed, callback will never be called. @@ -928,24 +718,25 @@ int ret; va_start(ap, fmt); - ret = ctdb_event_script_callback_v(ctdb, mem_ctx, callback, private_data, call, fmt, ap); + ret = ctdb_event_script_run(ctdb, mem_ctx, callback, private_data, + call, fmt, ap); va_end(ap); return ret; } -struct callback_status { +struct ctdb_event_script_args_state { bool done; int status; }; -/* - called when ctdb_event_script() finishes - */ -static void event_script_callback(struct ctdb_context *ctdb, int status, void *private_data) +static void ctdb_event_script_args_done(struct ctdb_context *ctdb, + int status, void *private_data) { - struct callback_status *s = (struct callback_status *)private_data; + struct ctdb_event_script_args_state *s = + (struct ctdb_event_script_args_state *)private_data; + s->done = true; s->status = status; } @@ -959,253 +750,41 @@ { va_list ap; int ret; - struct callback_status status = { + struct ctdb_event_script_args_state state = { .status = -1, .done = false, }; va_start(ap, fmt); - ret = ctdb_event_script_callback_v(ctdb, ctdb, - event_script_callback, &status, call, fmt, ap); + ret = ctdb_event_script_run(ctdb, ctdb, + ctdb_event_script_args_done, &state, + call, fmt, ap); va_end(ap); if (ret != 0) { return ret; } - while (status.done == false && tevent_loop_once(ctdb->ev) == 0) /* noop */; - - if (status.status == -ETIME) { - DEBUG(DEBUG_ERR, (__location__ " eventscript for '%s' timed out." - " Immediately banning ourself for %d seconds\n", - ctdb_eventscript_call_names[call], - ctdb->tunable.recovery_ban_period)); + while (! state.done) { + tevent_loop_once(ctdb->ev); + } + if (state.status == -ETIME) { /* Don't ban self if CTDB is starting up or shutting down */ if (call != CTDB_EVENT_INIT && call != CTDB_EVENT_SHUTDOWN) { + DEBUG(DEBUG_ERR, + (__location__ " eventscript for '%s' timed out." + " Immediately banning ourself for %d seconds\n", + ctdb_eventscript_call_names[call], + ctdb->tunable.recovery_ban_period)); ctdb_ban_self(ctdb); } } - return status.status; + return state.status; } int ctdb_event_script(struct ctdb_context *ctdb, enum ctdb_event call) { /* GCC complains about empty format string, so use %s and "". */ - return ctdb_event_script_args(ctdb, call, "%s", ""); -} - -struct eventscript_callback_state { - struct ctdb_req_control_old *c; -}; - -/* - called when a forced eventscript run has finished - */ -static void run_eventscripts_callback(struct ctdb_context *ctdb, int status, - void *private_data) -{ - const char *errmsg = NULL; - - struct eventscript_callback_state *state = - talloc_get_type(private_data, struct eventscript_callback_state); - - if (status != 0) { - if (status == -ECANCELED) { - DEBUG(DEBUG_WARNING, - (__location__ " Eventscript cancelled\n")); - errmsg = "cancelled"; - } else { - DEBUG(DEBUG_ERR, - (__location__ " Failed to run eventscripts\n")); - } - } - - ctdb_request_control_reply(ctdb, state->c, NULL, status, errmsg); - /* This will free the struct ctdb_event_script_state we are in! */ - talloc_free(state); - return; -} - - -/* Returns rest of string, or NULL if no match. */ -static const char *get_call(const char *p, enum ctdb_event *call) -{ - unsigned int len; - - /* Skip any initial whitespace. */ - p += strspn(p, " \t"); - - /* See if we match any. */ - for (*call = 0; *call < CTDB_EVENT_MAX; (*call)++) { - len = strlen(ctdb_eventscript_call_names[*call]); - if (strncmp(p, ctdb_eventscript_call_names[*call], len) == 0) { - /* If end of string or whitespace, we're done. */ - if (strcspn(p + len, " \t") == 0) { - return p + len; - } - } - } - return NULL; -} - -/* - A control to force running of the eventscripts from the ctdb client tool -*/ -int32_t ctdb_run_eventscripts(struct ctdb_context *ctdb, - struct ctdb_req_control_old *c, - TDB_DATA indata, bool *async_reply) -{ - int ret; - struct eventscript_callback_state *state; - const char *options; - enum ctdb_event call; - - /* Figure out what call they want. */ - options = get_call((const char *)indata.dptr, &call); - if (!options) { - DEBUG(DEBUG_ERR, (__location__ " Invalid event name \"%s\"\n", (const char *)indata.dptr)); - return -1; - } - - if (ctdb->recovery_mode != CTDB_RECOVERY_NORMAL) { - DEBUG(DEBUG_ERR, (__location__ " Aborted running eventscript \"%s\" while in RECOVERY mode\n", indata.dptr)); - return -1; - } - - state = talloc(ctdb->event_script_ctx, struct eventscript_callback_state); - CTDB_NO_MEMORY(ctdb, state); - - state->c = NULL; - - DEBUG(DEBUG_NOTICE,("Running eventscripts with arguments %s\n", indata.dptr)); - - ret = ctdb_event_script_callback(ctdb, - ctdb, run_eventscripts_callback, state, - call, "%s", options); - - if (ret != 0) { - DEBUG(DEBUG_ERR,(__location__ " Failed to run eventscripts with arguments %s\n", indata.dptr)); - talloc_free(state); - return -1; - } - - /* tell ctdb_control.c that we will be replying asynchronously */ - *async_reply = true; - state->c = talloc_steal(state, c); - return 0; -} - - - -int32_t ctdb_control_enable_script(struct ctdb_context *ctdb, TDB_DATA indata) -{ - const char *script; - struct stat st; - char *filename; - TALLOC_CTX *tmp_ctx = talloc_new(ctdb); - - script = (char *)indata.dptr; - if (indata.dsize == 0) { - DEBUG(DEBUG_ERR,(__location__ " No script specified.\n")); - talloc_free(tmp_ctx); - return -1; - } - if (indata.dptr[indata.dsize - 1] != '\0') { - DEBUG(DEBUG_ERR,(__location__ " String is not null terminated.\n")); - talloc_free(tmp_ctx); - return -1; - } - if (index(script,'/') != NULL) { - DEBUG(DEBUG_ERR,(__location__ " Script name contains '/'. Failed to enable script %s\n", script)); - talloc_free(tmp_ctx); - return -1; - } - - - if (stat(ctdb->event_script_dir, &st) != 0 && - errno == ENOENT) { - DEBUG(DEBUG_CRIT,("No event script directory found at '%s'\n", ctdb->event_script_dir)); - talloc_free(tmp_ctx); - return -1; - } - - - filename = talloc_asprintf(tmp_ctx, "%s/%s", ctdb->event_script_dir, script); - if (filename == NULL) { - DEBUG(DEBUG_ERR,(__location__ " Failed to create script path\n")); - talloc_free(tmp_ctx); - return -1; - } - - if (stat(filename, &st) != 0) { - DEBUG(DEBUG_ERR,("Could not stat event script %s. Failed to enable script.\n", filename)); - talloc_free(tmp_ctx); - return -1; - } - - if (chmod(filename, st.st_mode | S_IXUSR) == -1) { - DEBUG(DEBUG_ERR,("Could not chmod %s. Failed to enable script.\n", filename)); - talloc_free(tmp_ctx); - return -1; - } - - talloc_free(tmp_ctx); - return 0; -} - -int32_t ctdb_control_disable_script(struct ctdb_context *ctdb, TDB_DATA indata) -{ - const char *script; - struct stat st; - char *filename; - TALLOC_CTX *tmp_ctx = talloc_new(ctdb); - - script = (char *)indata.dptr; - if (indata.dsize == 0) { - DEBUG(DEBUG_ERR,(__location__ " No script specified.\n")); - talloc_free(tmp_ctx); - return -1; - } - if (indata.dptr[indata.dsize - 1] != '\0') { - DEBUG(DEBUG_ERR,(__location__ " String is not null terminated.\n")); - talloc_free(tmp_ctx); - return -1; - } - if (index(script,'/') != NULL) { - DEBUG(DEBUG_ERR,(__location__ " Script name contains '/'. Failed to disable script %s\n", script)); - talloc_free(tmp_ctx); - return -1; - } - - - if (stat(ctdb->event_script_dir, &st) != 0 && - errno == ENOENT) { - DEBUG(DEBUG_CRIT,("No event script directory found at '%s'\n", ctdb->event_script_dir)); - talloc_free(tmp_ctx); - return -1; - } - - - filename = talloc_asprintf(tmp_ctx, "%s/%s", ctdb->event_script_dir, script); - if (filename == NULL) { - DEBUG(DEBUG_ERR,(__location__ " Failed to create script path\n")); - talloc_free(tmp_ctx); - return -1; - } - - if (stat(filename, &st) != 0) { - DEBUG(DEBUG_ERR,("Could not stat event script %s. Failed to disable script.\n", filename)); - talloc_free(tmp_ctx); - return -1; - } - - if (chmod(filename, st.st_mode & ~(S_IXUSR|S_IXGRP|S_IXOTH)) == -1) { - DEBUG(DEBUG_ERR,("Could not chmod %s. Failed to disable script.\n", filename)); - talloc_free(tmp_ctx); - return -1; - } - - talloc_free(tmp_ctx); - return 0; + return ctdb_event_script_args(ctdb, call, NULL); } diff -Nru samba-4.5.8+dfsg/ctdb/server/ipalloc.c samba-4.6.5+dfsg/ctdb/server/ipalloc.c --- samba-4.5.8+dfsg/ctdb/server/ipalloc.c 2016-10-24 19:37:30.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ipalloc.c 2017-01-11 07:55:14.000000000 +0000 @@ -29,6 +29,8 @@ #include "common/logging.h" #include "common/rb_tree.h" +#include "protocol/protocol_api.h" + #include "server/ipalloc_private.h" /* Initialise main ipalloc state and sub-structures */ @@ -36,7 +38,9 @@ ipalloc_state_init(TALLOC_CTX *mem_ctx, uint32_t num_nodes, enum ipalloc_algorithm algorithm, + bool no_ip_takeover, bool no_ip_failback, + bool no_ip_host_on_all_disabled, uint32_t *force_rebalance_nodes) { struct ipalloc_state *ipalloc_state = @@ -48,14 +52,6 @@ ipalloc_state->num = num_nodes; - ipalloc_state->noiptakeover = - talloc_zero_array(ipalloc_state, - bool, - ipalloc_state->num); - if (ipalloc_state->noiptakeover == NULL) { - DEBUG(DEBUG_ERR, (__location__ " Out of memory\n")); - goto fail; - } ipalloc_state->noiphost = talloc_zero_array(ipalloc_state, bool, @@ -66,7 +62,9 @@ } ipalloc_state->algorithm = algorithm; + ipalloc_state->no_ip_takeover = no_ip_takeover; ipalloc_state->no_ip_failback = no_ip_failback; + ipalloc_state->no_ip_host_on_all_disabled = no_ip_host_on_all_disabled; ipalloc_state->force_rebalance_nodes = force_rebalance_nodes; return ipalloc_state; @@ -160,6 +158,37 @@ return ip_list; } +static bool populate_bitmap(struct ipalloc_state *ipalloc_state) +{ + struct public_ip_list *ip = NULL; + int i, j; + + for (ip = ipalloc_state->all_ips; ip != NULL; ip = ip->next) { + + ip->available_on = talloc_zero_array(ip, bool, + ipalloc_state->num); + if (ip->available_on == NULL) { + return false; + } + + for (i = 0; i < ipalloc_state->num; i++) { + struct ctdb_public_ip_list *avail = + &ipalloc_state->available_public_ips[i]; + + /* Check to see if "ip" is available on node "i" */ + for (j = 0; j < avail->num; j++) { + if (ctdb_sock_addr_same_ip( + &ip->addr, &avail->ip[j].addr)) { + ip->available_on[i] = true; + break; + } + } + } + } + + return true; +} + static bool all_nodes_are_disabled(struct ctdb_node_map *nodemap) { int i; @@ -177,7 +206,6 @@ /* Set internal flags for IP allocation: * Clear ip flags - * Set NOIPTAKOVER ip flags from per-node NoIPTakeover tunable * Set NOIPHOST ip flag for each INACTIVE node * if all nodes are disabled: * Set NOIPHOST ip flags from per-node NoIPHostOnAllDisabled tunable @@ -185,39 +213,25 @@ * Set NOIPHOST ip flags for disabled nodes */ void ipalloc_set_node_flags(struct ipalloc_state *ipalloc_state, - struct ctdb_node_map *nodemap, - uint32_t *tval_noiptakeover, - uint32_t *tval_noiphostonalldisabled) + struct ctdb_node_map *nodemap) { int i; + bool all_disabled = all_nodes_are_disabled(nodemap); for (i=0;inum;i++) { - /* Can not take IPs on node with NoIPTakeover set */ - if (tval_noiptakeover[i] != 0) { - ipalloc_state->noiptakeover[i] = true; - } - /* Can not host IPs on INACTIVE node */ if (nodemap->node[i].flags & NODE_FLAGS_INACTIVE) { ipalloc_state->noiphost[i] = true; } - } - if (all_nodes_are_disabled(nodemap)) { - /* If all nodes are disabled, can not host IPs on node - * with NoIPHostOnAllDisabled set + /* If node is disabled then it can only host IPs if + * all nodes are disabled and NoIPHostOnAllDisabled is + * unset */ - for (i=0;inum;i++) { - if (tval_noiphostonalldisabled[i] != 0) { - ipalloc_state->noiphost[i] = true; - } - } - } else { - /* If some nodes are not disabled, then can not host - * IPs on DISABLED node - */ - for (i=0;inum;i++) { - if (nodemap->node[i].flags & NODE_FLAGS_DISABLED) { + if (nodemap->node[i].flags & NODE_FLAGS_DISABLED) { + if (!(all_disabled && + ipalloc_state->no_ip_host_on_all_disabled == 0)) { + ipalloc_state->noiphost[i] = true; } } @@ -283,6 +297,10 @@ return NULL; } + if (!populate_bitmap(ipalloc_state)) { + return NULL; + } + switch (ipalloc_state->algorithm) { case IPALLOC_LCP2: ret = ipalloc_lcp2(ipalloc_state); diff -Nru samba-4.5.8+dfsg/ctdb/server/ipalloc_common.c samba-4.6.5+dfsg/ctdb/server/ipalloc_common.c --- samba-4.5.8+dfsg/ctdb/server/ipalloc_common.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ipalloc_common.c 2017-01-11 07:55:14.000000000 +0000 @@ -61,33 +61,18 @@ int32_t pnn, struct public_ip_list *ip) { - struct ctdb_public_ip_list *public_ips; - int i; - if (ipalloc_state->noiphost[pnn]) { return false; } - if (ipalloc_state->available_public_ips == NULL) { - return false; - } - - public_ips = &ipalloc_state->available_public_ips[pnn]; - - for (i=0; inum; i++) { - if (ctdb_sock_addr_same(&ip->addr, &public_ips->ip[i].addr)) { - /* yes, this node can serve this public ip */ - return true; - } - } - return false; + return ip->available_on[pnn]; } bool can_node_takeover_ip(struct ipalloc_state *ipalloc_state, int32_t pnn, struct public_ip_list *ip) { - if (ipalloc_state->noiptakeover[pnn]) { + if (ipalloc_state->no_ip_takeover) { return false; } diff -Nru samba-4.5.8+dfsg/ctdb/server/ipalloc_deterministic.c samba-4.6.5+dfsg/ctdb/server/ipalloc_deterministic.c --- samba-4.5.8+dfsg/ctdb/server/ipalloc_deterministic.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ipalloc_deterministic.c 2017-01-11 07:55:14.000000000 +0000 @@ -49,7 +49,8 @@ * back IPs to their "home" node. */ if (ipalloc_state->no_ip_failback) { - DEBUG(DEBUG_WARNING, ("WARNING: 'NoIPFailback' set but ignored - incompatible with 'DeterministicIPs\n")); + D_WARNING("WARNING: 'NoIPFailback' set but ignored - " + "incompatible with 'Deterministic IPs\n"); } unassign_unsuitable_ips(ipalloc_state); diff -Nru samba-4.5.8+dfsg/ctdb/server/ipalloc.h samba-4.6.5+dfsg/ctdb/server/ipalloc.h --- samba-4.5.8+dfsg/ctdb/server/ipalloc.h 2016-10-24 19:37:30.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ipalloc.h 2017-01-11 07:55:14.000000000 +0000 @@ -31,6 +31,7 @@ struct public_ip_list *next; uint32_t pnn; ctdb_sock_addr addr; + bool *available_on; }; #define IP_KEYLEN 4 @@ -48,13 +49,13 @@ struct ipalloc_state * ipalloc_state_init(TALLOC_CTX *mem_ctx, uint32_t num_nodes, enum ipalloc_algorithm algorithm, + bool no_ip_takeover, bool no_ip_failback, + bool no_ip_host_on_all_disabled, uint32_t *force_rebalance_nodes); void ipalloc_set_node_flags(struct ipalloc_state *ipalloc_state, - struct ctdb_node_map *nodemap, - uint32_t *tval_noiptakeover, - uint32_t *tval_noiphostonalldisabled); + struct ctdb_node_map *nodemap); void ipalloc_set_public_ips(struct ipalloc_state *ipalloc_state, struct ctdb_public_ip_list *known_ips, diff -Nru samba-4.5.8+dfsg/ctdb/server/ipalloc_private.h samba-4.6.5+dfsg/ctdb/server/ipalloc_private.h --- samba-4.5.8+dfsg/ctdb/server/ipalloc_private.h 2016-10-24 19:37:30.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/server/ipalloc_private.h 2017-01-11 07:55:14.000000000 +0000 @@ -32,12 +32,13 @@ /* Arrays with data for each node */ struct ctdb_public_ip_list *available_public_ips; struct ctdb_public_ip_list *known_public_ips; - bool *noiptakeover; bool *noiphost; struct public_ip_list *all_ips; enum ipalloc_algorithm algorithm; bool no_ip_failback; + bool no_ip_takeover; + bool no_ip_host_on_all_disabled; uint32_t *force_rebalance_nodes; }; diff -Nru samba-4.5.8+dfsg/ctdb/tests/complex/35_cifs_external_tickle.sh samba-4.6.5+dfsg/ctdb/tests/complex/35_cifs_external_tickle.sh --- samba-4.5.8+dfsg/ctdb/tests/complex/35_cifs_external_tickle.sh 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/complex/35_cifs_external_tickle.sh 2017-01-11 07:55:14.000000000 +0000 @@ -55,15 +55,15 @@ my_exit_hook () { - onnode -q all $CTDB enablescript "10.interface" - onnode -q all $CTDB disablescript "10.external" + onnode -q all $CTDB event script enable "10.interface" + onnode -q all $CTDB event script disable "10.external" } ctdb_test_exit_hook_add my_exit_hook echo "Disable 10.interface on all nodes" -try_command_on_node all $CTDB disablescript 10.interface +try_command_on_node all $CTDB event script disable 10.interface echo "Enable 10.external on all nodes" -try_command_on_node all $CTDB enablescript 10.external +try_command_on_node all $CTDB event script enable 10.external test_port=445 diff -Nru samba-4.5.8+dfsg/ctdb/tests/complex/60_rogueip_releaseip.sh samba-4.6.5+dfsg/ctdb/tests/complex/60_rogueip_releaseip.sh --- samba-4.5.8+dfsg/ctdb/tests/complex/60_rogueip_releaseip.sh 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/complex/60_rogueip_releaseip.sh 2017-01-11 07:55:14.000000000 +0000 @@ -0,0 +1,59 @@ +#!/bin/bash + +test_info() +{ + cat <] .*sleep+.* ---- ctdb scriptstatus monitor: ---- -[0-9]* scripts were executed last monitor cycle -99\\.timeout *Status:TIMEDOUT.* - *OUTPUT:sleeping for [0-9]* seconds\\.\\.\\. +99\\.timeout *TIMEDOUT.* + *OUTPUT: sleeping for [0-9]* seconds\\.\\.\\. EOF diff -Nru samba-4.5.8+dfsg/ctdb/tests/cunit/protocol_test_002.sh samba-4.6.5+dfsg/ctdb/tests/cunit/protocol_test_002.sh --- samba-4.5.8+dfsg/ctdb/tests/cunit/protocol_test_002.sh 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/cunit/protocol_test_002.sh 2017-01-11 07:55:14.000000000 +0000 @@ -11,6 +11,15 @@ echo ) +last_command=5 + +command_output=$( + for i in $(seq 1 $last_command) ; do + echo -n "$i.. " + done + echo +) + output=$( echo "ctdb_req_header" echo "ctdb_req_call" @@ -27,6 +36,15 @@ echo "ctdb_reply_control" echo "$control_output" echo "ctdb_req_message" + echo "ctdb_event_header" + echo "ctdb_event_request_data" + echo "$command_output" + echo "ctdb_event_reply_data" + echo "$command_output" + echo "ctdb_event_request" + echo "$command_output" + echo "ctdb_event_reply" + echo "$command_output" ) ok "$output" diff -Nru samba-4.5.8+dfsg/ctdb/tests/cunit/run_proc_001.sh samba-4.6.5+dfsg/ctdb/tests/cunit/run_proc_001.sh --- samba-4.5.8+dfsg/ctdb/tests/cunit/run_proc_001.sh 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/cunit/run_proc_001.sh 2017-01-11 07:55:14.000000000 +0000 @@ -0,0 +1,140 @@ +#!/bin/sh + +. "${TEST_SCRIPTS_DIR}/unit.sh" + +# Invalid path +ok < "$prog" < "$prog" < "$prog" <"$output" 2>&1 +echo hello +EOF + +ok < "$prog" < "$prog" < "$prog" < "$prog" < "$pidfile" +sleep 10 +EOF + +ok < "$eventd_scriptdir/a.sh" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/debug.sh" < "$eventd_debug" +EOF +chmod +x "$eventd_scriptdir/debug.sh" + +setup_eventd "$eventd_scriptdir/debug.sh" + +result_filter () +{ + _pid="[0-9][0-9]*" + sed -e "s| ${_pid}| PID|" +} + +required_result 62 < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/debug.sh" < "$eventd_debug" +EOF +chmod +x "$eventd_scriptdir/debug.sh" + +setup_eventd "$eventd_scriptdir/debug.sh" + +required_result 62 < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/debug.sh" <"$eventd_debug" 2>&1 + +ctdb_event "$eventd_socket" status monitor lastrun +EOF +chmod +x "$eventd_scriptdir/debug.sh" + +setup_eventd "$eventd_scriptdir/debug.sh" + +required_result 62 < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/debug.sh" < "$eventd_debug" +EOF +chmod +x "$eventd_scriptdir/debug.sh" + +setup_eventd "$eventd_scriptdir/debug.sh" + +result_filter() +{ + _pid="[0-9][0-9]*" + sed -e "s|${_pid}|PID|" +} + +required_result 62 < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/02.test" < "$eventd_scriptdir/03.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/02.test" < "$eventd_scriptdir/03.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" </dev/null || echo) + if [ -n "$pid" ] ; then + kill $pid || true + rm -f "$eventd_pidfile" + fi + rm -f "$eventd_socket" + rm -f "$eventd_logfile" + rm -f "$eventd_debug" + rm -rf "$eventd_scriptdir" +} + +setup_eventd () +{ + debug "Setting up eventd" + + if [ -n "$1" ]; then + extra_args="-D $1" + fi + + $VALGRIND ctdb_eventd -s "$eventd_socket" \ + -p "$eventd_pidfile" \ + -e "$eventd_scriptdir" \ + -l "file:" -d "DEBUG" $extra_args 2>&1 | tee "$eventd_logfile" & + # Wait till eventd is running + while [ ! -S "$eventd_socket" ] ; do + sleep 1 + done + + test_cleanup cleanup_eventd +} + +simple_test_background () +{ + background_log=$(mktemp --tmpdir="$TEST_VAR_DIR") + background_status=$(mktemp --tmpdir="$TEST_VAR_DIR") + background_running=1 + + ( + (unit_test ctdb_event "$eventd_socket" "$@") \ + > "$background_log" 2>&1 + echo $? > "$background_status" + ) & + background_pid=$! +} + +background_wait () +{ + [ -n "$background_running" ] || return + + count=0 + while [ ! -s "$background_status" -a $count -lt 30 ] ; do + count=$(( $count + 1 )) + sleep 1 + done + + if [ ! -s "$background_status" ] ; then + kill -9 "$background_pid" + echo TIMEOUT > "$background_status" + fi +} + +background_output () +{ + [ -n "$background_running" ] || return + + bg_status=$(cat "$background_status") + rm -f "$background_status" + echo "--- Background ---" + if [ "$bg_status" = "TIMEOUT" ] ; then + echo "Background process did not complete" + bg_status=1 + else + cat "$background_log" + rm -f "$background_log" + fi + echo "--- Background ---" + unset background_running + [ $bg_status -eq 0 ] || exit $bg_status +} + +simple_test () +{ + (unit_test ctdb_event "$eventd_socket" "$@") + status=$? + + background_wait + background_output + + [ $status -eq 0 ] || exit $status +} + +result_filter () +{ + _duration="\<[0-9][0-9]*\.[0-9][0-9][0-9]\>" + _day="\(Mon\|Tue\|Wed\|Thu\|Fri\|Sat\|Sun\)" + _month="\(Jan\|Feb\|Mar\|Apr\|May\|Jun\|Jul\|Aug\|Sep\|Oct\|Nov\|Dec\)" + _date="\( [0-9]\|[0-9][0-9]\)" + _time="[0-9][0-9]:[0-9][0-9]:[0-9][0-9]" + _year="[0-9][0-9][0-9][0-9]" + _datetime="${_day} ${_month} ${_date} ${_time} ${_year}" + _pid="[0-9][0-9]*" + sed -e "s#${_duration}#DURATION#" \ + -e "s#${_datetime}#DATETIME#" \ + -e "s#,${_pid}#,PID#" \ + -e "s#\[${_pid}\]#[PID]#" +} diff -Nru samba-4.5.8+dfsg/ctdb/tests/eventscripts/50.samba.monitor.107.sh samba-4.6.5+dfsg/ctdb/tests/eventscripts/50.samba.monitor.107.sh --- samba-4.5.8+dfsg/ctdb/tests/eventscripts/50.samba.monitor.107.sh 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/eventscripts/50.samba.monitor.107.sh 2017-01-11 07:55:14.000000000 +0000 @@ -4,7 +4,7 @@ define_test "port 139 down, default tcp checker, debug" -export CTDB_SCRIPT_DEBUGLEVEL=4 +export CTDB_SCRIPT_DEBUGLEVEL=DEBUG setup_samba tcp_port_down 139 diff -Nru samba-4.5.8+dfsg/ctdb/tests/eventscripts/50.samba.shutdown.001.sh samba-4.6.5+dfsg/ctdb/tests/eventscripts/50.samba.shutdown.001.sh --- samba-4.5.8+dfsg/ctdb/tests/eventscripts/50.samba.shutdown.001.sh 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/eventscripts/50.samba.shutdown.001.sh 2017-01-11 07:55:14.000000000 +0000 @@ -0,0 +1,12 @@ +#!/bin/sh + +. "${TEST_SCRIPTS_DIR}/unit.sh" + +define_test "shutdown, simple" + +setup_samba + +ok < replay error" - -setup_nfs - -public_address=$(ctdb_get_1_public_address) - -err="foo: bar error occurred" - -ok_null - -simple_test_event "takeip" $public_address - -ctdb_fake_scriptstatus 1 "ERROR" "$err" - -eventscript_call ctdb_reconfigure_take_lock - -required_result 1 < reconfigure, replay timed out" - -setup_nfs - -public_address=$(ctdb_get_1_public_address) - -err="waiting, waiting..." - -ok_null - -simple_test_event "takeip" $public_address - -ctdb_fake_scriptstatus -62 "TIMEDOUT" "$err" - -eventscript_call ctdb_reconfigure_take_lock - -required_result 1 < reconfigure, replay disabled" - -setup_nfs - -public_address=$(ctdb_get_1_public_address) - -err="" - -ok_null - -simple_test_event "takeip" $public_address - -ctdb_fake_scriptstatus -8 "DISABLED" "$err" - -eventscript_call ctdb_reconfigure_take_lock - -ok </dev/null 2>&1 || _failed=true - echo "Tickle TCP connection $dest $src" - $CTDB tickle "$dest" "$src" >/dev/null 2>&1 || _failed=true - done + # Get connections, both directions + _conns=$(get_tcp_connections_for_ip "$_ip" | \ + awk '{ print $1, $2 ; print $2, $1 }') - if $_failed ; then - echo "Failed to send tickle control" - fi - } + echo "$_conns" | awk '{ print "Tickle TCP connection", $1, $2 }' + echo "$_conns" | ctdb tickle } get_tcp_connections_for_ip () @@ -862,139 +853,6 @@ : } -ctdb_reconfigure_take_lock () -{ - _ctdb_service_reconfigure_common - _lock="${_d}/reconfigure_lock" - mkdir -p "${_lock%/*}" # dirname - touch "$_lock" - - ( - flock 9 - # This is overkill but will work if we need to extend - # this to allow certain events to run multiple times - # in parallel (e.g. takeip) and write multiple PIDs to - # the file. - { - read _locker_event - if [ -n "$_locker_event" ] ; then - while read _pid ; do - if [ -n "$_pid" -a "$_pid" != $$ ] && \ - kill -0 "$_pid" 2>/dev/null ; then - exit 1 - fi - done - fi - } <"$_lock" - - printf "%s\n%s\n" "$event_name" $$ >"$_lock" - exit 0 - ) 9>"${_lock}.flock" -} - -ctdb_reconfigure_release_lock () -{ - _ctdb_service_reconfigure_common - _lock="${_d}/reconfigure_lock" - - rm -f "$_lock" -} - -ctdb_replay_monitor_status () -{ - echo "Replaying previous status for this script due to reconfigure..." - # Leading separator ('|') is missing in some versions... - _out=$($CTDB scriptstatus -X | grep -E "^\|?monitor\|${script_name}\|") - # Output looks like this: - # |monitor|60.nfs|1|ERROR|1314764004.030861|1314764004.035514|foo bar| - # This is the cheapest way of getting fields in the middle. - # Intentional word splitting here - # shellcheck disable=SC2046,2086 - set -- $(IFS="|" ; echo $_out) - _code="$3" - _status="$4" - # The error output field can include colons so we'll try to - # preserve them. The weak checking at the beginning tries to make - # this work for both broken (no leading '|') and fixed output. - _out="${_out%|}" - _err_out="${_out#*monitor|${script_name}|*|*|*|*|}" - case "$_status" in - OK) : ;; # Do nothing special. - TIMEDOUT) - # Recast this as an error, since we can't exit with the - # correct negative number. - _code=1 - _err_out="[Replay of TIMEDOUT scriptstatus - note incorrect return code.] ${_err_out}" - ;; - DISABLED) - # Recast this as an OK, since we can't exit with the - # correct negative number. - _code=0 - _err_out="[Replay of DISABLED scriptstatus - note incorrect return code.] ${_err_out}" - ;; - *) : ;; # Must be ERROR, do nothing special. - esac - if [ -n "$_err_out" ] ; then - echo "$_err_out" - fi - exit $_code -} - -ctdb_service_check_reconfigure () -{ - assert_service_name - - # We only care about some events in this function. For others we - # return now. - case "$event_name" in - monitor|ipreallocated|reconfigure) : ;; - *) return 0 ;; - esac - - if ctdb_reconfigure_take_lock ; then - # No events covered by this function are running, so proceed - # with gay abandon. - case "$event_name" in - reconfigure) - (ctdb_service_reconfigure) - exit $? - ;; - ipreallocated) - if ctdb_service_needs_reconfigure ; then - ctdb_service_reconfigure - fi - ;; - esac - - ctdb_reconfigure_release_lock - else - # Somebody else is running an event we don't want to collide - # with. We proceed with caution. - case "$event_name" in - reconfigure) - # Tell whoever called us to retry. - exit 2 - ;; - ipreallocated) - # Defer any scheduled reconfigure and just run the - # rest of the ipreallocated event, as per the - # eventscript. There's an assumption here that the - # event doesn't depend on any scheduled reconfigure. - # This is true in the current code. - return 0 - ;; - monitor) - # There is most likely a reconfigure in progress so - # the service is possibly unstable. As above, we - # defer any scheduled reconfigured. We also replay - # the previous monitor status since that's the best - # information we have. - ctdb_replay_monitor_status - ;; - esac - fi -} - ################################################################## # Does CTDB manage this service? - and associated auto-start/stop @@ -1276,16 +1134,12 @@ sort >"$_my_tickles" # Add tickles for connections that we haven't already got tickles for - comm -23 "$_my_connections" "$_my_tickles" | - while read _src _dst ; do - $CTDB addtickle "$_src" "$_dst" - done + comm -23 "$_my_connections" "$_my_tickles" | \ + $CTDB addtickle # Remove tickles for connections that are no longer there - comm -13 "$_my_connections" "$_my_tickles" | - while read _src _dst ; do - $CTDB deltickle "$_src" "$_dst" - done + comm -13 "$_my_connections" "$_my_tickles" | \ + $CTDB deltickle rm -f "$_my_connections" "$_my_tickles" diff -Nru samba-4.5.8+dfsg/ctdb/tests/eventscripts/scripts/local.sh samba-4.6.5+dfsg/ctdb/tests/eventscripts/scripts/local.sh --- samba-4.5.8+dfsg/ctdb/tests/eventscripts/scripts/local.sh 2016-12-05 08:18:44.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/eventscripts/scripts/local.sh 2017-01-11 07:55:14.000000000 +0000 @@ -839,6 +839,23 @@ fi } +samba_setup_fake_threads () +{ + export FAKE_SMBD_THREAD_PIDS="$*" + + _nl=" +" + _out="" + _count=0 + for _pid ; do + [ "$_count" -lt 5 ] || break + _t=$(program_stack_trace "smbd" $_pid) + _out="${_out:+${_out}${_nl}}${_t}" + _count=$((_count + 1)) + done + SAMBA_STACK_TRACES="$_out" +} + setup_winbind () { setup_ctdb @@ -999,6 +1016,17 @@ esac } +program_stack_trace () +{ + _prog="$1" + _pid="$2" + + cat <] fake_stack_trace_for_pid_${_pid}/stack+0x0/0xff +EOF +} + program_stack_traces () { _prog="$1" @@ -1008,10 +1036,7 @@ for _pid in ${FAKE_NFSD_THREAD_PIDS:-$FAKE_RPC_THREAD_PIDS} ; do [ $_count -le $_max ] || break - cat <] fake_stack_trace_for_pid_${_pid}/stack+0x0/0xff -EOF + program_stack_trace "$_prog" "$_pid" _count=$(($_count + 1)) done } diff -Nru samba-4.5.8+dfsg/ctdb/tests/eventscripts/stubs/ctdb samba-4.6.5+dfsg/ctdb/tests/eventscripts/stubs/ctdb --- samba-4.5.8+dfsg/ctdb/tests/eventscripts/stubs/ctdb 2017-01-30 09:56:26.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/eventscripts/stubs/ctdb 2017-01-26 12:19:08.000000000 +0000 @@ -59,6 +59,55 @@ touch "$tickles_file" } +ctdb_gettickles () +{ + _ip="$1" + _port="$2" + + setup_tickles + + echo "|source ip|port|destination ip|port|" + while read _src _dst ; do + if [ -z "$_ip" -o "$_ip" = "${_dst%:*}" ] ; then + if [ -z "$_port" -o "$_port" = "${_dst##*:}" ] ; then + echo "|${_src%:*}|${_src##*:}|${_dst%:*}|${_dst##*:}|" + fi + fi + done <"$tickles_file" +} + +ctdb_addtickle () +{ + _src="$1" + _dst="$2" + + setup_tickles + + if [ -n "$_dst" ] ; then + echo "${_src} ${_dst}" >>"$tickles_file" + else + cat >>"$tickles_file" + fi +} + +ctdb_deltickle () +{ + _src="$1" + _dst="$2" + + setup_tickles + + if [ -n "$_dst" ] ; then + _t=$(grep -F -v "${_src} $${_dst}" "$tickles_file") + else + _t=$(cat "$tickles_file") + while read _src _dst ; do + _t=$(echo "$_t" | grep -F -v "${_src} ${_dst}") + done + fi + echo "$_t" >"$tickles_file" +} + parse_nodespec () { if [ "$nodespec" = "all" ] ; then @@ -125,7 +174,7 @@ fi _flags="${_flags}${_flags:+,}${_this}" done - CTDB_TEST_LOGLEVEL=2 \ + CTDB_TEST_LOGLEVEL=NOTICE \ "ctdb_takeover_tests" \ "ipalloc" "$_flags" <"$FAKE_CTDB_IP_LAYOUT" | sort >"$_t" @@ -358,22 +407,9 @@ ###################################################################### case "$1" in - gettickles) - setup_tickles - echo "|source ip|port|destination ip|port|" - while read src dst ; do - echo "|${src}|${dst}|" - done <"$tickles_file" - ;; - addtickle) - setup_tickles - echo "$2 $3" >>"$tickles_file" - ;; - deltickle) - setup_tickles - _t=$(grep -F -v "$2 $3" "$tickles_file") - echo "$_t" >"$tickles_file" - ;; + gettickles) shift ; ctdb_gettickles "$@" ;; + addtickle) shift ; ctdb_addtickle "$@" ;; + deltickle) shift ; ctdb_deltickle "$@" ;; pstore) ctdb_pstore "$@" ;; pdelete) ctdb_pdelete "$@" ;; pfetch) ctdb_pfetch "$@" ;; diff -Nru samba-4.5.8+dfsg/ctdb/tests/eventscripts/stubs/pidof samba-4.6.5+dfsg/ctdb/tests/eventscripts/stubs/pidof --- samba-4.5.8+dfsg/ctdb/tests/eventscripts/stubs/pidof 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/eventscripts/stubs/pidof 2017-01-11 07:55:14.000000000 +0000 @@ -1,12 +1,15 @@ #!/bin/sh case "$1" in - nfsd) +nfsd) echo "$FAKE_NFSD_THREAD_PIDS" ;; - rpc.statd|rpc.rquotad|rpc.mountd) +rpc.statd|rpc.rquotad|rpc.mountd) echo "$FAKE_RPC_THREAD_PIDS" ;; +smbd) + echo "$FAKE_SMBD_THREAD_PIDS" + ;; *) echo "pidof: \"$1\" not implemented" exit 1 diff -Nru samba-4.5.8+dfsg/ctdb/tests/onnode/functions samba-4.6.5+dfsg/ctdb/tests/onnode/functions --- samba-4.5.8+dfsg/ctdb/tests/onnode/functions 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/onnode/functions 2017-01-11 07:55:14.000000000 +0000 @@ -101,7 +101,7 @@ # configuration file. debug () { - if [ "${CTDB_SCRIPT_DEBUGLEVEL:-2}" -ge 4 ] ; then + if [ "${CTDB_SCRIPT_DEBUGLEVEL:-NOTICE}" = "DEBUG" ] ; then # If there are arguments then echo them. Otherwise expect to # use stdin, which allows us to pass lots of debug using a # here document. @@ -520,21 +520,12 @@ { _ip="$1" - get_tcp_connections_for_ip "$_ip" | - { - _failed=false - - while read dest src; do - echo "Tickle TCP connection $src $dest" - $CTDB tickle "$src" "$dest" >/dev/null 2>&1 || _failed=true - echo "Tickle TCP connection $dest $src" - $CTDB tickle "$dest" "$src" >/dev/null 2>&1 || _failed=true - done + # Get connections, both directions + _conns=$(get_tcp_connections_for_ip "$_ip" | \ + awk '{ print $1, $2 ; print $2, $1 }') - if $_failed ; then - echo "Failed to send tickle control" - fi - } + echo "$_conns" | awk '{ print "Tickle TCP connection", $1, $2 }' + echo "$_conns" | ctdb tickle } get_tcp_connections_for_ip () @@ -862,139 +853,6 @@ : } -ctdb_reconfigure_take_lock () -{ - _ctdb_service_reconfigure_common - _lock="${_d}/reconfigure_lock" - mkdir -p "${_lock%/*}" # dirname - touch "$_lock" - - ( - flock 9 - # This is overkill but will work if we need to extend - # this to allow certain events to run multiple times - # in parallel (e.g. takeip) and write multiple PIDs to - # the file. - { - read _locker_event - if [ -n "$_locker_event" ] ; then - while read _pid ; do - if [ -n "$_pid" -a "$_pid" != $$ ] && \ - kill -0 "$_pid" 2>/dev/null ; then - exit 1 - fi - done - fi - } <"$_lock" - - printf "%s\n%s\n" "$event_name" $$ >"$_lock" - exit 0 - ) 9>"${_lock}.flock" -} - -ctdb_reconfigure_release_lock () -{ - _ctdb_service_reconfigure_common - _lock="${_d}/reconfigure_lock" - - rm -f "$_lock" -} - -ctdb_replay_monitor_status () -{ - echo "Replaying previous status for this script due to reconfigure..." - # Leading separator ('|') is missing in some versions... - _out=$($CTDB scriptstatus -X | grep -E "^\|?monitor\|${script_name}\|") - # Output looks like this: - # |monitor|60.nfs|1|ERROR|1314764004.030861|1314764004.035514|foo bar| - # This is the cheapest way of getting fields in the middle. - # Intentional word splitting here - # shellcheck disable=SC2046,2086 - set -- $(IFS="|" ; echo $_out) - _code="$3" - _status="$4" - # The error output field can include colons so we'll try to - # preserve them. The weak checking at the beginning tries to make - # this work for both broken (no leading '|') and fixed output. - _out="${_out%|}" - _err_out="${_out#*monitor|${script_name}|*|*|*|*|}" - case "$_status" in - OK) : ;; # Do nothing special. - TIMEDOUT) - # Recast this as an error, since we can't exit with the - # correct negative number. - _code=1 - _err_out="[Replay of TIMEDOUT scriptstatus - note incorrect return code.] ${_err_out}" - ;; - DISABLED) - # Recast this as an OK, since we can't exit with the - # correct negative number. - _code=0 - _err_out="[Replay of DISABLED scriptstatus - note incorrect return code.] ${_err_out}" - ;; - *) : ;; # Must be ERROR, do nothing special. - esac - if [ -n "$_err_out" ] ; then - echo "$_err_out" - fi - exit $_code -} - -ctdb_service_check_reconfigure () -{ - assert_service_name - - # We only care about some events in this function. For others we - # return now. - case "$event_name" in - monitor|ipreallocated|reconfigure) : ;; - *) return 0 ;; - esac - - if ctdb_reconfigure_take_lock ; then - # No events covered by this function are running, so proceed - # with gay abandon. - case "$event_name" in - reconfigure) - (ctdb_service_reconfigure) - exit $? - ;; - ipreallocated) - if ctdb_service_needs_reconfigure ; then - ctdb_service_reconfigure - fi - ;; - esac - - ctdb_reconfigure_release_lock - else - # Somebody else is running an event we don't want to collide - # with. We proceed with caution. - case "$event_name" in - reconfigure) - # Tell whoever called us to retry. - exit 2 - ;; - ipreallocated) - # Defer any scheduled reconfigure and just run the - # rest of the ipreallocated event, as per the - # eventscript. There's an assumption here that the - # event doesn't depend on any scheduled reconfigure. - # This is true in the current code. - return 0 - ;; - monitor) - # There is most likely a reconfigure in progress so - # the service is possibly unstable. As above, we - # defer any scheduled reconfigured. We also replay - # the previous monitor status since that's the best - # information we have. - ctdb_replay_monitor_status - ;; - esac - fi -} - ################################################################## # Does CTDB manage this service? - and associated auto-start/stop @@ -1276,16 +1134,12 @@ sort >"$_my_tickles" # Add tickles for connections that we haven't already got tickles for - comm -23 "$_my_connections" "$_my_tickles" | - while read _src _dst ; do - $CTDB addtickle "$_src" "$_dst" - done + comm -23 "$_my_connections" "$_my_tickles" | \ + $CTDB addtickle # Remove tickles for connections that are no longer there - comm -13 "$_my_connections" "$_my_tickles" | - while read _src _dst ; do - $CTDB deltickle "$_src" "$_dst" - done + comm -13 "$_my_connections" "$_my_tickles" | \ + $CTDB deltickle rm -f "$_my_connections" "$_my_tickles" diff -Nru samba-4.5.8+dfsg/ctdb/tests/run_cluster_tests.sh samba-4.6.5+dfsg/ctdb/tests/run_cluster_tests.sh --- samba-4.5.8+dfsg/ctdb/tests/run_cluster_tests.sh 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/run_cluster_tests.sh 2017-01-11 07:55:14.000000000 +0000 @@ -155,20 +155,20 @@ # Not perfect, but it will do... mktemp () { - _dir=false + local dir=false if [ "$1" = "-d" ] ; then - _dir=true + dir=true fi - _t="${TMPDIR:-/tmp}/tmp.$$.$RANDOM" + local t="${TMPDIR:-/tmp}/tmp.$$.$RANDOM" ( umask 077 - if $_dir ; then - mkdir "$_t" + if $dir ; then + mkdir "$t" else - >"$_t" + >"$t" fi ) - echo "$_t" + echo "$t" } fi @@ -179,12 +179,12 @@ run_one_test () { - _f="$1" + local f="$1" - [ -x "$_f" ] || die "test \"$_f\" is not executable" + [ -x "$f" ] || die "test \"$f\" is not executable" tests_total=$(($tests_total + 1)) - ctdb_test_run "$_f" | tee "$tf" | show_progress + ctdb_test_run "$f" | tee "$tf" | show_progress status=$? if [ $status -eq 0 ] ; then tests_passed=$(($tests_passed + 1)) @@ -192,37 +192,39 @@ tests_failed=$(($tests_failed + 1)) fi if $with_summary ; then + local t if [ $status -eq 0 ] ; then - _t=" PASSED " + t=" PASSED " else - _t="*FAILED*" + t="*FAILED*" fi if $with_desc ; then desc=$(tail -n +4 $tf | head -n 1) - _f="$desc" + f="$desc" fi - echo "$_t $_f" >>"$sf" + echo "$t $f" >>"$sf" fi } find_and_run_one_test () { - _t="$1" - _dir="$2" + local t="$1" + local dir="$2" - _f="${_dir}${_dir:+/}${_t}" + local f="${dir}${dir:+/}${t}" - if [ -d "$_f" ] ; then - for _i in $(ls "${_f%/}/"*".sh" 2>/dev/null) ; do - run_one_test "$_i" + if [ -d "$f" ] ; then + local i + for i in $(ls "${f%/}/"*".sh" 2>/dev/null) ; do + run_one_test "$i" if $exit_on_fail && [ $status -ne 0 ] ; then break fi done # No tests found? Not a tests directory! Not found... [ -n "$status" ] || status=127 - elif [ -f "$_f" ] ; then - run_one_test "$_f" + elif [ -f "$f" ] ; then + run_one_test "$f" else status=127 fi @@ -255,7 +257,8 @@ # If no tests specified then run some defaults if [ -z "$1" ] ; then if [ -n "$TEST_LOCAL_DAEMONS" ] ; then - set -- onnode takeover tool eventscripts cunit shellcheck simple + set -- onnode takeover takeover_helper tool eventscripts \ + cunit eventd shellcheck simple else set -- simple complex fi diff -Nru samba-4.5.8+dfsg/ctdb/tests/run_tests.sh samba-4.6.5+dfsg/ctdb/tests/run_tests.sh --- samba-4.5.8+dfsg/ctdb/tests/run_tests.sh 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/run_tests.sh 2017-01-11 07:55:14.000000000 +0000 @@ -155,20 +155,20 @@ # Not perfect, but it will do... mktemp () { - _dir=false + local dir=false if [ "$1" = "-d" ] ; then - _dir=true + dir=true fi - _t="${TMPDIR:-/tmp}/tmp.$$.$RANDOM" + local t="${TMPDIR:-/tmp}/tmp.$$.$RANDOM" ( umask 077 - if $_dir ; then - mkdir "$_t" + if $dir ; then + mkdir "$t" else - >"$_t" + >"$t" fi ) - echo "$_t" + echo "$t" } fi @@ -179,12 +179,12 @@ run_one_test () { - _f="$1" + local f="$1" - [ -x "$_f" ] || die "test \"$_f\" is not executable" + [ -x "$f" ] || die "test \"$f\" is not executable" tests_total=$(($tests_total + 1)) - ctdb_test_run "$_f" | tee "$tf" | show_progress + ctdb_test_run "$f" | tee "$tf" | show_progress status=$? if [ $status -eq 0 ] ; then tests_passed=$(($tests_passed + 1)) @@ -192,37 +192,39 @@ tests_failed=$(($tests_failed + 1)) fi if $with_summary ; then + local t if [ $status -eq 0 ] ; then - _t=" PASSED " + t=" PASSED " else - _t="*FAILED*" + t="*FAILED*" fi if $with_desc ; then desc=$(tail -n +4 $tf | head -n 1) - _f="$desc" + f="$desc" fi - echo "$_t $_f" >>"$sf" + echo "$t $f" >>"$sf" fi } find_and_run_one_test () { - _t="$1" - _dir="$2" + local t="$1" + local dir="$2" - _f="${_dir}${_dir:+/}${_t}" + local f="${dir}${dir:+/}${t}" - if [ -d "$_f" ] ; then - for _i in $(ls "${_f%/}/"*".sh" 2>/dev/null) ; do - run_one_test "$_i" + if [ -d "$f" ] ; then + local i + for i in $(ls "${f%/}/"*".sh" 2>/dev/null) ; do + run_one_test "$i" if $exit_on_fail && [ $status -ne 0 ] ; then break fi done # No tests found? Not a tests directory! Not found... [ -n "$status" ] || status=127 - elif [ -f "$_f" ] ; then - run_one_test "$_f" + elif [ -f "$f" ] ; then + run_one_test "$f" else status=127 fi @@ -255,7 +257,8 @@ # If no tests specified then run some defaults if [ -z "$1" ] ; then if [ -n "$TEST_LOCAL_DAEMONS" ] ; then - set -- onnode takeover tool eventscripts cunit shellcheck simple + set -- onnode takeover takeover_helper tool eventscripts \ + cunit eventd shellcheck simple else set -- simple complex fi diff -Nru samba-4.5.8+dfsg/ctdb/tests/scripts/unit.sh samba-4.6.5+dfsg/ctdb/tests/scripts/unit.sh --- samba-4.5.8+dfsg/ctdb/tests/scripts/unit.sh 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/scripts/unit.sh 2017-01-11 07:55:14.000000000 +0000 @@ -75,7 +75,7 @@ Output (Exit status: ${_rc}): -------------------------------------------------- EOF - echo "$_out" | cat $TEST_CAT_RESULTS_OPTS + echo "$_out" | result_filter | cat $TEST_CAT_RESULTS_OPTS fi if ! $_passed ; then @@ -208,6 +208,16 @@ result_check || exit $? } + +# Simple test harness for running tests without tracing +unit_test_notrace () +{ + test_header "$@" + + _out=$("$@" 2>&1) + + result_check || exit $? +} test_cleanup_hooks="" diff -Nru samba-4.5.8+dfsg/ctdb/tests/simple/04_ctdb_setvar.sh samba-4.6.5+dfsg/ctdb/tests/simple/04_ctdb_setvar.sh --- samba-4.5.8+dfsg/ctdb/tests/simple/04_ctdb_setvar.sh 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/simple/04_ctdb_setvar.sh 2017-01-11 07:55:14.000000000 +0000 @@ -18,8 +18,8 @@ 1. Verify that the status on all of the ctdb nodes is 'OK'. 2. Get a list of all the ctdb tunable variables, using the 'ctdb listvars' command. -3. Set the value of one of the variables using the 'setvar' control on - one of the nodes. E.g. 'ctdb setvar DeterministicIPs 0'. +3. Increment the value of one of the variables using the 'setvar' control on + one of the nodes. E.g. 'ctdb setvar RecoverTimeout 31'. 4. Verify that the 'listvars' control now shows the new value for the variable. diff -Nru samba-4.5.8+dfsg/ctdb/tests/simple/13_ctdb_setdebug.sh samba-4.6.5+dfsg/ctdb/tests/simple/13_ctdb_setdebug.sh --- samba-4.5.8+dfsg/ctdb/tests/simple/13_ctdb_setdebug.sh 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/simple/13_ctdb_setdebug.sh 2017-01-11 07:55:14.000000000 +0000 @@ -62,14 +62,24 @@ set_and_check_debug $test_node "$new_debug" done -i=0 -for new_debug in $levels ; do +while read new_debug i ; do [ "$initial_debug" != "$i" ] || continue echo set_and_check_debug $test_node "$i" "$new_debug" - i=$[ $i + 1 ] -done +done </dev/null 2>&1 || _failed=true - echo "Tickle TCP connection $dest $src" - $CTDB tickle "$dest" "$src" >/dev/null 2>&1 || _failed=true - done + # Get connections, both directions + _conns=$(get_tcp_connections_for_ip "$_ip" | \ + awk '{ print $1, $2 ; print $2, $1 }') - if $_failed ; then - echo "Failed to send tickle control" - fi - } + echo "$_conns" | awk '{ print "Tickle TCP connection", $1, $2 }' + echo "$_conns" | ctdb tickle } get_tcp_connections_for_ip () @@ -862,139 +853,6 @@ : } -ctdb_reconfigure_take_lock () -{ - _ctdb_service_reconfigure_common - _lock="${_d}/reconfigure_lock" - mkdir -p "${_lock%/*}" # dirname - touch "$_lock" - - ( - flock 9 - # This is overkill but will work if we need to extend - # this to allow certain events to run multiple times - # in parallel (e.g. takeip) and write multiple PIDs to - # the file. - { - read _locker_event - if [ -n "$_locker_event" ] ; then - while read _pid ; do - if [ -n "$_pid" -a "$_pid" != $$ ] && \ - kill -0 "$_pid" 2>/dev/null ; then - exit 1 - fi - done - fi - } <"$_lock" - - printf "%s\n%s\n" "$event_name" $$ >"$_lock" - exit 0 - ) 9>"${_lock}.flock" -} - -ctdb_reconfigure_release_lock () -{ - _ctdb_service_reconfigure_common - _lock="${_d}/reconfigure_lock" - - rm -f "$_lock" -} - -ctdb_replay_monitor_status () -{ - echo "Replaying previous status for this script due to reconfigure..." - # Leading separator ('|') is missing in some versions... - _out=$($CTDB scriptstatus -X | grep -E "^\|?monitor\|${script_name}\|") - # Output looks like this: - # |monitor|60.nfs|1|ERROR|1314764004.030861|1314764004.035514|foo bar| - # This is the cheapest way of getting fields in the middle. - # Intentional word splitting here - # shellcheck disable=SC2046,2086 - set -- $(IFS="|" ; echo $_out) - _code="$3" - _status="$4" - # The error output field can include colons so we'll try to - # preserve them. The weak checking at the beginning tries to make - # this work for both broken (no leading '|') and fixed output. - _out="${_out%|}" - _err_out="${_out#*monitor|${script_name}|*|*|*|*|}" - case "$_status" in - OK) : ;; # Do nothing special. - TIMEDOUT) - # Recast this as an error, since we can't exit with the - # correct negative number. - _code=1 - _err_out="[Replay of TIMEDOUT scriptstatus - note incorrect return code.] ${_err_out}" - ;; - DISABLED) - # Recast this as an OK, since we can't exit with the - # correct negative number. - _code=0 - _err_out="[Replay of DISABLED scriptstatus - note incorrect return code.] ${_err_out}" - ;; - *) : ;; # Must be ERROR, do nothing special. - esac - if [ -n "$_err_out" ] ; then - echo "$_err_out" - fi - exit $_code -} - -ctdb_service_check_reconfigure () -{ - assert_service_name - - # We only care about some events in this function. For others we - # return now. - case "$event_name" in - monitor|ipreallocated|reconfigure) : ;; - *) return 0 ;; - esac - - if ctdb_reconfigure_take_lock ; then - # No events covered by this function are running, so proceed - # with gay abandon. - case "$event_name" in - reconfigure) - (ctdb_service_reconfigure) - exit $? - ;; - ipreallocated) - if ctdb_service_needs_reconfigure ; then - ctdb_service_reconfigure - fi - ;; - esac - - ctdb_reconfigure_release_lock - else - # Somebody else is running an event we don't want to collide - # with. We proceed with caution. - case "$event_name" in - reconfigure) - # Tell whoever called us to retry. - exit 2 - ;; - ipreallocated) - # Defer any scheduled reconfigure and just run the - # rest of the ipreallocated event, as per the - # eventscript. There's an assumption here that the - # event doesn't depend on any scheduled reconfigure. - # This is true in the current code. - return 0 - ;; - monitor) - # There is most likely a reconfigure in progress so - # the service is possibly unstable. As above, we - # defer any scheduled reconfigured. We also replay - # the previous monitor status since that's the best - # information we have. - ctdb_replay_monitor_status - ;; - esac - fi -} - ################################################################## # Does CTDB manage this service? - and associated auto-start/stop @@ -1276,16 +1134,12 @@ sort >"$_my_tickles" # Add tickles for connections that we haven't already got tickles for - comm -23 "$_my_connections" "$_my_tickles" | - while read _src _dst ; do - $CTDB addtickle "$_src" "$_dst" - done + comm -23 "$_my_connections" "$_my_tickles" | \ + $CTDB addtickle # Remove tickles for connections that are no longer there - comm -13 "$_my_connections" "$_my_tickles" | - while read _src _dst ; do - $CTDB deltickle "$_src" "$_dst" - done + comm -13 "$_my_connections" "$_my_tickles" | \ + $CTDB deltickle rm -f "$_my_connections" "$_my_tickles" diff -Nru samba-4.5.8+dfsg/ctdb/tests/simple/scripts/local_daemons.bash samba-4.6.5+dfsg/ctdb/tests/simple/scripts/local_daemons.bash --- samba-4.5.8+dfsg/ctdb/tests/simple/scripts/local_daemons.bash 2016-10-24 19:37:30.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/simple/scripts/local_daemons.bash 2017-01-11 07:55:14.000000000 +0000 @@ -1,20 +1,17 @@ # If we're not running on a real cluster then we need a local copy of # ctdb (and other stuff) in $PATH and we will use local daemons. -export CTDB_NODES_SOCKETS="" -for i in $(seq 0 $(($TEST_LOCAL_DAEMONS - 1))) ; do - CTDB_NODES_SOCKETS="${CTDB_NODES_SOCKETS}${CTDB_NODES_SOCKETS:+ }${TEST_VAR_DIR}/sock.${i}" -done - # Use in-tree binaries if running against local daemons. # Otherwise CTDB need to be installed on all nodes. if [ -n "$ctdb_dir" -a -d "${ctdb_dir}/bin" ] ; then # ctdbd_wrapper is in config/ directory PATH="${ctdb_dir}/bin:${ctdb_dir}/config:${PATH}" hdir="${ctdb_dir}/bin" + export CTDB_EVENTD="${hdir}/ctdb_eventd" + export CTDB_EVENT_HELPER="${hdir}/ctdb_event" export CTDB_LOCK_HELPER="${hdir}/ctdb_lock_helper" - export CTDB_EVENT_HELPER="${hdir}/ctdb_event_helper" export CTDB_RECOVERY_HELPER="${hdir}/ctdb_recovery_helper" + export CTDB_TAKEOVER_HELPER="${hdir}/ctdb_takeover_helper" export CTDB_CLUSTER_MUTEX_HELPER="${hdir}/ctdb_mutex_fcntl_helper" fi @@ -31,10 +28,54 @@ sed -e 's@=\([^"]\)@="\1@' -e 's@[^"]$@&"@' -e 's@="$@&"@' } -setup_ctdb () +# If the given IP is hosted then print 2 items: maskbits and iface +have_ip () +{ + local addr="$1" + local bits t + + case "$addr" in + *:*) bits=128 ;; + *) bits=32 ;; + esac + + t=$(ip addr show to "${addr}/${bits}") + [ -n "$t" ] +} + +node_dir () { - mkdir -p "${TEST_VAR_DIR}/test.db/persistent" + local pnn="$1" + echo "${TEST_VAR_DIR}/node.${pnn}" +} + +node_conf () +{ + local pnn="$1" + + local node_dir=$(node_dir "$pnn") + echo "${node_dir}/ctdbd.conf" +} + +node_pidfile () +{ + local pnn="$1" + + local node_dir=$(node_dir "$pnn") + echo "${node_dir}/ctdbd.pid" +} + +node_socket () +{ + local pnn="$1" + + local node_dir=$(node_dir "$pnn") + echo "${node_dir}/ctdbd.socket" +} + +setup_ctdb () +{ local public_addresses_all="${TEST_VAR_DIR}/public_addresses_all" rm -f $CTDB_NODES $public_addresses_all @@ -53,13 +94,19 @@ mkdir -p "${TEST_VAR_DIR}/events.d" cp -p "${events_d}/"* "${TEST_VAR_DIR}/events.d/" + local have_all_ips=true local i for i in $(seq 1 $TEST_LOCAL_DAEMONS) ; do - if [ "${CTDB_USE_IPV6}x" != "x" ]; then - j=$((printf "%02x" $i)) - echo "fd00::5357:5f${j}" >>"$CTDB_NODES" - # FIXME: need to add addresses to lo as root before running :-( - # ip addr add "fc00:10::${i}/64" dev lo + if [ -n "$CTDB_USE_IPV6" ]; then + local j=$(printf "%02x" $i) + local node_ip="fd00::5357:5f${j}" + if have_ip "$node_ip" ; then + echo "$node_ip" >>"$CTDB_NODES" + else + echo "ERROR: ${node_ip} not on an interface, please add it" + have_all_ips=false + fi + # 2 public addresses on most nodes, just to make things interesting. if [ $(($i - 1)) -ne $no_public_ips ] ; then echo "fc00:10::1:${i}/64 lo" >>"$public_addresses_all" @@ -76,9 +123,16 @@ fi done + if ! $have_all_ips ; then + return 1 + fi + local pnn for pnn in $(seq 0 $(($TEST_LOCAL_DAEMONS - 1))) ; do - local public_addresses_mine="${TEST_VAR_DIR}/public_addresses.${pnn}" + local node_dir=$(node_dir "$pnn") + mkdir -p "$node_dir" + + local public_addresses_mine="${node_dir}/public_addresses" local public_addresses if [ "$no_public_ips" = $pnn ] ; then @@ -91,20 +145,24 @@ local node_ip=$(sed -n -e "$(($pnn + 1))p" "$CTDB_NODES") - local pidfile="${TEST_VAR_DIR}/ctdbd.${pnn}.pid" - local conf="${TEST_VAR_DIR}/ctdbd.${pnn}.conf" + local conf=$(node_conf "$pnn") + local socket=$(node_socket "$pnn") + + local db_dir="${node_dir}/db" + mkdir -p "${db_dir}/persistent" + cat >"$conf" <' | xargs | sed -e 's| |,|g') -o args ww echo } + +# onnode will use CTDB_NODES_SOCKETS to help the ctdb tool connection +# to each daemon +export CTDB_NODES_SOCKETS="" +for i in $(seq 0 $(($TEST_LOCAL_DAEMONS - 1))) ; do + socket=$(node_socket "$i") + CTDB_NODES_SOCKETS="${CTDB_NODES_SOCKETS}${CTDB_NODES_SOCKETS:+ }${socket}" +done diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/ctdb_takeover_tests.c samba-4.6.5+dfsg/ctdb/tests/src/ctdb_takeover_tests.c --- samba-4.5.8+dfsg/ctdb/tests/src/ctdb_takeover_tests.c 2016-10-24 19:37:30.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/src/ctdb_takeover_tests.c 2017-01-11 07:55:14.000000000 +0000 @@ -32,6 +32,8 @@ #include "server/ipalloc.h" +#include "ipalloc_read_known_ips.h" + static void print_ctdb_public_ip_list(TALLOC_CTX *mem_ctx, struct public_ip_list * ips) { @@ -49,86 +51,6 @@ static enum ctdb_runstate *get_runstate(TALLOC_CTX *tmp_ctx, int numnodes); -static void add_ip(TALLOC_CTX *mem_ctx, - struct ctdb_public_ip_list *l, - ctdb_sock_addr *addr, - uint32_t pnn) -{ - - l->ip = talloc_realloc(mem_ctx, l->ip, - struct ctdb_public_ip, l->num + 1); - assert(l->ip != NULL); - - l->ip[l->num].addr = *addr; - l->ip[l->num].pnn = pnn; - l->num++; -} - -/* Format of each line is "IP CURRENT_PNN [ALLOWED_PNN,...]". - * If multi is true then ALLOWED_PNNs are not allowed. */ -static void read_ctdb_public_ip_info_node(int numnodes, - bool multi, - struct ctdb_public_ip_list **k, - struct ctdb_public_ip_list *known) -{ - char line[1024]; - ctdb_sock_addr addr; - char *t, *tok; - int pnn, n; - - /* Known public IPs */ - *k = talloc_zero(known, struct ctdb_public_ip_list); - assert(k != NULL); - - while (fgets(line, sizeof(line), stdin) != NULL) { - - /* Get rid of pesky newline */ - if ((t = strchr(line, '\n')) != NULL) { - *t = '\0'; - } - - /* Exit on an empty line */ - if (line[0] == '\0') { - break; - } - - /* Get the IP address */ - tok = strtok(line, " \t"); - if (tok == NULL) { - DEBUG(DEBUG_ERR, (__location__ " WARNING, bad line ignored :%s\n", line)); - continue; - } - - if (!parse_ip(tok, NULL, 0, &addr)) { - DEBUG(DEBUG_ERR, (__location__ " ERROR, bad address :%s\n", tok)); - continue; - } - - /* Get the PNN */ - pnn = -1; - tok = strtok(NULL, " \t"); - if (tok != NULL) { - pnn = (int) strtol(tok, (char **) NULL, 10); - } - - add_ip(*k, *k, &addr, pnn); - - tok = strtok(NULL, " \t#"); - if (tok == NULL) { - continue; - } - - /* Handle allowed nodes for addr */ - assert(multi == false); - t = strtok(tok, ","); - while (t != NULL) { - n = (int) strtol(t, (char **) NULL, 10); - add_ip(known, &known[n], &addr, pnn); - t = strtok(NULL, ","); - } - } -} - static void read_ctdb_public_ip_info(TALLOC_CTX *ctx, int numnodes, bool multi, @@ -136,34 +58,15 @@ struct ctdb_public_ip_list ** avail) { int n; - struct ctdb_public_ip_list * k; enum ctdb_runstate *runstate; - *known = talloc_zero_array(ctx, struct ctdb_public_ip_list, - numnodes); + *known = ipalloc_read_known_ips(ctx, numnodes, multi); assert(*known != NULL); + *avail = talloc_zero_array(ctx, struct ctdb_public_ip_list, numnodes); assert(*avail != NULL); - if (multi) { - for (n = 0; n < numnodes; n++) { - read_ctdb_public_ip_info_node(numnodes, multi, - &k, *known); - - (*known)[n] = *k; - } - } else { - read_ctdb_public_ip_info_node(numnodes, multi, &k, *known); - - /* Assign it to any nodes that don't have a list assigned */ - for (n = 0; n < numnodes; n++) { - if ((*known)[n].num == 0) { - (*known)[n] = *k; - } - } - } - runstate = get_runstate(ctx, numnodes); for (n = 0; n < numnodes; n++) { if (runstate[n] == CTDB_RUNSTATE_RUNNING) { @@ -251,10 +154,11 @@ { struct ctdb_public_ip_list *known; struct ctdb_public_ip_list *avail; - char *tok, *ns, *t; + char *tok, *ns; + const char *t; struct ctdb_node_map *nodemap; - uint32_t *tval_noiptakeover; - uint32_t *tval_noiptakeoverondisabled; + uint32_t noiptakeover; + uint32_t noiphostonalldisabled; ctdb_sock_addr sa_zero = { .ip = { 0 } }; enum ipalloc_algorithm algorithm; @@ -291,9 +195,26 @@ } } + t = getenv("CTDB_SET_NoIPTakeover"); + if (t != NULL) { + noiptakeover = (uint32_t) strtol(t, NULL, 0); + } else { + noiptakeover = 0; + } + + t = getenv("CTDB_SET_NoIPHostOnAllDisabled"); + if (t != NULL) { + noiphostonalldisabled = (uint32_t) strtol(t, NULL, 0); + } else { + noiphostonalldisabled = 0; + } + *ipalloc_state = ipalloc_state_init(mem_ctx, nodemap->num, algorithm, - false, NULL); + (noiptakeover != 0), + false, + (noiphostonalldisabled != 0), + NULL); assert(*ipalloc_state != NULL); read_ctdb_public_ip_info(mem_ctx, nodemap->num, @@ -302,17 +223,7 @@ ipalloc_set_public_ips(*ipalloc_state, known, avail); - tval_noiptakeover = get_tunable_values(mem_ctx, nodemap->num, - "CTDB_SET_NoIPTakeover"); - assert(tval_noiptakeover != NULL); - tval_noiptakeoverondisabled = - get_tunable_values(mem_ctx, nodemap->num, - "CTDB_SET_NoIPHostOnAllDisabled"); - assert(tval_noiptakeoverondisabled != NULL); - - ipalloc_set_node_flags(*ipalloc_state, nodemap, - tval_noiptakeover, - tval_noiptakeoverondisabled); + ipalloc_set_node_flags(*ipalloc_state, nodemap); } /* IP layout is read from stdin. See comment for ctdb_test_init() for @@ -340,10 +251,13 @@ int main(int argc, const char *argv[]) { - DEBUGLEVEL = DEBUG_DEBUG; - if (getenv("CTDB_TEST_LOGLEVEL")) { - DEBUGLEVEL = atoi(getenv("CTDB_TEST_LOGLEVEL")); - } + int loglevel; + const char *debuglevelstr = getenv("CTDB_TEST_LOGLEVEL"); + + if (! debug_level_parse(debuglevelstr, &loglevel)) { + loglevel = DEBUG_DEBUG; + } + DEBUGLEVEL = loglevel; if (argc < 2) { usage(); diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/fake_ctdbd.c samba-4.6.5+dfsg/ctdb/tests/src/fake_ctdbd.c --- samba-4.5.8+dfsg/ctdb/tests/src/fake_ctdbd.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/src/fake_ctdbd.c 2017-01-11 07:55:14.000000000 +0000 @@ -40,6 +40,8 @@ #include "common/logging.h" #include "common/tunable.h" +#include "ipalloc_read_known_ips.h" + #define CTDB_PORT 4379 @@ -98,6 +100,14 @@ uint64_t srvid; }; +struct fake_control_failure { + struct fake_control_failure *prev, *next; + enum ctdb_controls opcode; + uint32_t pnn; + const char *error; + const char *comment; +}; + struct ctdbd_context { struct node_map *node_map; struct interface_map *iface_map; @@ -109,11 +119,13 @@ struct timeval recovery_start_time; struct timeval recovery_end_time; bool takeover_disabled; - enum debug_level log_level; + int log_level; enum ctdb_runstate runstate; struct ctdb_tunable_list tun_list; int monitoring_mode; char *reclock; + struct ctdb_public_ip_list *known_ips; + struct fake_control_failure *control_failures; }; /* @@ -704,6 +716,109 @@ return NULL; } +static bool public_ips_parse(struct ctdbd_context *ctdb, + uint32_t numnodes) +{ + if (numnodes == 0) { + D_ERR("Must initialise nodemap before public IPs\n"); + return false; + } + + ctdb->known_ips = ipalloc_read_known_ips(ctdb, numnodes, false); + + return (ctdb->known_ips != NULL); +} + +/* Read information about controls to fail. Format is: + * {ERROR|TIMEOUT} + */ +static bool control_failures_parse(struct ctdbd_context *ctdb) +{ + char line[1024]; + + while ((fgets(line, sizeof(line), stdin) != NULL)) { + char *tok, *t; + enum ctdb_controls opcode; + uint32_t pnn; + const char *error; + const char *comment; + struct fake_control_failure *failure = NULL; + + if (line[0] == '\n') { + break; + } + + /* Get rid of pesky newline */ + if ((t = strchr(line, '\n')) != NULL) { + *t = '\0'; + } + + /* Get opcode */ + tok = strtok(line, " \t"); + if (tok == NULL) { + D_ERR("bad line (%s) - missing opcode\n", line); + continue; + } + opcode = (enum ctdb_controls)strtoul(tok, NULL, 0); + + /* Get PNN */ + tok = strtok(NULL, " \t"); + if (tok == NULL) { + D_ERR("bad line (%s) - missing PNN\n", line); + continue; + } + pnn = (uint32_t)strtoul(tok, NULL, 0); + + /* Get error */ + tok = strtok(NULL, " \t"); + if (tok == NULL) { + D_ERR("bad line (%s) - missing errno\n", line); + continue; + } + error = talloc_strdup(ctdb, tok); + if (error == NULL) { + goto fail; + } + if (strcmp(error, "ERROR") != 0 && + strcmp(error, "TIMEOUT") != 0) { + D_ERR("bad line (%s) " + "- error must be \"ERROR\" or \"TIMEOUT\"\n", + line); + goto fail; + } + + /* Get comment */ + tok = strtok(NULL, "\n"); /* rest of line */ + if (tok == NULL) { + D_ERR("bad line (%s) - missing comment\n", line); + continue; + } + comment = talloc_strdup(ctdb, tok); + if (comment == NULL) { + goto fail; + } + + failure = talloc_zero(ctdb, struct fake_control_failure); + if (failure == NULL) { + goto fail; + } + + failure->opcode = opcode; + failure->pnn = pnn; + failure->error = error; + failure->comment = comment; + + DLIST_ADD(ctdb->control_failures, failure); + } + + D_INFO("Parsing fake control failures done\n"); + return true; + +fail: + D_INFO("Parsing fake control failures failed\n"); + return false; +} + /* * CTDB context setup */ @@ -769,8 +884,13 @@ status = vnnmap_parse(ctdb->vnn_map); } else if (strcmp(line, "DBMAP") == 0) { status = dbmap_parse(ctdb->db_map); + } else if (strcmp(line, "PUBLICIPS") == 0) { + status = public_ips_parse(ctdb, + ctdb->node_map->num_nodes); } else if (strcmp(line, "RECLOCK") == 0) { status = reclock_parse(ctdb); + } else if (strcmp(line, "CONTROLFAILS") == 0) { + status = control_failures_parse(ctdb); } else { fprintf(stderr, "Unknown line %s\n", line); status = false; @@ -1242,7 +1362,7 @@ struct ctdb_reply_control reply; reply.rdata.opcode = request->opcode; - reply.rdata.data.loglevel = debug_level_to_int(ctdb->log_level); + reply.rdata.data.loglevel = (uint32_t)ctdb->log_level; reply.status = 0; reply.errmsg = NULL; @@ -1259,7 +1379,7 @@ struct ctdbd_context *ctdb = state->ctdb; struct ctdb_reply_control reply; - ctdb->log_level = debug_level_from_int(request->rdata.data.loglevel); + ctdb->log_level = (int)request->rdata.data.loglevel; reply.rdata.opcode = request->opcode; reply.status = 0; @@ -1917,6 +2037,174 @@ client_send_control(req, header, &reply); } +static void control_release_ip(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct ctdb_req_header *header, + struct ctdb_req_control *request) +{ + struct client_state *state = tevent_req_data( + req, struct client_state); + struct ctdbd_context *ctdb = state->ctdb; + struct ctdb_public_ip *ip = request->rdata.data.pubip; + struct ctdb_reply_control reply; + struct ctdb_public_ip_list *ips = NULL; + struct ctdb_public_ip *t = NULL; + int i; + + reply.rdata.opcode = request->opcode; + + if (ctdb->known_ips == NULL) { + D_INFO("RELEASE_IP %s - not a public IP\n", + ctdb_sock_addr_to_string(mem_ctx, &ip->addr)); + goto done; + } + + ips = &ctdb->known_ips[header->destnode]; + + t = NULL; + for (i = 0; i < ips->num; i++) { + if (ctdb_sock_addr_same_ip(&ips->ip[i].addr, &ip->addr)) { + t = &ips->ip[i]; + break; + } + } + if (t == NULL) { + D_INFO("RELEASE_IP %s - not a public IP\n", + ctdb_sock_addr_to_string(mem_ctx, &ip->addr)); + goto done; + } + + if (t->pnn != header->destnode) { + if (header->destnode == ip->pnn) { + D_ERR("error: RELEASE_IP %s - to TAKE_IP node %d\n", + ctdb_sock_addr_to_string(mem_ctx, &ip->addr), + ip->pnn); + reply.status = -1; + reply.errmsg = "RELEASE_IP to TAKE_IP node"; + client_send_control(req, header, &reply); + return; + } + + D_INFO("RELEASE_IP %s - to node %d - redundant\n", + ctdb_sock_addr_to_string(mem_ctx, &ip->addr), + ip->pnn); + t->pnn = ip->pnn; + } else { + D_NOTICE("RELEASE_IP %s - to node %d\n", + ctdb_sock_addr_to_string(mem_ctx, &ip->addr), + ip->pnn); + t->pnn = ip->pnn; + } + +done: + reply.status = 0; + reply.errmsg = NULL; + client_send_control(req, header, &reply); +} + +static void control_takeover_ip(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct ctdb_req_header *header, + struct ctdb_req_control *request) +{ + struct client_state *state = tevent_req_data( + req, struct client_state); + struct ctdbd_context *ctdb = state->ctdb; + struct ctdb_public_ip *ip = request->rdata.data.pubip; + struct ctdb_reply_control reply; + struct ctdb_public_ip_list *ips = NULL; + struct ctdb_public_ip *t = NULL; + int i; + + reply.rdata.opcode = request->opcode; + + if (ctdb->known_ips == NULL) { + D_INFO("TAKEOVER_IP %s - not a public IP\n", + ctdb_sock_addr_to_string(mem_ctx, &ip->addr)); + goto done; + } + + ips = &ctdb->known_ips[header->destnode]; + + t = NULL; + for (i = 0; i < ips->num; i++) { + if (ctdb_sock_addr_same_ip(&ips->ip[i].addr, &ip->addr)) { + t = &ips->ip[i]; + break; + } + } + if (t == NULL) { + D_INFO("TAKEOVER_IP %s - not a public IP\n", + ctdb_sock_addr_to_string(mem_ctx, &ip->addr)); + goto done; + } + + if (t->pnn == header->destnode) { + D_INFO("TAKEOVER_IP %s - redundant\n", + ctdb_sock_addr_to_string(mem_ctx, &ip->addr)); + } else { + D_NOTICE("TAKEOVER_IP %s\n", + ctdb_sock_addr_to_string(mem_ctx, &ip->addr)); + t->pnn = ip->pnn; + } + +done: + reply.status = 0; + reply.errmsg = NULL; + client_send_control(req, header, &reply); +} + +static void control_get_public_ips(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct ctdb_req_header *header, + struct ctdb_req_control *request) +{ + struct client_state *state = tevent_req_data( + req, struct client_state); + struct ctdbd_context *ctdb = state->ctdb; + struct ctdb_reply_control reply; + struct ctdb_public_ip_list *ips = NULL; + + reply.rdata.opcode = request->opcode; + + if (ctdb->known_ips == NULL) { + /* No IPs defined so create a dummy empty struct and ship it */ + ips = talloc_zero(mem_ctx, struct ctdb_public_ip_list);; + if (ips == NULL) { + reply.status = ENOMEM; + reply.errmsg = "Memory error"; + goto done; + } + goto ok; + } + + ips = &ctdb->known_ips[header->destnode]; + + if (request->flags & CTDB_PUBLIC_IP_FLAGS_ONLY_AVAILABLE) { + /* If runstate is not RUNNING or a node is then return + * no available IPs. Don't worry about interface + * states here - we're not faking down to that level. + */ + if (ctdb->runstate != CTDB_RUNSTATE_RUNNING) { + /* No available IPs: return dummy empty struct */ + ips = talloc_zero(mem_ctx, struct ctdb_public_ip_list);; + if (ips == NULL) { + reply.status = ENOMEM; + reply.errmsg = "Memory error"; + goto done; + } + } + } + +ok: + reply.rdata.data.pubip_list = ips; + reply.status = 0; + reply.errmsg = NULL; + +done: + client_send_control(req, header, &reply); +} + static void control_get_nodemap(TALLOC_CTX *mem_ctx, struct tevent_req *req, struct ctdb_req_header *header, @@ -2163,31 +2451,24 @@ client_send_control(req, header, &reply); } -static void control_get_ifaces(TALLOC_CTX *mem_ctx, - struct tevent_req *req, - struct ctdb_req_header *header, - struct ctdb_req_control *request) +static struct ctdb_iface_list *get_ctdb_iface_list(TALLOC_CTX *mem_ctx, + struct ctdbd_context *ctdb) { - struct client_state *state = tevent_req_data( - req, struct client_state); - struct ctdbd_context *ctdb = state->ctdb; - struct ctdb_reply_control reply; struct ctdb_iface_list *iface_list; struct interface *iface; int i; - reply.rdata.opcode = request->opcode; - iface_list = talloc_zero(mem_ctx, struct ctdb_iface_list); if (iface_list == NULL) { - goto fail; + goto done; } iface_list->num = ctdb->iface_map->num; iface_list->iface = talloc_array(iface_list, struct ctdb_iface, iface_list->num); if (iface_list->iface == NULL) { - goto fail; + TALLOC_FREE(iface_list); + goto done; } for (i=0; inum; i++) { @@ -2200,6 +2481,105 @@ sizeof(iface_list->iface[i].name)); } +done: + return iface_list; +} + +static void control_get_public_ip_info(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct ctdb_req_header *header, + struct ctdb_req_control *request) +{ + struct client_state *state = tevent_req_data( + req, struct client_state); + struct ctdbd_context *ctdb = state->ctdb; + struct ctdb_reply_control reply; + ctdb_sock_addr *addr = request->rdata.data.addr; + struct ctdb_public_ip_list *known = NULL; + struct ctdb_public_ip_info *info = NULL; + unsigned i; + + reply.rdata.opcode = request->opcode; + + info = talloc_zero(mem_ctx, struct ctdb_public_ip_info); + if (info == NULL) { + reply.status = ENOMEM; + reply.errmsg = "Memory error"; + goto done; + } + + reply.rdata.data.ipinfo = info; + + if (ctdb->known_ips != NULL) { + known = &ctdb->known_ips[header->destnode]; + } else { + /* No IPs defined so create a dummy empty struct and + * fall through. The given IP won't be matched + * below... + */ + known = talloc_zero(mem_ctx, struct ctdb_public_ip_list);; + if (known == NULL) { + reply.status = ENOMEM; + reply.errmsg = "Memory error"; + goto done; + } + } + + for (i = 0; i < known->num; i++) { + if (ctdb_sock_addr_same_ip(&known->ip[i].addr, + addr)) { + break; + } + } + + if (i == known->num) { + D_ERR("GET_PUBLIC_IP_INFO: not known public IP %s\n", + ctdb_sock_addr_to_string(mem_ctx, addr)); + reply.status = -1; + reply.errmsg = "Unknown address"; + goto done; + } + + info->ip = known->ip[i]; + + /* The fake PUBLICIPS stanza and resulting known_ips data + * don't know anything about interfaces, so completely fake + * this. + */ + info->active_idx = 0; + + info->ifaces = get_ctdb_iface_list(mem_ctx, ctdb); + if (info->ifaces == NULL) { + reply.status = ENOMEM; + reply.errmsg = "Memory error"; + goto done; + } + + reply.status = 0; + reply.errmsg = NULL; + +done: + client_send_control(req, header, &reply); +} + +static void control_get_ifaces(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct ctdb_req_header *header, + struct ctdb_req_control *request) +{ + struct client_state *state = tevent_req_data( + req, struct client_state); + struct ctdbd_context *ctdb = state->ctdb; + struct ctdb_reply_control reply; + struct ctdb_iface_list *iface_list; + + reply.rdata.opcode = request->opcode; + + iface_list = get_ctdb_iface_list(mem_ctx, ctdb); + if (iface_list == NULL) { + goto fail; + } + reply.rdata.data.iface_list = iface_list; reply.status = 0; reply.errmsg = NULL; @@ -2347,6 +2727,21 @@ client_send_control(req, header, &reply); } +static void control_ipreallocated(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct ctdb_req_header *header, + struct ctdb_req_control *request) +{ + struct ctdb_reply_control reply; + + /* Always succeed */ + reply.rdata.opcode = request->opcode; + reply.status = 0; + reply.errmsg = NULL; + + client_send_control(req, header, &reply); +} + static void control_get_runstate(TALLOC_CTX *mem_ctx, struct tevent_req *req, struct ctdb_req_header *header, @@ -2392,6 +2787,44 @@ client_send_control(req, header, &reply); } +static bool fake_control_failure(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct ctdb_req_header *header, + struct ctdb_req_control *request) +{ + struct client_state *state = tevent_req_data( + req, struct client_state); + struct ctdbd_context *ctdb = state->ctdb; + struct ctdb_reply_control reply; + struct fake_control_failure *f = NULL; + + D_DEBUG("Checking fake control failure for control %u on node %u\n", + request->opcode, header->destnode); + for (f = ctdb->control_failures; f != NULL; f = f->next) { + if (f->opcode == request->opcode && + (f->pnn == header->destnode || + f->pnn == CTDB_UNKNOWN_PNN)) { + + reply.rdata.opcode = request->opcode; + if (strcmp(f->error, "TIMEOUT") == 0) { + /* Causes no reply */ + D_ERR("Control %u fake timeout on node %u\n", + request->opcode, header->destnode); + return true; + } else if (strcmp(f->error, "ERROR") == 0) { + D_ERR("Control %u fake error on node %u\n", + request->opcode, header->destnode); + reply.status = -1; + reply.errmsg = f->comment; + client_send_control(req, header, &reply); + return true; + } + } + } + + return false; +} + static void control_error(TALLOC_CTX *mem_ctx, struct tevent_req *req, struct ctdb_req_header *header, @@ -2753,6 +3186,10 @@ DEBUG(DEBUG_INFO, ("request opcode = %u, reqid = %u\n", request.opcode, header.reqid)); + if (fake_control_failure(mem_ctx, req, &header, &request)) { + goto done; + } + switch (request.opcode) { case CTDB_CONTROL_PROCESS_EXISTS: control_process_exists(mem_ctx, req, &header, &request); @@ -2862,6 +3299,18 @@ control_get_capabilities(mem_ctx, req, &header, &request); break; + case CTDB_CONTROL_RELEASE_IP: + control_release_ip(mem_ctx, req, &header, &request); + break; + + case CTDB_CONTROL_TAKEOVER_IP: + control_takeover_ip(mem_ctx, req, &header, &request); + break; + + case CTDB_CONTROL_GET_PUBLIC_IPS: + control_get_public_ips(mem_ctx, req, &header, &request); + break; + case CTDB_CONTROL_GET_NODEMAP: control_get_nodemap(mem_ctx, req, &header, &request); break; @@ -2890,6 +3339,10 @@ control_db_get_health(mem_ctx, req, &header, &request); break; + case CTDB_CONTROL_GET_PUBLIC_IP_INFO: + control_get_public_ip_info(mem_ctx, req, &header, &request); + break; + case CTDB_CONTROL_GET_IFACES: control_get_ifaces(mem_ctx, req, &header, &request); break; @@ -2906,6 +3359,10 @@ control_set_db_sticky(mem_ctx, req, &header, &request); break; + case CTDB_CONTROL_IPREALLOCATED: + control_ipreallocated(mem_ctx, req, &header, &request); + break; + case CTDB_CONTROL_GET_RUNSTATE: control_get_runstate(mem_ctx, req, &header, &request); break; @@ -2921,6 +3378,7 @@ break; } +done: talloc_free(mem_ctx); } @@ -3165,7 +3623,6 @@ TALLOC_CTX *mem_ctx; struct ctdbd_context *ctdb; struct tevent_context *ev; - enum debug_level debug_level; poptContext pc; int opt, fd, ret, pfd[2]; ssize_t len; @@ -3191,24 +3648,19 @@ exit(1); } - if (options.debuglevel == NULL) { - DEBUGLEVEL = debug_level_to_int(DEBUG_ERR); - } else { - if (debug_level_parse(options.debuglevel, &debug_level)) { - DEBUGLEVEL = debug_level_to_int(debug_level); - } else { - fprintf(stderr, "Invalid debug level\n"); - poptPrintHelp(pc, stdout, 0); - exit(1); - } - } - mem_ctx = talloc_new(NULL); if (mem_ctx == NULL) { fprintf(stderr, "Memory error\n"); exit(1); } + ret = logging_init(mem_ctx, "file:", options.debuglevel, "fake-ctdbd"); + if (ret != 0) { + fprintf(stderr, "Invalid debug level\n"); + poptPrintHelp(pc, stdout, 0); + exit(1); + } + ctdb = ctdbd_setup(mem_ctx); if (ctdb == NULL) { exit(1); diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/ipalloc_read_known_ips.c samba-4.6.5+dfsg/ctdb/tests/src/ipalloc_read_known_ips.c --- samba-4.5.8+dfsg/ctdb/tests/src/ipalloc_read_known_ips.c 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/src/ipalloc_read_known_ips.c 2017-01-11 07:55:14.000000000 +0000 @@ -0,0 +1,181 @@ +/* + Tests support for CTDB IP allocation + + Copyright (C) Martin Schwenke 2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include + +#include "replace.h" +#include "system/network.h" + +#include "lib/util/debug.h" + +#include "protocol/protocol.h" +#include "common/logging.h" +#include "common/system.h" + +#include "ipalloc_read_known_ips.h" + +static bool add_ip(TALLOC_CTX *mem_ctx, + struct ctdb_public_ip_list *l, + ctdb_sock_addr *addr, + uint32_t pnn) +{ + + l->ip = talloc_realloc(mem_ctx, l->ip, + struct ctdb_public_ip, l->num + 1); + if (l->ip == NULL) { + D_ERR(__location__ " out of memory\n"); + return false; + } + + l->ip[l->num].addr = *addr; + l->ip[l->num].pnn = pnn; + l->num++; + + return true; +} + +/* Format of each line is "IP CURRENT_PNN [ALLOWED_PNN,...]". + * If multi is true then ALLOWED_PNNs are not allowed. */ +static bool read_ctdb_public_ip_info_node(bool multi, + struct ctdb_public_ip_list **k, + struct ctdb_public_ip_list *known) +{ + char line[1024]; + ctdb_sock_addr addr; + char *t, *tok; + int pnn, n; + + /* Known public IPs */ + *k = talloc_zero(known, struct ctdb_public_ip_list); + if (*k == NULL) { + goto fail; + } + + while (fgets(line, sizeof(line), stdin) != NULL) { + + /* Get rid of pesky newline */ + if ((t = strchr(line, '\n')) != NULL) { + *t = '\0'; + } + + /* Exit on an empty line */ + if (line[0] == '\0') { + break; + } + + /* Get the IP address */ + tok = strtok(line, " \t"); + if (tok == NULL) { + D_WARNING("WARNING, bad line ignored :%s\n", line); + continue; + } + + if (!parse_ip(tok, NULL, 0, &addr)) { + D_ERR("ERROR, bad address :%s\n", tok); + continue; + } + + /* Get the PNN */ + pnn = -1; + tok = strtok(NULL, " \t"); + if (tok != NULL) { + pnn = (int) strtol(tok, (char **) NULL, 10); + } + + if (! add_ip(*k, *k, &addr, pnn)) { + goto fail; + } + + tok = strtok(NULL, " \t#"); + if (tok == NULL) { + continue; + } + + /* Handle allowed nodes for addr */ + if (multi) { + D_ERR("ERROR, bad token\n"); + goto fail; + } + t = strtok(tok, ","); + while (t != NULL) { + n = (int) strtol(t, (char **) NULL, 10); + if (! add_ip(known, &known[n], &addr, pnn)) { + goto fail; + } + t = strtok(NULL, ","); + } + } + + return true; + +fail: + TALLOC_FREE(*k); + return false; +} + +struct ctdb_public_ip_list * ipalloc_read_known_ips(TALLOC_CTX *ctx, + int numnodes, + bool multi) +{ + int n; + struct ctdb_public_ip_list *k; + struct ctdb_public_ip_list *known; + + known = talloc_zero_array(ctx, struct ctdb_public_ip_list, + numnodes); + if (known == NULL) { + D_ERR(__location__ " out of memory\n"); + goto fail; + } + + if (multi) { + for (n = 0; n < numnodes; n++) { + if (! read_ctdb_public_ip_info_node(multi, &k, known)) { + goto fail; + } + + known[n] = *k; + } + } else { + if (! read_ctdb_public_ip_info_node(multi, &k, known)) { + goto fail; + } + + /* Copy it to any nodes that don't have a + * list assigned + */ + for (n = 0; n < numnodes; n++) { + if (known[n].num == 0) { + known[n].num = k->num; + known[n].ip = talloc_memdup( + known, k->ip, + k->num * sizeof(struct ctdb_public_ip)); + if (known[n].ip == NULL) { + goto fail; + } + } + } + } + + return known; + +fail: + talloc_free(known); + return NULL; +} diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/ipalloc_read_known_ips.h samba-4.6.5+dfsg/ctdb/tests/src/ipalloc_read_known_ips.h --- samba-4.5.8+dfsg/ctdb/tests/src/ipalloc_read_known_ips.h 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/src/ipalloc_read_known_ips.h 2017-01-11 07:55:14.000000000 +0000 @@ -0,0 +1,32 @@ +/* + Tests support for CTDB IP allocation + + Copyright (C) Martin Schwenke 2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#ifndef __IPALLOC_READ_KNOWN_IPS_H__ +#define __IPALLOC_READ_KNOWN_IPS_H__ + +#include +#include + +#include "protocol/protocol.h" + +struct ctdb_public_ip_list * ipalloc_read_known_ips(TALLOC_CTX *ctx, + int numnodes, + bool multi); + +#endif /* __IPALLOC_READ_KNOWN_IPS_H__ */ diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/pidfile_test.c samba-4.6.5+dfsg/ctdb/tests/src/pidfile_test.c --- samba-4.5.8+dfsg/ctdb/tests/src/pidfile_test.c 2016-10-24 19:37:30.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/src/pidfile_test.c 2017-01-11 07:55:14.000000000 +0000 @@ -43,6 +43,7 @@ assert(S_ISREG(st.st_mode)); fp = fopen(pidfile, "r"); + assert(fp != NULL); ret = fscanf(fp, "%d", &pid); assert(ret == 1); assert(pid == getpid()); diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/porting_tests.c samba-4.6.5+dfsg/ctdb/tests/src/porting_tests.c --- samba-4.5.8+dfsg/ctdb/tests/src/porting_tests.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/src/porting_tests.c 2017-01-11 07:55:14.000000000 +0000 @@ -31,7 +31,6 @@ #include "lib/util/blocking.h" #include "protocol/protocol.h" -#include "common/cmdline.h" #include "common/system.h" #include "common/logging.h" diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/protocol_client_test.c samba-4.6.5+dfsg/ctdb/tests/src/protocol_client_test.c --- samba-4.5.8+dfsg/ctdb/tests/src/protocol_client_test.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/src/protocol_client_test.c 2017-01-11 07:55:14.000000000 +0000 @@ -376,11 +376,6 @@ fill_ctdb_addr_info(mem_ctx, cd->data.addr_info); break; - case CTDB_CONTROL_RUN_EVENTSCRIPTS: - fill_ctdb_string(mem_ctx, &cd->data.event_str); - assert(cd->data.event_str != NULL); - break; - case CTDB_CONTROL_GET_CAPABILITIES: break; @@ -411,10 +406,6 @@ case CTDB_CONTROL_GET_NODEMAP: break; - case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS: - cd->data.event = rand_int(CTDB_EVENT_MAX); - break; - case CTDB_CONTROL_TRAVERSE_KILL: cd->data.traverse_start = talloc(mem_ctx, struct ctdb_traverse_start); assert(cd->data.traverse_start != NULL); @@ -442,16 +433,6 @@ cd->data.role = rand_int(2); break; - case CTDB_CONTROL_ENABLE_SCRIPT: - fill_ctdb_string(mem_ctx, &cd->data.script); - assert(cd->data.script != NULL); - break; - - case CTDB_CONTROL_DISABLE_SCRIPT: - fill_ctdb_string(mem_ctx, &cd->data.script); - assert(cd->data.script != NULL); - break; - case CTDB_CONTROL_SET_BAN_STATE: cd->data.ban_state = talloc(mem_ctx, struct ctdb_ban_state); assert(cd->data.ban_state != NULL); @@ -821,10 +802,6 @@ verify_ctdb_addr_info(cd->data.addr_info, cd2->data.addr_info); break; - case CTDB_CONTROL_RUN_EVENTSCRIPTS: - verify_ctdb_string(cd->data.event_str, cd2->data.event_str); - break; - case CTDB_CONTROL_GET_CAPABILITIES: break; @@ -851,10 +828,6 @@ case CTDB_CONTROL_GET_NODEMAP: break; - case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS: - assert(cd->data.event == cd2->data.event); - break; - case CTDB_CONTROL_TRAVERSE_KILL: verify_ctdb_traverse_start(cd->data.traverse_start, cd2->data.traverse_start); @@ -881,14 +854,6 @@ assert(cd->data.role == cd2->data.role); break; - case CTDB_CONTROL_ENABLE_SCRIPT: - verify_ctdb_string(cd->data.script, cd2->data.script); - break; - - case CTDB_CONTROL_DISABLE_SCRIPT: - verify_ctdb_string(cd->data.script, cd2->data.script); - break; - case CTDB_CONTROL_SET_BAN_STATE: verify_ctdb_ban_state(cd->data.ban_state, cd2->data.ban_state); break; @@ -1262,9 +1227,6 @@ case CTDB_CONTROL_DEL_PUBLIC_IP: break; - case CTDB_CONTROL_RUN_EVENTSCRIPTS: - break; - case CTDB_CONTROL_GET_CAPABILITIES: cd->data.caps = rand32(); break; @@ -1296,12 +1258,6 @@ fill_ctdb_node_map(mem_ctx, cd->data.nodemap); break; - case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS: - cd->data.script_list = talloc(mem_ctx, struct ctdb_script_list); - assert(cd->data.script_list != NULL); - fill_ctdb_script_list(mem_ctx, cd->data.script_list); - break; - case CTDB_CONTROL_TRAVERSE_KILL: break; @@ -1325,12 +1281,6 @@ case CTDB_CONTROL_SET_RECMASTERROLE: break; - case CTDB_CONTROL_ENABLE_SCRIPT: - break; - - case CTDB_CONTROL_DISABLE_SCRIPT: - break; - case CTDB_CONTROL_SET_BAN_STATE: break; @@ -1638,9 +1588,6 @@ case CTDB_CONTROL_DEL_PUBLIC_IP: break; - case CTDB_CONTROL_RUN_EVENTSCRIPTS: - break; - case CTDB_CONTROL_GET_CAPABILITIES: assert(cd->data.caps == cd2->data.caps); break; @@ -1663,11 +1610,6 @@ verify_ctdb_node_map(cd->data.nodemap, cd2->data.nodemap); break; - case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS: - verify_ctdb_script_list(cd->data.script_list, - cd2->data.script_list); - break; - case CTDB_CONTROL_TRAVERSE_KILL: break; @@ -1691,12 +1633,6 @@ case CTDB_CONTROL_SET_RECMASTERROLE: break; - case CTDB_CONTROL_ENABLE_SCRIPT: - break; - - case CTDB_CONTROL_DISABLE_SCRIPT: - break; - case CTDB_CONTROL_SET_BAN_STATE: break; @@ -1837,6 +1773,176 @@ } /* + * Functions to fill and verify eventd protocol structures + */ + +static void fill_ctdb_event_request_data(TALLOC_CTX *mem_ctx, + struct ctdb_event_request_data *r, + uint32_t command) +{ + r->command = command; + + switch (command) { + case CTDB_EVENT_COMMAND_RUN: + r->data.run = talloc(mem_ctx, struct ctdb_event_request_run); + assert(r->data.run != NULL); + + fill_ctdb_event_request_run(mem_ctx, r->data.run); + break; + + case CTDB_EVENT_COMMAND_STATUS: + r->data.status = talloc(mem_ctx, + struct ctdb_event_request_status); + assert(r->data.status != NULL); + + fill_ctdb_event_request_status(mem_ctx, r->data.status); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_LIST: + break; + + case CTDB_EVENT_COMMAND_SCRIPT_ENABLE: + r->data.script_enable = talloc(mem_ctx, + struct ctdb_event_request_script_enable); + assert(r->data.script_enable != NULL); + + fill_ctdb_event_request_script_enable(mem_ctx, + r->data.script_enable); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_DISABLE: + r->data.script_disable = talloc(mem_ctx, + struct ctdb_event_request_script_disable); + assert(r->data.script_disable != NULL); + + fill_ctdb_event_request_script_disable(mem_ctx, + r->data.script_disable); + break; + } +} + +static void verify_ctdb_event_request_data(struct ctdb_event_request_data *r, + struct ctdb_event_request_data *r2) +{ + assert(r->command == r2->command); + + switch (r->command) { + case CTDB_EVENT_COMMAND_RUN: + verify_ctdb_event_request_run(r->data.run, r2->data.run); + break; + + case CTDB_EVENT_COMMAND_STATUS: + verify_ctdb_event_request_status(r->data.status, + r2->data.status); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_LIST: + break; + + case CTDB_EVENT_COMMAND_SCRIPT_ENABLE: + verify_ctdb_event_request_script_enable(r->data.script_enable, + r2->data.script_enable); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_DISABLE: + verify_ctdb_event_request_script_disable(r->data.script_disable, + r2->data.script_disable); + break; + } +} + +static void fill_ctdb_event_reply_data(TALLOC_CTX *mem_ctx, + struct ctdb_event_reply_data *r, + uint32_t command) +{ + r->command = command; + r->result = rand32i(); + + switch (command) { + case CTDB_EVENT_COMMAND_STATUS: + r->data.status = talloc(mem_ctx, + struct ctdb_event_reply_status); + assert(r->data.status != NULL); + + fill_ctdb_event_reply_status(mem_ctx, r->data.status); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_LIST: + r->data.script_list = talloc(mem_ctx, + struct ctdb_event_reply_script_list); + assert(r->data.script_list != NULL); + + fill_ctdb_event_reply_script_list(mem_ctx, + r->data.script_list); + break; + } +} + +static void verify_ctdb_event_reply_data(struct ctdb_event_reply_data *r, + struct ctdb_event_reply_data *r2) +{ + assert(r->command == r2->command); + assert(r->result == r2->result); + + switch (r->command) { + case CTDB_EVENT_COMMAND_RUN: + break; + + case CTDB_EVENT_COMMAND_STATUS: + verify_ctdb_event_reply_status(r->data.status, + r2->data.status); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_LIST: + verify_ctdb_event_reply_script_list(r->data.script_list, + r2->data.script_list); + break; + + case CTDB_EVENT_COMMAND_SCRIPT_ENABLE: + break; + + case CTDB_EVENT_COMMAND_SCRIPT_DISABLE: + break; + } +} + +static void verify_ctdb_event_header(struct ctdb_event_header *h, + struct ctdb_event_header *h2) +{ + verify_buffer(h, h2, ctdb_event_header_len(h)); +} + +static void fill_ctdb_event_request(TALLOC_CTX *mem_ctx, + struct ctdb_event_request *r, + uint32_t command) +{ + ctdb_event_header_fill(&r->header, rand()); + fill_ctdb_event_request_data(mem_ctx, &r->rdata, command); +} + +static void verify_ctdb_event_request(struct ctdb_event_request *r, + struct ctdb_event_request *r2) +{ + verify_ctdb_event_header(&r->header, &r2->header); + verify_ctdb_event_request_data(&r->rdata, &r2->rdata); +} + +static void fill_ctdb_event_reply(TALLOC_CTX *mem_ctx, + struct ctdb_event_reply *r, + uint32_t command) +{ + ctdb_event_header_fill(&r->header, rand()); + fill_ctdb_event_reply_data(mem_ctx, &r->rdata, command); +} + +static void verify_ctdb_event_reply(struct ctdb_event_reply *r, + struct ctdb_event_reply *r2) +{ + verify_ctdb_event_header(&r->header, &r2->header); + verify_ctdb_event_reply_data(&r->rdata, &r2->rdata); +} + +/* * Functions to test marshalling */ @@ -2263,6 +2369,179 @@ talloc_free(mem_ctx); } +/* + * Functions to test eventd protocol marshalling + */ + +static void test_ctdb_event_header(void) +{ + TALLOC_CTX *mem_ctx; + size_t buflen; + struct ctdb_event_header h, h2; + int ret; + + printf("ctdb_event_header\n"); + fflush(stdout); + + mem_ctx = talloc_new(NULL); + assert(mem_ctx != NULL); + + ctdb_event_header_fill(&h, REQID); + + buflen = ctdb_event_header_len(&h); + ctdb_event_header_push(&h, BUFFER); + ret = ctdb_event_header_pull(BUFFER, buflen, mem_ctx, &h2); + assert(ret == 0); + + verify_ctdb_event_header(&h, &h2); + + talloc_free(mem_ctx); +} + +#define NUM_COMMANDS 5 + +static void test_event_request_data(void) +{ + TALLOC_CTX *mem_ctx; + size_t buflen; + struct ctdb_event_request_data rd, rd2; + uint32_t command; + int ret; + + printf("ctdb_event_request_data\n"); + fflush(stdout); + + for (command=1; command<=NUM_COMMANDS; command++) { + mem_ctx = talloc_new(NULL); + assert(mem_ctx != NULL); + + printf("%u.. ", command); + fflush(stdout); + fill_ctdb_event_request_data(mem_ctx, &rd, command); + buflen = ctdb_event_request_data_len(&rd); + ctdb_event_request_data_push(&rd, BUFFER); + ret = ctdb_event_request_data_pull(BUFFER, buflen, mem_ctx, &rd2); + assert(ret == 0); + verify_ctdb_event_request_data(&rd, &rd2); + + talloc_free(mem_ctx); + } + + printf("\n"); + fflush(stdout); +} + +static void test_event_reply_data(void) +{ + TALLOC_CTX *mem_ctx; + size_t buflen; + struct ctdb_event_reply_data rd, rd2; + uint32_t command; + int ret; + + printf("ctdb_event_reply_data\n"); + fflush(stdout); + + for (command=1; command<=NUM_COMMANDS; command++) { + mem_ctx = talloc_new(NULL); + assert(mem_ctx != NULL); + + printf("%u.. ", command); + fflush(stdout); + fill_ctdb_event_reply_data(mem_ctx, &rd, command); + buflen = ctdb_event_reply_data_len(&rd); + ctdb_event_reply_data_push(&rd, BUFFER); + ret = ctdb_event_reply_data_pull(BUFFER, buflen, mem_ctx, &rd2); + assert(ret == 0); + verify_ctdb_event_reply_data(&rd, &rd2); + + talloc_free(mem_ctx); + } + + printf("\n"); + fflush(stdout); +} + +static void test_event_request(void) +{ + TALLOC_CTX *mem_ctx; + uint8_t *buf; + size_t len, buflen; + int ret; + struct ctdb_event_request r, r2; + uint32_t command; + + printf("ctdb_event_request\n"); + fflush(stdout); + + for (command=1; command<=NUM_COMMANDS; command++) { + mem_ctx = talloc_new(NULL); + assert(mem_ctx != NULL); + + printf("%u.. ", command); + fflush(stdout); + fill_ctdb_event_request(mem_ctx, &r, command); + buflen = ctdb_event_request_len(&r); + buf = talloc_size(mem_ctx, buflen); + assert(buf != NULL); + len = 0; + ret = ctdb_event_request_push(&r, buf, &len); + assert(ret == EMSGSIZE); + assert(len == buflen); + ret = ctdb_event_request_push(&r, buf, &buflen); + assert(ret == 0); + ret = ctdb_event_request_pull(buf, buflen, mem_ctx, &r2); + assert(ret == 0); + assert(r2.header.length == buflen); + verify_ctdb_event_request(&r, &r2); + + talloc_free(mem_ctx); + } + + printf("\n"); + fflush(stdout); +} + +static void test_event_reply(void) +{ + TALLOC_CTX *mem_ctx; + uint8_t *buf; + size_t len, buflen; + int ret; + struct ctdb_event_reply r, r2; + uint32_t command; + + printf("ctdb_event_reply\n"); + fflush(stdout); + + for (command=1; command<=NUM_COMMANDS; command++) { + mem_ctx = talloc_new(NULL); + assert(mem_ctx != NULL); + + printf("%u.. ", command); + fflush(stdout); + fill_ctdb_event_reply(mem_ctx, &r, command); + buflen = ctdb_event_reply_len(&r); + buf = talloc_size(mem_ctx, buflen); + assert(buf != NULL); + len = 0; + ret = ctdb_event_reply_push(&r, buf, &len); + assert(ret == EMSGSIZE); + assert(len == buflen); + ret = ctdb_event_reply_push(&r, buf, &buflen); + assert(ret == 0); + ret = ctdb_event_reply_pull(buf, buflen, mem_ctx, &r2); + assert(ret == 0); + assert(r2.header.length == buflen); + verify_ctdb_event_reply(&r, &r2); + + talloc_free(mem_ctx); + } + + printf("\n"); + fflush(stdout); +} + int main(int argc, char *argv[]) { if (argc == 2) { @@ -2286,5 +2565,12 @@ test_req_message_test(); + test_ctdb_event_header(); + + test_event_request_data(); + test_event_reply_data(); + test_event_request(); + test_event_reply(); + return 0; } diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/protocol_types_test.c samba-4.6.5+dfsg/ctdb/tests/src/protocol_types_test.c --- samba-4.5.8+dfsg/ctdb/tests/src/protocol_types_test.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/src/protocol_types_test.c 2017-01-11 07:55:14.000000000 +0000 @@ -27,6 +27,7 @@ #include "protocol/protocol_header.c" #include "protocol/protocol_call.c" #include "protocol/protocol_control.c" +#include "protocol/protocol_event.c" #include "protocol/protocol_message.c" #include "protocol/protocol_packet.c" @@ -47,6 +48,11 @@ return val; } +static int32_t rand32i(void) +{ + return INT_MIN + random(); +} + static uint32_t rand32(void) { return random(); @@ -909,6 +915,107 @@ } } +static void fill_ctdb_event_request_run(TALLOC_CTX *mem_ctx, + struct ctdb_event_request_run *p) +{ + p->event = rand_int(CTDB_EVENT_MAX); + p->timeout = rand(); + fill_ctdb_string(mem_ctx, &p->arg_str); +} + +static void verify_ctdb_event_request_run(struct ctdb_event_request_run *p1, + struct ctdb_event_request_run *p2) +{ + assert(p1->event == p2->event); + assert(p1->timeout == p2->timeout); + verify_ctdb_string(p1->arg_str, p2->arg_str); +} + +static void fill_ctdb_event_request_status(TALLOC_CTX *mem_ctx, + struct ctdb_event_request_status *p) +{ + p->event = rand_int(CTDB_EVENT_MAX); + p->state = rand_int(3) + 1; +} + +static void verify_ctdb_event_request_status( + struct ctdb_event_request_status *p1, + struct ctdb_event_request_status *p2) +{ + assert(p1->event == p2->event); + assert(p1->state == p2->state); +} + +static void fill_ctdb_event_request_script_enable( + TALLOC_CTX *mem_ctx, + struct ctdb_event_request_script_enable *p) +{ + fill_ctdb_string(mem_ctx, &p->script_name); +} + +static void verify_ctdb_event_request_script_enable( + struct ctdb_event_request_script_enable *p1, + struct ctdb_event_request_script_enable *p2) +{ + verify_ctdb_string(p1->script_name, p2->script_name); +} + +static void fill_ctdb_event_request_script_disable( + TALLOC_CTX *mem_ctx, + struct ctdb_event_request_script_disable *p) +{ + fill_ctdb_string(mem_ctx, &p->script_name); +} + +static void verify_ctdb_event_request_script_disable( + struct ctdb_event_request_script_disable *p1, + struct ctdb_event_request_script_disable *p2) +{ + verify_ctdb_string(p1->script_name, p2->script_name); +} + +static void fill_ctdb_event_reply_status(TALLOC_CTX *mem_ctx, + struct ctdb_event_reply_status *p) +{ + if (rand_int(2) == 0) { + p->status = 0; + p->script_list = talloc(mem_ctx, struct ctdb_script_list); + assert(p->script_list != NULL); + fill_ctdb_script_list(mem_ctx, p->script_list); + } else { + p->status = rand32i(); + p->script_list = NULL; + } +} + +static void verify_ctdb_event_reply_status(struct ctdb_event_reply_status *p1, + struct ctdb_event_reply_status *p2) +{ + assert(p1->status == p2->status); + if (p1->script_list == NULL) { + assert(p1->script_list == p2->script_list); + } else { + verify_ctdb_script_list(p1->script_list, p2->script_list); + } +} + +static void fill_ctdb_event_reply_script_list( + TALLOC_CTX *mem_ctx, + struct ctdb_event_reply_script_list *p) +{ + p->script_list = talloc(mem_ctx, struct ctdb_script_list); + assert(p->script_list != NULL); + + fill_ctdb_script_list(mem_ctx, p->script_list); +} + +static void verify_ctdb_event_reply_script_list( + struct ctdb_event_reply_script_list *p1, + struct ctdb_event_reply_script_list *p2) +{ + verify_ctdb_script_list(p1->script_list, p2->script_list); +} + #ifndef PROTOCOL_TEST static void fill_ctdb_election_message(TALLOC_CTX *mem_ctx, @@ -1017,6 +1124,20 @@ * Functions to test marshalling routines */ +static void test_ctdb_int32(void) +{ + int32_t p1, p2; + size_t buflen; + int ret; + + p1 = rand32i(); + buflen = ctdb_int32_len(p1); + ctdb_int32_push(p1, BUFFER); + ret = ctdb_int32_pull(BUFFER, buflen, NULL, &p2); + assert(ret == 0); + assert(p1 == p2); +} + static void test_ctdb_uint32(void) { uint32_t p1, p2; @@ -1206,6 +1327,15 @@ DEFINE_TEST(struct ctdb_disable_message, ctdb_disable_message); DEFINE_TEST(struct ctdb_g_lock_list, ctdb_g_lock_list); +DEFINE_TEST(struct ctdb_event_request_run, ctdb_event_request_run); +DEFINE_TEST(struct ctdb_event_request_status, ctdb_event_request_status); +DEFINE_TEST(struct ctdb_event_request_script_enable, + ctdb_event_request_script_enable); +DEFINE_TEST(struct ctdb_event_request_script_disable, + ctdb_event_request_script_disable); +DEFINE_TEST(struct ctdb_event_reply_status, ctdb_event_reply_status); +DEFINE_TEST(struct ctdb_event_reply_script_list, ctdb_event_reply_script_list); + static void test_ctdb_rec_buffer_read_write(void) { TALLOC_CTX *mem_ctx = talloc_new(NULL); @@ -1259,6 +1389,7 @@ srandom(seed); } + test_ctdb_int32(); test_ctdb_uint32(); test_ctdb_uint64(); test_ctdb_double(); @@ -1312,6 +1443,13 @@ TEST_FUNC(ctdb_disable_message)(); TEST_FUNC(ctdb_g_lock_list)(); + TEST_FUNC(ctdb_event_request_run)(); + TEST_FUNC(ctdb_event_request_status)(); + TEST_FUNC(ctdb_event_request_script_enable)(); + TEST_FUNC(ctdb_event_request_script_disable)(); + TEST_FUNC(ctdb_event_reply_status)(); + TEST_FUNC(ctdb_event_reply_script_list)(); + test_ctdb_rec_buffer_read_write(); return 0; diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/rb_perftest.c samba-4.6.5+dfsg/ctdb/tests/src/rb_perftest.c --- samba-4.5.8+dfsg/ctdb/tests/src/rb_perftest.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/src/rb_perftest.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,123 +0,0 @@ -/* - simple rb vs dlist benchmark - - Copyright (C) Ronnie Sahlberg 2007 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . -*/ - -#include "includes.h" -#include "lib/events/events.h" -#include "lib/util/dlinklist.h" -#include "system/filesys.h" -#include "popt.h" -#include "cmdline.h" - -#include -#include -#include "common/rb_tree.h" - -static struct timeval tp1,tp2; - -static void start_timer(void) -{ - gettimeofday(&tp1,NULL); -} - -static double end_timer(void) -{ - gettimeofday(&tp2,NULL); - return (tp2.tv_sec + (tp2.tv_usec*1.0e-6)) - - (tp1.tv_sec + (tp1.tv_usec*1.0e-6)); -} - - -static int num_records = 1000; - - -struct list_node { - struct list_node *prev, *next; -}; - -/* - main program -*/ -int main(int argc, const char *argv[]) -{ - struct poptOption popt_options[] = { - POPT_AUTOHELP - POPT_CTDB_CMDLINE - { "num-records", 'r', POPT_ARG_INT, &num_records, 0, "num_records", "integer" }, - POPT_TABLEEND - }; - int opt; - const char **extra_argv; - int extra_argc = 0; - int ret; - poptContext pc; - struct event_context *ev; - double elapsed; - int i; - trbt_tree_t *tree; - struct list_node *list, *list_new, *list_head=NULL; - - pc = poptGetContext(argv[0], argc, argv, popt_options, POPT_CONTEXT_KEEP_FIRST); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - default: - fprintf(stderr, "Invalid option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); - exit(1); - } - } - - /* setup the remaining options for the main program to use */ - extra_argv = poptGetArgs(pc); - if (extra_argv) { - extra_argv++; - while (extra_argv[extra_argc]) extra_argc++; - } - - ev = event_context_init(NULL); - - - printf("testing tree insert for %d records\n", num_records); - tree = trbt_create(NULL); - start_timer(); - for (i=0;inext;list=list->next) { - /* the events code does a timeval_compare */ - timeval_compare(&tp1, &tp2); - } - - list_new=talloc(NULL, struct list_node); - DLIST_ADD_AFTER(list_head, list_new, list); - } - elapsed=end_timer(); - printf("%f seconds\n",(float)elapsed); - - return 0; -} diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/run_proc_test.c samba-4.6.5+dfsg/ctdb/tests/src/run_proc_test.c --- samba-4.5.8+dfsg/ctdb/tests/src/run_proc_test.c 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/src/run_proc_test.c 2017-01-11 07:55:14.000000000 +0000 @@ -0,0 +1,105 @@ +/* + run_proc test wrapper + + Copyright (C) Amitay Isaacs 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include "replace.h" + +#include +#include + +#include "common/db_hash.c" +#include "common/run_proc.c" + +int main(int argc, const char **argv) +{ + TALLOC_CTX *mem_ctx; + struct tevent_context *ev; + struct tevent_req *req; + struct run_proc_context *run_ctx; + struct timeval tv; + char *output; + struct run_proc_result result; + pid_t pid; + int timeout, ret; + bool status; + + if (argc < 3) { + fprintf(stderr, "Usage: %s \n", + argv[0]); + exit(1); + } + + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + fprintf(stderr, "talloc_new() failed\n"); + exit(1); + } + + ev = tevent_context_init(mem_ctx); + if (ev == NULL) { + fprintf(stderr, "tevent_context_init() failed\n"); + exit(1); + } + + timeout = atoi(argv[1]); + if (timeout <= 0) { + tv = tevent_timeval_zero(); + } else { + tv = tevent_timeval_current_ofs(timeout, 0); + } + + ret = run_proc_init(mem_ctx, ev, &run_ctx); + if (ret != 0) { + fprintf(stderr, "run_proc_init() failed, ret=%d\n", ret); + exit(1); + } + + req = run_proc_send(mem_ctx, ev, run_ctx, argv[2], &argv[2], tv); + if (req == NULL) { + fprintf(stderr, "run_proc_send() failed\n"); + exit(1); + } + + tevent_req_poll(req, ev); + + status = run_proc_recv(req, &ret, &result, &pid, mem_ctx, &output); + if (! status) { + fprintf(stderr, "run_proc_recv() failed, ret=%d\n", ret); + exit(1); + } + + if (result.sig > 0) { + printf("Process exited with signal %d\n", result.sig); + } else if (result.err > 0) { + printf("Process exited with error %d\n", result.err); + } else { + printf("Process exited with status %d\n", result.status); + } + + if (pid != -1) { + printf("Child = %d\n", pid); + } + + if (output != NULL) { + printf("Output = (%s)\n", output); + } + + talloc_free(mem_ctx); + + exit(0); +} diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/sock_daemon_test.c samba-4.6.5+dfsg/ctdb/tests/src/sock_daemon_test.c --- samba-4.5.8+dfsg/ctdb/tests/src/sock_daemon_test.c 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/src/sock_daemon_test.c 2017-01-26 12:19:08.000000000 +0000 @@ -0,0 +1,992 @@ +/* + sock daemon tests + + Copyright (C) Amitay Isaacs 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include "replace.h" +#include "system/filesys.h" +#include "system/network.h" +#include "system/wait.h" + +#include + +#include "common/logging.c" +#include "common/pkt_read.c" +#include "common/pkt_write.c" +#include "common/comm.c" +#include "common/pidfile.c" +#include "common/sock_daemon.c" +#include "common/sock_io.c" + +static struct tevent_req *dummy_read_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sock_client_context *client, + uint8_t *buf, size_t buflen, + void *private_data) +{ + return NULL; +} + +static bool dummy_read_recv(struct tevent_req *req, int *perr) +{ + if (perr != NULL) { + *perr = EINVAL; + } + return false; +} + +static struct sock_socket_funcs dummy_socket_funcs = { + .read_send = dummy_read_send, + .read_recv = dummy_read_recv, +}; + +static void test1(TALLOC_CTX *mem_ctx, const char *pidfile, + const char *sockpath) +{ + struct sock_daemon_context *sockd; + struct stat st; + int ret; + + ret = sock_daemon_setup(mem_ctx, "test1", "file:", "NOTICE", pidfile, + NULL, NULL, &sockd); + assert(ret == 0); + assert(sockd != NULL); + + ret = stat(pidfile, &st); + assert(ret == 0); + assert(S_ISREG(st.st_mode)); + + ret = sock_daemon_add_unix(sockd, sockpath, &dummy_socket_funcs, NULL); + assert(ret == 0); + + ret = stat(sockpath, &st); + assert(ret == 0); + assert(S_ISSOCK(st.st_mode)); + + talloc_free(sockd); + + ret = stat(pidfile, &st); + assert(ret == -1); + + ret = stat(sockpath, &st); + assert(ret == -1); +} + +static void test2_startup(void *private_data) +{ + int fd = *(int *)private_data; + int ret = 1; + ssize_t nwritten; + + nwritten = write(fd, &ret, sizeof(ret)); + assert(nwritten == sizeof(ret)); +} + +static void test2_reconfigure(void *private_data) +{ + int fd = *(int *)private_data; + int ret = 2; + ssize_t nwritten; + + nwritten = write(fd, &ret, sizeof(ret)); + assert(nwritten == sizeof(ret)); +} + +static void test2_shutdown(void *private_data) +{ + int fd = *(int *)private_data; + int ret = 3; + ssize_t nwritten; + + nwritten = write(fd, &ret, sizeof(ret)); + assert(nwritten == sizeof(ret)); +} + +static struct sock_daemon_funcs test2_funcs = { + .startup = test2_startup, + .reconfigure = test2_reconfigure, + .shutdown = test2_shutdown, +}; + +static void test2(TALLOC_CTX *mem_ctx, const char *pidfile, + const char *sockpath) +{ + struct stat st; + int fd[2]; + pid_t pid, pid2; + int ret; + ssize_t n; + + ret = pipe(fd); + assert(ret == 0); + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + struct tevent_context *ev; + struct sock_daemon_context *sockd; + + close(fd[0]); + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + ret = sock_daemon_setup(mem_ctx, "test2", "file:", "NOTICE", + pidfile, &test2_funcs, &fd[1], &sockd); + assert(ret == 0); + + ret = sock_daemon_add_unix(sockd, sockpath, + &dummy_socket_funcs, NULL); + assert(ret == 0); + + ret = sock_daemon_run(ev, sockd, -1); + assert(ret == EINTR); + + exit(0); + } + + close(fd[1]); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 1); + + ret = kill(pid, SIGHUP); + assert(ret == 0); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 2); + + ret = kill(pid, SIGUSR1); + assert(ret == 0); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 2); + + ret = kill(pid, SIGTERM); + assert(ret == 0); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 3); + + pid2 = waitpid(pid, &ret, 0); + assert(pid2 == pid); + assert(WEXITSTATUS(ret) == 0); + + close(fd[0]); + + ret = stat(pidfile, &st); + assert(ret == -1); + + ret = stat(sockpath, &st); + assert(ret == -1); +} + +static void test3(TALLOC_CTX *mem_ctx, const char *pidfile, + const char *sockpath) +{ + struct stat st; + pid_t pid_watch, pid, pid2; + int ret; + + pid_watch = fork(); + assert(pid_watch != -1); + + if (pid_watch == 0) { + sleep(10); + exit(0); + } + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + struct tevent_context *ev; + struct sock_daemon_context *sockd; + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + ret = sock_daemon_setup(mem_ctx, "test3", "file:", "NOTICE", + NULL, NULL, NULL, &sockd); + assert(ret == 0); + + ret = sock_daemon_add_unix(sockd, sockpath, + &dummy_socket_funcs, NULL); + assert(ret == 0); + + ret = sock_daemon_run(ev, sockd, pid_watch); + assert(ret == ESRCH); + + exit(0); + } + + pid2 = waitpid(pid_watch, &ret, 0); + assert(pid2 == pid_watch); + assert(WEXITSTATUS(ret) == 0); + + pid2 = waitpid(pid, &ret, 0); + assert(pid2 == pid); + assert(WEXITSTATUS(ret) == 0); + + ret = stat(pidfile, &st); + assert(ret == -1); + + ret = stat(sockpath, &st); + assert(ret == -1); +} + +struct test4_wait_state { +}; + +static void test4_wait_done(struct tevent_req *subreq); + +static struct tevent_req *test4_wait_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + void *private_data) +{ + struct tevent_req *req, *subreq; + struct test4_wait_state *state; + + req = tevent_req_create(mem_ctx, &state, struct test4_wait_state); + if (req == NULL) { + return NULL; + } + + subreq = tevent_wakeup_send(state, ev, + tevent_timeval_current_ofs(10,0)); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, test4_wait_done, req); + + return req; +} + +static void test4_wait_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + bool status; + + status = tevent_wakeup_recv(subreq); + TALLOC_FREE(subreq); + + if (! status) { + tevent_req_error(req, EIO); + } else { + tevent_req_done(req); + } +} + +static bool test4_wait_recv(struct tevent_req *req, int *perr) +{ + int ret; + + if (tevent_req_is_unix_error(req, &ret)) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + return true; +} + +static struct sock_daemon_funcs test4_funcs = { + .wait_send = test4_wait_send, + .wait_recv = test4_wait_recv, +}; + +static void test4(TALLOC_CTX *mem_ctx, const char *pidfile, + const char *sockpath) +{ + struct stat st; + pid_t pid, pid2; + int ret; + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + struct tevent_context *ev; + struct sock_daemon_context *sockd; + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + ret = sock_daemon_setup(mem_ctx, "test4", "file:", "NOTICE", + pidfile, &test4_funcs, NULL, &sockd); + assert(ret == 0); + + ret = sock_daemon_run(ev, sockd, -1); + assert(ret == 0); + + exit(0); + } + + pid2 = waitpid(pid, &ret, 0); + assert(pid2 == pid); + assert(WEXITSTATUS(ret) == 0); + + ret = stat(pidfile, &st); + assert(ret == -1); + + ret = stat(sockpath, &st); + assert(ret == -1); +} + +#define TEST5_MAX_CLIENTS 10 + +struct test5_pkt { + uint32_t len; + int data; +}; + +struct test5_client_state { + int id; + int fd; + bool done; +}; + +static void test5_client_callback(uint8_t *buf, size_t buflen, + void *private_data) +{ + struct test5_client_state *state = + (struct test5_client_state *)private_data; + struct test5_pkt *pkt; + ssize_t n; + int ret; + + if (buf == NULL) { + assert(buflen == 0); + + ret = 0; + } else { + assert(buflen == sizeof(struct test5_pkt)); + pkt = (struct test5_pkt *)buf; + assert(pkt->len == sizeof(struct test5_pkt)); + + ret = pkt->data; + } + + assert(state->fd != -1); + + n = write(state->fd, (void *)&ret, sizeof(int)); + assert(n == sizeof(int)); + + state->done = true; +} + +static int test5_client(const char *sockpath, int id) +{ + pid_t pid; + int fd[2]; + int ret; + ssize_t n; + + ret = pipe(fd); + assert(ret == 0); + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + struct tevent_context *ev; + struct test5_client_state state; + struct sock_queue *queue; + struct test5_pkt pkt; + int conn; + + close(fd[0]); + + ev = tevent_context_init(NULL); + assert(ev != NULL); + + conn = sock_connect(sockpath); + assert(conn != -1); + + state.id = id; + state.fd = fd[1]; + state.done = false; + + queue = sock_queue_setup(ev, ev, conn, + test5_client_callback, &state); + assert(queue != NULL); + + pkt.len = 8; + pkt.data = 0xbaba; + + ret = sock_queue_write(queue, (uint8_t *)&pkt, + sizeof(struct test5_pkt)); + assert(ret == 0); + + while (! state.done) { + tevent_loop_once(ev); + } + + close(fd[0]); + state.fd = -1; + + sleep(10); + exit(0); + } + + close(fd[1]); + + ret = 0; + n = read(fd[0], &ret, sizeof(ret)); + if (n == 0) { + fprintf(stderr, "client id %d read 0 bytes\n", id); + } + assert(n == 0 || n == sizeof(ret)); + + close(fd[0]); + + return ret; +} + +struct test5_server_state { + int num_clients; +}; + +static bool test5_connect(struct sock_client_context *client, + void *private_data) +{ + struct test5_server_state *state = + (struct test5_server_state *)private_data; + + if (state->num_clients == TEST5_MAX_CLIENTS) { + return false; + } + + state->num_clients += 1; + assert(state->num_clients <= TEST5_MAX_CLIENTS); + return true; +} + +static void test5_disconnect(struct sock_client_context *client, + void *private_data) +{ + struct test5_server_state *state = + (struct test5_server_state *)private_data; + + state->num_clients -= 1; + assert(state->num_clients >= 0); +} + +struct test5_read_state { + struct test5_pkt reply; +}; + +static void test5_read_done(struct tevent_req *subreq); + +static struct tevent_req *test5_read_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sock_client_context *client, + uint8_t *buf, size_t buflen, + void *private_data) +{ + struct test5_server_state *server_state = + (struct test5_server_state *)private_data; + struct tevent_req *req, *subreq; + struct test5_read_state *state; + struct test5_pkt *pkt; + + req = tevent_req_create(mem_ctx, &state, struct test5_read_state); + assert(req != NULL); + + assert(buflen == sizeof(struct test5_pkt)); + + pkt = (struct test5_pkt *)buf; + assert(pkt->data == 0xbaba); + + state->reply.len = sizeof(struct test5_pkt); + state->reply.data = server_state->num_clients; + + subreq = sock_socket_write_send(state, ev, client, + (uint8_t *)&state->reply, + state->reply.len); + assert(subreq != NULL); + + tevent_req_set_callback(subreq, test5_read_done, req); + + return req; +} + +static void test5_read_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + int ret; + bool status; + + status = sock_socket_write_recv(subreq, &ret); + TALLOC_FREE(subreq); + if (! status) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static bool test5_read_recv(struct tevent_req *req, int *perr) +{ + int ret; + + if (tevent_req_is_unix_error(req, &ret)) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + return true; +} + +static struct sock_socket_funcs test5_client_funcs = { + .connect = test5_connect, + .disconnect = test5_disconnect, + .read_send = test5_read_send, + .read_recv = test5_read_recv, +}; + +static void test5_startup(void *private_data) +{ + int fd = *(int *)private_data; + int ret = 1; + ssize_t nwritten; + + nwritten = write(fd, &ret, sizeof(ret)); + assert(nwritten == sizeof(ret)); + close(fd); +} + +static struct sock_daemon_funcs test5_funcs = { + .startup = test5_startup, +}; + +static void test5(TALLOC_CTX *mem_ctx, const char *pidfile, + const char *sockpath) +{ + pid_t pid_server, pid; + int fd[2], ret, i; + ssize_t n; + + pid = getpid(); + + ret = pipe(fd); + assert(ret == 0); + + pid_server = fork(); + assert(pid_server != -1); + + if (pid_server == 0) { + struct tevent_context *ev; + struct sock_daemon_context *sockd; + struct test5_server_state state; + + close(fd[0]); + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + ret = sock_daemon_setup(mem_ctx, "test5", "file:", "NOTICE", + pidfile, &test5_funcs, &fd[1], &sockd); + assert(ret == 0); + + state.num_clients = 0; + + ret = sock_daemon_add_unix(sockd, sockpath, + &test5_client_funcs, &state); + assert(ret == 0); + + ret = sock_daemon_run(ev, sockd, pid); + assert(ret == EINTR); + + exit(0); + } + + close(fd[1]); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 1); + + close(fd[0]); + + for (i=0; i<100; i++) { + ret = test5_client(sockpath, i); + if (i < TEST5_MAX_CLIENTS) { + assert(ret == i+1); + } else { + assert(ret == 0); + } + } + + for (i=0; i<100; i++) { + pid = wait(&ret); + assert(pid != -1); + } + + ret = kill(pid_server, SIGTERM); + assert(ret == 0); +} + +struct test6_pkt { + uint32_t len; + uint32_t data; +}; + +struct test6_client_state { + bool done; +}; + +static void test6_client_callback(uint8_t *buf, size_t buflen, + void *private_data) +{ + struct test6_client_state *state = + (struct test6_client_state *)private_data; + struct test6_pkt *pkt; + + assert(buflen == sizeof(struct test6_pkt)); + pkt = (struct test6_pkt *)buf; + assert(pkt->len == sizeof(struct test6_pkt)); + assert(pkt->data == 0xffeeddcc); + + state->done = true; +} + +static void test6_client(const char *sockpath) +{ + struct tevent_context *ev; + struct test6_client_state state; + struct sock_queue *queue; + struct test6_pkt pkt; + int conn, ret; + + ev = tevent_context_init(NULL); + assert(ev != NULL); + + conn = sock_connect(sockpath); + assert(conn != -1); + + state.done = false; + + queue = sock_queue_setup(ev, ev, conn, + test6_client_callback, &state); + assert(queue != NULL); + + pkt.len = 8; + pkt.data = 0xaabbccdd; + + ret = sock_queue_write(queue, (uint8_t *)&pkt, + sizeof(struct test6_pkt)); + assert(ret == 0); + + while (! state.done) { + tevent_loop_once(ev); + } + + talloc_free(ev); +} + +struct test6_server_state { + struct sock_daemon_context *sockd; + int fd, done; +}; + +struct test6_read_state { + struct test6_server_state *server_state; + struct test6_pkt reply; +}; + +static void test6_read_done(struct tevent_req *subreq); + +static struct tevent_req *test6_read_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sock_client_context *client, + uint8_t *buf, size_t buflen, + void *private_data) +{ + struct test6_server_state *server_state = + (struct test6_server_state *)private_data; + struct tevent_req *req, *subreq; + struct test6_read_state *state; + struct test6_pkt *pkt; + + req = tevent_req_create(mem_ctx, &state, struct test6_read_state); + assert(req != NULL); + + state->server_state = server_state; + + assert(buflen == sizeof(struct test6_pkt)); + + pkt = (struct test6_pkt *)buf; + assert(pkt->data == 0xaabbccdd); + + state->reply.len = sizeof(struct test6_pkt); + state->reply.data = 0xffeeddcc; + + subreq = sock_socket_write_send(state, ev, client, + (uint8_t *)&state->reply, + state->reply.len); + assert(subreq != NULL); + + tevent_req_set_callback(subreq, test6_read_done, req); + + return req; +} + +static void test6_read_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct test6_read_state *state = tevent_req_data( + req, struct test6_read_state); + int ret; + bool status; + + status = sock_socket_write_recv(subreq, &ret); + TALLOC_FREE(subreq); + if (! status) { + tevent_req_error(req, ret); + return; + } + + state->server_state->done = 1; + tevent_req_done(req); +} + +static bool test6_read_recv(struct tevent_req *req, int *perr) +{ + int ret; + + if (tevent_req_is_unix_error(req, &ret)) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + return true; +} + +static struct sock_socket_funcs test6_client_funcs = { + .read_send = test6_read_send, + .read_recv = test6_read_recv, +}; + +static void test6_startup(void *private_data) +{ + struct test6_server_state *server_state = + (struct test6_server_state *)private_data; + int ret = 1; + ssize_t nwritten; + + nwritten = write(server_state->fd, &ret, sizeof(ret)); + assert(nwritten == sizeof(ret)); + close(server_state->fd); + server_state->fd = -1; +} + +struct test6_wait_state { + struct test6_server_state *server_state; +}; + +static void test6_wait_done(struct tevent_req *subreq); + +static struct tevent_req *test6_wait_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + void *private_data) +{ + struct tevent_req *req, *subreq; + struct test6_wait_state *state; + + req = tevent_req_create(mem_ctx, &state, struct test6_wait_state); + if (req == NULL) { + return NULL; + } + + state->server_state = (struct test6_server_state *)private_data; + + subreq = tevent_wakeup_send(state, ev, + tevent_timeval_current_ofs(10,0)); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, test6_wait_done, req); + + return req; +} + +static void test6_wait_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct test6_wait_state *state = tevent_req_data( + req, struct test6_wait_state); + bool status; + + status = tevent_wakeup_recv(subreq); + TALLOC_FREE(subreq); + if (! status) { + tevent_req_error(req, EIO); + return; + } + + if (state->server_state->done == 0) { + tevent_req_error(req, EIO); + return; + } + + tevent_req_done(req); +} + +static bool test6_wait_recv(struct tevent_req *req, int *perr) +{ + int ret; + + if (tevent_req_is_unix_error(req, &ret)) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + return true; +} + +static struct sock_daemon_funcs test6_funcs = { + .startup = test6_startup, + .wait_send = test6_wait_send, + .wait_recv = test6_wait_recv, +}; + +static void test6(TALLOC_CTX *mem_ctx, const char *pidfile, + const char *sockpath) +{ + pid_t pid_server, pid; + int fd[2], ret; + ssize_t n; + + pid = getpid(); + + ret = pipe(fd); + assert(ret == 0); + + pid_server = fork(); + assert(pid_server != -1); + + if (pid_server == 0) { + struct tevent_context *ev; + struct sock_daemon_context *sockd; + struct test6_server_state server_state = { 0 }; + + close(fd[0]); + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + server_state.fd = fd[1]; + + ret = sock_daemon_setup(mem_ctx, "test6", "file:", "NOTICE", + pidfile, &test6_funcs, &server_state, + &sockd); + assert(ret == 0); + + server_state.sockd = sockd; + server_state.done = 0; + + ret = sock_daemon_add_unix(sockd, sockpath, + &test6_client_funcs, &server_state); + assert(ret == 0); + + ret = sock_daemon_run(ev, sockd, pid); + assert(ret == 0); + + exit(0); + } + + close(fd[1]); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 1); + + close(fd[0]); + + test6_client(sockpath); + + pid = wait(&ret); + assert(pid != -1); +} + +int main(int argc, const char **argv) +{ + TALLOC_CTX *mem_ctx; + const char *pidfile, *sockpath; + int num; + + if (argc != 4) { + fprintf(stderr, "%s \n", argv[0]); + exit(1); + } + + pidfile = argv[1]; + sockpath = argv[2]; + num = atoi(argv[3]); + + mem_ctx = talloc_new(NULL); + assert(mem_ctx != NULL); + + switch (num) { + case 1: + test1(mem_ctx, pidfile, sockpath); + break; + + case 2: + test2(mem_ctx, pidfile, sockpath); + break; + + case 3: + test3(mem_ctx, pidfile, sockpath); + break; + + case 4: + test4(mem_ctx, pidfile, sockpath); + break; + + case 5: + test5(mem_ctx, pidfile, sockpath); + break; + + case 6: + test6(mem_ctx, pidfile, sockpath); + break; + + default: + fprintf(stderr, "Unknown test number %d\n", num); + } + + return 0; +} diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/sock_io_test.c samba-4.6.5+dfsg/ctdb/tests/src/sock_io_test.c --- samba-4.5.8+dfsg/ctdb/tests/src/sock_io_test.c 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/src/sock_io_test.c 2017-01-26 12:18:16.000000000 +0000 @@ -0,0 +1,283 @@ +/* + sock I/O tests + + Copyright (C) Amitay Isaacs 2017 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include "replace.h" +#include "system/filesys.h" +#include "system/network.h" +#include "system/wait.h" + +#include + +#include "common/sock_io.c" + +static int socket_init(const char *sockpath) +{ + struct sockaddr_un addr; + int fd, ret; + size_t len; + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + + len = strlcpy(addr.sun_path, sockpath, sizeof(addr.sun_path)); + assert(len < sizeof(addr.sun_path)); + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + assert(fd != -1); + + ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); + assert(ret != -1); + + ret = listen(fd, 10); + assert(ret != -1); + + return fd; +} + +static void test1_writer(int fd) +{ + uint8_t buf[1024]; + ssize_t nwritten; + uint32_t len; + + for (len = 10; len < 1000; len += 10) { + int value = len / 10; + uint32_t buflen = len + sizeof(uint32_t); + + memset(buf, value, buflen); + memcpy(buf, &buflen, sizeof(uint32_t)); + + nwritten = sys_write(fd, buf, buflen); + assert(nwritten == buflen); + } +} + +struct test1_reader_state { + size_t pkt_len; + bool done; +}; + +static void test1_reader(uint8_t *buf, size_t buflen, void *private_data) +{ + struct test1_reader_state *state = + (struct test1_reader_state *)private_data; + + if (buflen == 0) { + state->done = true; + return; + } + + assert(buflen == state->pkt_len); + + state->pkt_len += 10; +} + +static void test1(TALLOC_CTX *mem_ctx, const char *sockpath) +{ + struct test1_reader_state state; + struct tevent_context *ev; + struct sock_queue *queue; + pid_t pid; + int pfd[2], fd, ret; + ssize_t n; + + ret = pipe(pfd); + assert(ret == 0); + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + int newfd; + + close(pfd[0]); + + fd = socket_init(sockpath); + assert(fd != -1); + + ret = 1; + n = sys_write(pfd[1], &ret, sizeof(int)); + assert(n == sizeof(int)); + + newfd = accept(fd, NULL, NULL); + assert(newfd != -1); + + test1_writer(newfd); + close(newfd); + unlink(sockpath); + + exit(0); + } + + close(pfd[1]); + + n = sys_read(pfd[0], &ret, sizeof(int)); + assert(n == sizeof(int)); + assert(ret == 1); + + close(pfd[0]); + + fd = sock_connect(sockpath); + assert(fd != -1); + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + state.pkt_len = 10 + sizeof(uint32_t); + state.done = false; + + queue = sock_queue_setup(mem_ctx, ev, fd, test1_reader, &state); + assert(queue != NULL); + + while (! state.done) { + tevent_loop_once(ev); + } + + talloc_free(queue); + talloc_free(ev); + + pid = wait(&ret); + assert(pid != -1); +} + +static void test2_reader(int fd) +{ + uint8_t buf[1024]; + size_t pkt_len = 10 + sizeof(uint32_t); + ssize_t n; + + while (1) { + n = sys_read(fd, buf, 1024); + assert(n != -1); + + if (n == 0) { + return; + } + + assert(n == pkt_len); + pkt_len += 10; + } +} + +static void test2_dummy_reader(uint8_t *buf, size_t buflen, + void *private_data) +{ + assert(buflen == -1); +} + +static void test2_writer(struct sock_queue *queue) +{ + uint8_t buf[1024]; + uint32_t len; + int ret; + + for (len = 10; len < 1000; len += 10) { + int value = len / 10; + uint32_t buflen = len + sizeof(uint32_t); + + memset(buf, value, buflen); + memcpy(buf, &buflen, sizeof(uint32_t)); + + ret = sock_queue_write(queue, buf, buflen); + assert(ret == 0); + } +} + +static void test2(TALLOC_CTX *mem_ctx, const char *sockpath) +{ + struct tevent_context *ev; + struct sock_queue *queue; + pid_t pid; + int pfd[2], fd, ret; + ssize_t n; + + ret = pipe(pfd); + assert(ret == 0); + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + int newfd; + + close(pfd[0]); + + fd = socket_init(sockpath); + assert(fd != -1); + + ret = 1; + n = sys_write(pfd[1], &ret, sizeof(int)); + assert(n == sizeof(int)); + + newfd = accept(fd, NULL, NULL); + assert(newfd != -1); + + test2_reader(newfd); + close(newfd); + unlink(sockpath); + + exit(0); + } + + close(pfd[1]); + + n = sys_read(pfd[0], &ret, sizeof(int)); + assert(n == sizeof(int)); + assert(ret == 1); + + close(pfd[0]); + + fd = sock_connect(sockpath); + assert(fd != -1); + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + queue = sock_queue_setup(mem_ctx, ev, fd, test2_dummy_reader, NULL); + assert(queue != NULL); + + test2_writer(queue); + + talloc_free(queue); + talloc_free(ev); + + pid = wait(&ret); + assert(pid != -1); +} + +int main(int argc, const char **argv) +{ + TALLOC_CTX *mem_ctx; + const char *sockpath; + + if (argc != 2) { + fprintf(stderr, "%s \n", argv[0]); + exit(1); + } + + sockpath = argv[1]; + + mem_ctx = talloc_new(NULL); + assert(mem_ctx != NULL); + + test1(mem_ctx, sockpath); + test2(mem_ctx, sockpath); + + return 0; +} diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/test_options.c samba-4.6.5+dfsg/ctdb/tests/src/test_options.c --- samba-4.5.8+dfsg/ctdb/tests/src/test_options.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/src/test_options.c 2017-01-11 07:55:14.000000000 +0000 @@ -88,7 +88,7 @@ static bool verify_options_basic(struct test_options *opts) { - enum debug_level log_level; + int log_level; bool status; status = debug_level_parse(opts->debugstr, &log_level); @@ -98,7 +98,7 @@ return false; } - DEBUGLEVEL = debug_level_to_int(log_level); + DEBUGLEVEL = log_level; return true; } diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/transaction_loop.c samba-4.6.5+dfsg/ctdb/tests/src/transaction_loop.c --- samba-4.5.8+dfsg/ctdb/tests/src/transaction_loop.c 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/src/transaction_loop.c 2017-01-11 07:55:14.000000000 +0000 @@ -162,6 +162,7 @@ ret = ctdb_transaction_fetch_record(state->h, state->key, state, &data); if (ret != 0) { + fprintf(stderr, "transaction fetch record failed\n"); tevent_req_error(req, ret); return; } @@ -246,6 +247,7 @@ status = tevent_wakeup_recv(subreq); TALLOC_FREE(subreq); if (! status) { + fprintf(stderr, "tevent wakeup failed\n"); tevent_req_error(req, EIO); return; } @@ -390,7 +392,7 @@ status = transaction_loop_recv(req, &ret); if (! status) { - fprintf(stderr, "transaction loop test failed\n"); + fprintf(stderr, "transaction loop test failed, ret=%d\n", ret); exit(1); } diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/lcp2.001.sh samba-4.6.5+dfsg/ctdb/tests/takeover/lcp2.001.sh --- samba-4.5.8+dfsg/ctdb/tests/takeover/lcp2.001.sh 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/takeover/lcp2.001.sh 2017-01-11 07:55:14.000000000 +0000 @@ -4,7 +4,7 @@ define_test "3 nodes, 3 -> 1 healthy" -export CTDB_TEST_LOGLEVEL=0 +export CTDB_TEST_LOGLEVEL=ERR required_result < 2 healthy" -export CTDB_TEST_LOGLEVEL=0 +export CTDB_TEST_LOGLEVEL=ERR required_result < all healthy" -export CTDB_TEST_LOGLEVEL=0 +export CTDB_TEST_LOGLEVEL=ERR required_result < all healthy, info logging" -export CTDB_TEST_LOGLEVEL=3 +export CTDB_TEST_LOGLEVEL=INFO required_result < 192.168.20.253 -> 0 [+0] diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/lcp2.005.sh samba-4.6.5+dfsg/ctdb/tests/takeover/lcp2.005.sh --- samba-4.5.8+dfsg/ctdb/tests/takeover/lcp2.005.sh 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/takeover/lcp2.005.sh 2017-01-11 07:55:14.000000000 +0000 @@ -4,7 +4,7 @@ define_test "3 nodes, 1 -> all healthy, debug logging" -export CTDB_TEST_LOGLEVEL=4 +export CTDB_TEST_LOGLEVEL=DEBUG required_result < 1 healthy" -export CTDB_TEST_LOGLEVEL=0 +export CTDB_TEST_LOGLEVEL=ERR required_result < 2 healthy" -export CTDB_TEST_LOGLEVEL=0 +export CTDB_TEST_LOGLEVEL=ERR required_result < all healthy" -export CTDB_TEST_LOGLEVEL=0 +export CTDB_TEST_LOGLEVEL=ERR required_result < all disconnected" -export CTDB_TEST_LOGLEVEL=0 +export CTDB_TEST_LOGLEVEL=ERR required_result <3 unhealthy" -export CTDB_TEST_LOGLEVEL=0 +export CTDB_TEST_LOGLEVEL=ERR required_result <3 unhealthy, NoIPHostOnAllDisabled" -export CTDB_TEST_LOGLEVEL=0 +export CTDB_TEST_LOGLEVEL=ERR export CTDB_SET_NoIPHostOnAllDisabled=1 required_result <3 unhealthy, NoIPHostOnAllDisabled on 2" - -export CTDB_TEST_LOGLEVEL=0 - -required_result <2 unhealthy, NoIPHostOnAllDisabled on 2 others" - -export CTDB_TEST_LOGLEVEL=0 - -required_result <3 unhealthy, all IPs assigned, split NoIPTakeover" +define_test "3 nodes, 2->3 unhealthy, all IPs assigned, NoIPTakeover" -export CTDB_TEST_LOGLEVEL=0 +export CTDB_TEST_LOGLEVEL=ERR -# We expect 1/2 the IPs to move, but the rest to stay (as opposed to -# NoIPHostOnAllDisabled) +# We expect the IPs stay where they are (as opposed to +# NoIPHostOnAllDisabled). IPs are hosted when all nodes are disabled, +# but they have nowhere else to go because of NoIPTakeover. required_result <3 unhealthy" -export CTDB_TEST_LOGLEVEL=4 +export CTDB_TEST_LOGLEVEL=DEBUG required_result <4 unhealthy" -export CTDB_TEST_LOGLEVEL=0 +export CTDB_TEST_LOGLEVEL=ERR required_result < 2 [+9216] diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/lcp2.030.sh samba-4.6.5+dfsg/ctdb/tests/takeover/lcp2.030.sh --- samba-4.5.8+dfsg/ctdb/tests/takeover/lcp2.030.sh 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/takeover/lcp2.030.sh 2017-01-11 07:55:14.000000000 +0000 @@ -4,7 +4,7 @@ define_test "900 IPs, 5 nodes, 0 -> 5 healthy" -export CTDB_TEST_LOGLEVEL=0 +export CTDB_TEST_LOGLEVEL=ERR required_result < 4 healthy" -export CTDB_TEST_LOGLEVEL=4 +export CTDB_TEST_LOGLEVEL=DEBUG required_result < 3 -> 4 healthy" -export CTDB_TEST_LOGLEVEL=0 +export CTDB_TEST_LOGLEVEL=ERR set -e diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ctdb_takeover.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ctdb_takeover.py --- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ctdb_takeover.py 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ctdb_takeover.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,888 +0,0 @@ -#!/usr/bin/env python - -# ctdb ip takeover code - -# Copyright (C) Martin Schwenke, Ronnie Sahlberg 2010, 2011 - -# Based on original CTDB C code: -# -# Copyright (C) Ronnie Sahlberg 2007 -# Copyright (C) Andrew Tridgell 2007 - -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program; if not, see . - - -import os -import sys -# Use optparse since newer argparse not available in RHEL5/EPEL. -from optparse import OptionParser -import copy -import random -import itertools - -# For parsing IP addresses -import socket -import struct - -# For external algorithm -import subprocess -import re - -options = None - -def process_args(extra_options=[]): - global options - - parser = OptionParser(option_list=extra_options) - - parser.add_option("--nd", - action="store_false", dest="deterministic_public_ips", - default=True, - help="turn off deterministic_public_ips") - parser.add_option("--ni", - action="store_true", dest="no_ip_failback", default=False, - help="turn on no_ip_failback") - parser.add_option("-L", "--lcp2", - action="store_true", dest="lcp2", default=False, - help="use LCP2 IP rebalancing algorithm [default: %default]") - parser.add_option("-e", "--external", - action="store_true", dest="external", default=False, - help="use external test program to implement IP allocation algorithm [default: %default]") - parser.add_option("-b", "--balance", - action="store_true", dest="balance", default=False, - help="show (im)balance information after each event") - parser.add_option("-d", "--diff", - action="store_true", dest="diff", default=False, - help="show IP address movements for each event") - parser.add_option("-n", "--no-print", - action="store_false", dest="show", default=True, - help="don't show IP address layout after each event") - parser.add_option("-v", "--verbose", - action="count", dest="verbose", default=0, - help="print information and actions taken to stdout") - parser.add_option("-r", "--retries", - action="store", type="int", dest="retries", default=5, - help="number of retry loops for rebalancing non-deterministic failback [default: %default]") - parser.add_option("-i", "--iterations", - action="store", type="int", dest="iterations", - default=1000, - help="number of iterations to run in test [default: %default]") - parser.add_option("-o", "--odds", - action="store", type="int", dest="odds", default=4, - help="make the chances of a failover 1 in ODDS [default: %default]") - parser.add_option("-A", "--aggressive", - action="store_true", dest="aggressive", default=False, - help="apply ODDS to try to flip each node [default: %default]") - - def seed_callback(option, opt, value, parser): - random.seed(value) - parser.add_option("-s", "--seed", - action="callback", type="int", callback=seed_callback, - help="initial random number seed for random events") - - parser.add_option("-x", "--exit", - action="store_true", dest="exit", default=False, - help="exit on the 1st gratuitous IP move or IP imbalance") - parser.add_option("-H", "--hard-imbalance-limit", - action="store", type="int", dest="hard_limit", default=1, - help="exceeding this limit causes termination [default: %default]") - parser.add_option("-S", "--soft-imbalance-limit", - action="store", type="int", dest="soft_limit", default=1, - help="exceeding this limit increments a counter [default: %default]") - - (options, args) = parser.parse_args() - - if len(args) != 0: - parser.error("too many arguments") - - # Could use a callback for this or change the default, but - # laziness is sometimes a virtue. ;-) - if options.lcp2: - options.deterministic_public_ips = False - -def print_begin(t, delim='='): - print delim * 40 - print "%s:" % (t) - -def print_end(): - print "-" * 40 - -def verbose_begin(t): - if options.verbose > 0: - print_begin(t) - -def verbose_end(): - if options.verbose > 0: - print_end() - -def verbose_print(t): - if options.verbose > 0: - if not type(t) == list: - t = [t] - if t != []: - print "\n".join([str(i) for i in t]) - -# more than this and we switch to the logging module... :-) -def debug_begin(t): - if options.verbose > 1: - print_begin(t, '-') - -def debug_end(): - if options.verbose > 1: - print_end() - -def debug_print(t): - if options.verbose > 1: - if not type(t) == list: - t = [t] - if t != []: - print "\n".join([str(i) for i in t]) - -def ip_to_list_of_ints(ip): - # Be lazy... but only expose errors in IPv4 addresses, since - # they'll be more commonly used. :-) - try: - l = socket.inet_pton(socket.AF_INET6, ip) - except: - # Pad with leading 0s. This makes IPv4 addresses comparable - # with IPv6 but reduces the overall effectiveness of the - # algorithm. The alternative would be to treat these - # addresses separately while trying to keep all the IPs in - # overall balance. - l = "".join(itertools.repeat("\0", 12)) + \ - socket.inet_pton(socket.AF_INET, ip) - - return map(lambda x: struct.unpack('B', x)[0], l) - -def ip_distance(ip1, ip2): - """Calculate the distance between 2 IPs. - - This is the length of the longtest common prefix between the IPs. - It is calculated by XOR-ing the 2 IPs together and counting the - number of leading zeroes.""" - - distance = 0 - for (o1, o2) in zip(ip_to_list_of_ints(ip1), ip_to_list_of_ints(ip2)): - # XOR this pair of octets - x = o1 ^ o2 - # count number leading zeroes - if x == 0: - distance += 8 - else: - # bin() gives minimal length '0bNNN' string - distance += (8 - (len(bin(x)) - 2)) - break - - return distance - -def ip_distance_2_sum(ip, ips): - """Calculate the IP distance for the given IP relative to IPs. - - This could be made more efficient by insering ip_distance_2 into - the loop in this function. However, that would result in some - loss of clarity and also will not be necessary in a C - implemntation.""" - - sum = 0 - for i in ips: - sum += ip_distance(ip, i) ** 2 - - return sum - -def imbalance_metric(ips): - """Return the imbalance metric for a group of IPs. - - This is the sum of squares of the IP distances between each pair of IPs.""" - if len(ips) > 1: - (h, t) = (ips[0], ips[1:]) - return ip_distance_2_sum(h, t) + imbalance_metric(t) - else: - return 0 - -def mean(l): - return float(sum(l))/len(l) - -class Node(object): - def __init__(self, public_addresses): - # List of list allows groups of IPs to be passed in. They're - # not actually used in the algorithm but are just used by - # calculate_imbalance() for checking the simulation. Note - # that people can pass in garbage and make this code - # fail... but we're all friends here in simulation world... - # :-) - if type(public_addresses[0]) is str: - self.public_addresses = set(public_addresses) - self.ip_groups = [] - else: - # flatten - self.public_addresses = set([i for s in public_addresses for i in s]) - self.ip_groups = public_addresses - - self.current_addresses = set() - self.healthy = True - self.imbalance = -1 - - def __str__(self): - return "%s %s%s" % \ - ("*" if len(self.public_addresses) == 0 else \ - (" " if self.healthy else "#"), - sorted(list(self.current_addresses)), - " %d" % self.imbalance if options.lcp2 else "") - - def can_node_serve_ip(self, ip): - return ip in self.public_addresses - - def node_ip_coverage(self, ips=None): - return len([a for a in self.current_addresses if ips == None or a in ips]) - - def set_imbalance(self, imbalance=-1): - """Set the imbalance metric to the given value. If none given - then calculate it.""" - - if imbalance != -1: - self.imbalance = imbalance - else: - self.imbalance = imbalance_metric(list(self.current_addresses)) - - def get_imbalance(self): - return self.imbalance - -class Cluster(object): - def __init__(self): - self.nodes = [] - self.deterministic_public_ips = options.deterministic_public_ips - self.no_ip_failback = options.no_ip_failback - self.all_public_ips = set() - - # Statistics - self.ip_moves = [] - self.grat_ip_moves = [] - self.imbalance = [] - self.imbalance_groups = [] - self.imbalance_count = 0 - self.imbalance_groups_count = itertools.repeat(0) - self.imbalance_metric = [] - self.events = -1 - self.num_unhealthy = [] - - self.prev = None - - def __str__(self): - return "\n".join(["%2d %s" % (i, n) \ - for (i, n) in enumerate(self.nodes)]) - - # This is naive. It assumes that IP groups are indicated by the - # 1st node having IP groups. - def have_ip_groups(self): - return (len(self.nodes[0].ip_groups) > 0) - - def print_statistics(self): - print_begin("STATISTICS") - print "Events: %6d" % self.events - print "Total IP moves: %6d" % sum(self.ip_moves) - print "Gratuitous IP moves: %6d" % sum(self.grat_ip_moves) - print "Max imbalance: %6d" % max(self.imbalance) - if self.have_ip_groups(): - print "Max group imbalance counts: ", map(max, zip(*self.imbalance_groups)) - print "Mean imbalance: %f" % mean(self.imbalance) - if self.have_ip_groups(): - print "Mean group imbalances counts: ", map(mean, zip(*self.imbalance_groups)) - print "Final imbalance: %6d" % self.imbalance[-1] - if self.have_ip_groups(): - print "Final group imbalances: ", self.imbalance_groups[-1] - if options.lcp2: - print "Max LCP2 imbalance : %6d" % max(self.imbalance_metric) - print "Soft imbalance count: %6d" % self.imbalance_count - if self.have_ip_groups(): - print "Soft imbalance group counts: ", self.imbalance_groups_count - if options.lcp2: - print "Final LCP2 imbalance : %6d" % self.imbalance_metric[-1] - print "Maximum unhealthy: %6d" % max(self.num_unhealthy) - print_end() - - def find_pnn_with_ip(self, ip): - for (i, n) in enumerate(self.nodes): - if ip in n.current_addresses: - return i - return -1 - - def quietly_remove_ip(self, ip): - # Remove address from old node. - old = self.find_pnn_with_ip(ip) - if old != -1: - self.nodes[old].current_addresses.remove(ip) - - def add_node(self, node): - self.nodes.append(node) - self.all_public_ips |= node.public_addresses - - def healthy(self, *pnns): - verbose_begin("HEALTHY") - - for pnn in pnns: - self.nodes[pnn].healthy = True - verbose_print(pnn) - - verbose_end() - - def unhealthy(self, *pnns): - - verbose_begin("UNHEALTHY") - - for pnn in pnns: - self.nodes[pnn].healthy = False - verbose_print(pnn) - - verbose_end() - - def do_something_random(self): - - """Make random node(s) healthy or unhealthy. - - If options.aggressive is False then: If all nodes are healthy - or unhealthy, then invert one of them; otherwise, there's a 1 - in options.odds chance of making another node unhealthy. - - If options.aggressive is True then: For each node there is a 1 - in options.odds chance of flipping the state of that node - between healthy and unhealthy.""" - - if not options.aggressive: - num_nodes = len(self.nodes) - healthy_pnns = [i for (i,n) in enumerate(self.nodes) if n.healthy] - num_healthy = len(healthy_pnns) - - if num_nodes == num_healthy: - self.unhealthy(random.randint(0, num_nodes-1)) - elif num_healthy == 0: - self.healthy(random.randint(0, num_nodes-1)) - elif random.randint(1, options.odds) == 1: - self.unhealthy(random.choice(healthy_pnns)) - else: - all_pnns = range(num_nodes) - unhealthy_pnns = sorted(list(set(all_pnns) - set(healthy_pnns))) - self.healthy(random.choice(unhealthy_pnns)) - else: - # We need to make at least one change or we retry...x - changed = False - while not changed: - for (pnn, n) in enumerate(self.nodes): - if random.randint(1, options.odds) == 1: - changed = True - if n.healthy: - self.unhealthy(pnn) - else: - self.healthy(pnn) - - def random_iterations(self): - i = 1 - while i <= options.iterations: - verbose_begin("EVENT %d" % i) - verbose_end() - self.do_something_random() - if self.recover() and options.exit: - break - i += 1 - - self.print_statistics() - - def imbalance_for_ips(self, ips): - - imbalance = 0 - - maxnode = -1 - minnode = -1 - - for ip in ips: - for (i, n) in enumerate(self.nodes): - - if not n.healthy or not n.can_node_serve_ip(ip): - continue - - num = n.node_ip_coverage(ips) - - if maxnode == -1 or num > maxnum: - maxnode = i - maxnum = num - - if minnode == -1 or num < minnum: - minnode = i - minnum = num - - if maxnode == -1 or minnode == -1: - continue - - i = maxnum - minnum - #if i < 2: - # i = 0 - imbalance = max([imbalance, i]) - - return imbalance - - - def calculate_imbalance(self): - - # First, do all the assigned IPs. - assigned = sorted([ip - for n in self.nodes - for ip in n.current_addresses]) - - i = self.imbalance_for_ips(assigned) - - ig = [] - # FIXME? If dealing with IP groups, assume the nodes are all - # the same. - for ips in self.nodes[0].ip_groups: - gi = self.imbalance_for_ips(ips) - ig.append(gi) - - return (i, ig) - - - def diff(self): - """Calculate differences in IP assignments between self and prev. - - Gratuitous IP moves (from a healthy node to a healthy node) - are prefixed by !!.""" - - ip_moves = 0 - grat_ip_moves = 0 - details = [] - - for (new, n) in enumerate(self.nodes): - for ip in n.current_addresses: - old = self.prev.find_pnn_with_ip(ip) - if old != new: - ip_moves += 1 - if old != -1 and \ - self.prev.nodes[new].healthy and \ - self.nodes[new].healthy and \ - self.nodes[old].healthy and \ - self.prev.nodes[old].healthy: - prefix = "!!" - grat_ip_moves += 1 - else: - prefix = " " - details.append("%s %s: %d -> %d" % - (prefix, ip, old, new)) - - return (ip_moves, grat_ip_moves, details) - - def find_takeover_node(self, ip): - - pnn = -1 - min = 0 - for (i, n) in enumerate(self.nodes): - if not n.healthy: - continue - - if not n.can_node_serve_ip(ip): - continue - - num = n.node_ip_coverage() - - if (pnn == -1): - pnn = i - min = num - else: - if num < min: - pnn = i - min = num - - if pnn == -1: - verbose_print("Could not find node to take over public address %s" % ip) - return False - - self.nodes[pnn].current_addresses.add(ip) - - verbose_print("%s -> %d" % (ip, pnn)) - return True - - def basic_allocate_unassigned(self): - - assigned = set([ip for n in self.nodes for ip in n.current_addresses]) - unassigned = sorted(list(self.all_public_ips - assigned)) - - for ip in unassigned: - self.find_takeover_node(ip) - - def basic_failback(self, retries_l): - - assigned = sorted([ip - for n in self.nodes - for ip in n.current_addresses]) - for ip in assigned: - - maxnode = -1 - minnode = -1 - for (i, n) in enumerate(self.nodes): - if not n.healthy: - continue - - if not n.can_node_serve_ip(ip): - continue - - num = n.node_ip_coverage() - - if maxnode == -1: - maxnode = i - maxnum = num - else: - if num > maxnum: - maxnode = i - maxnum = num - if minnode == -1: - minnode = i - minnum = num - else: - if num < minnum: - minnode = i - minnum = num - - if maxnode == -1: - print "Could not find maxnode. May not be able to serve ip", ip - continue - - #if self.deterministic_public_ips: - # continue - - if maxnum > minnum + 1 and retries_l[0] < options.retries: - # Remove the 1st ip from maxnode - t = sorted(list(self.nodes[maxnode].current_addresses)) - realloc = t[0] - verbose_print("%s <- %d" % (realloc, maxnode)) - self.nodes[maxnode].current_addresses.remove(realloc) - # Redo the outer loop. - retries_l[0] += 1 - return True - - return False - - - def lcp2_allocate_unassigned(self): - - # Assign as many unassigned addresses as possible. Keep - # selecting the optimal assignment until we don't manage to - # assign anything. - assigned = set([ip for n in self.nodes for ip in n.current_addresses]) - unassigned = sorted(list(self.all_public_ips - assigned)) - - should_loop = True - while len(unassigned) > 0 and should_loop: - should_loop = False - - debug_begin(" CONSIDERING MOVES (UNASSIGNED)") - - minnode = -1 - mindsum = 0 - minip = None - - for ip in unassigned: - for dstnode in range(len(self.nodes)): - if self.nodes[dstnode].can_node_serve_ip(ip) and \ - self.nodes[dstnode].healthy: - dstdsum = ip_distance_2_sum(ip, self.nodes[dstnode].current_addresses) - dstimbl = self.nodes[dstnode].get_imbalance() + dstdsum - debug_print(" %s -> %d [+%d]" % \ - (ip, - dstnode, - dstimbl - self.nodes[dstnode].get_imbalance())) - - if (minnode == -1) or (dstdsum < mindsum): - minnode = dstnode - minimbl = dstimbl - mindsum = dstdsum - minip = ip - should_loop = True - debug_end() - - if minnode != -1: - self.nodes[minnode].current_addresses.add(minip) - self.nodes[minnode].set_imbalance(self.nodes[minnode].get_imbalance() + mindsum) - verbose_print("%s -> %d [+%d]" % (minip, minnode, mindsum)) - unassigned.remove(minip) - - for ip in unassigned: - verbose_print("Could not find node to take over public address %s" % ip) - - def lcp2_failback(self, targets): - - # Get the node with the highest imbalance metric. - srcnode = -1 - maximbl = 0 - for (pnn, n) in enumerate(self.nodes): - b = n.get_imbalance() - if (srcnode == -1) or (b > maximbl): - srcnode = pnn - maximbl = b - - # This means that all nodes had 0 or 1 addresses, so can't - # be imbalanced. - if maximbl == 0: - return False - - # We'll need this a few times... - ips = self.nodes[srcnode].current_addresses - - # Find an IP and destination node that best reduces imbalance. - optimum = None - debug_begin(" CONSIDERING MOVES FROM %d [%d]" % (srcnode, maximbl)) - for ip in ips: - # What is this IP address costing the source node? - srcdsum = ip_distance_2_sum(ip, ips - set([ip])) - srcimbl = maximbl - srcdsum - - # Consider this IP address would cost each potential - # destination node. Destination nodes are limited to - # those that are newly healthy, since we don't want to - # do gratuitous failover of IPs just to make minor - # balance improvements. - for dstnode in targets: - if self.nodes[dstnode].can_node_serve_ip(ip) and \ - self.nodes[dstnode].healthy: - dstdsum = ip_distance_2_sum(ip, self.nodes[dstnode].current_addresses) - dstimbl = self.nodes[dstnode].get_imbalance() + dstdsum - debug_print(" %d [%d] -> %s -> %d [+%d]" % \ - (srcnode, - srcimbl - self.nodes[srcnode].get_imbalance(), - ip, - dstnode, - dstimbl - self.nodes[dstnode].get_imbalance())) - - if (dstimbl < maximbl) and (dstdsum < srcdsum): - if optimum is None: - optimum = (ip, srcnode, srcimbl, dstnode, dstimbl) - else: - (x, sn, si, dn, di) = optimum - if (srcimbl + dstimbl) < (si + di): - optimum = (ip, srcnode, srcimbl, dstnode, dstimbl) - debug_end() - - if optimum is not None: - # We found a move that makes things better... - (ip, srcnode, srcimbl, dstnode, dstimbl) = optimum - ini_srcimbl = self.nodes[srcnode].get_imbalance() - ini_dstimbl = self.nodes[dstnode].get_imbalance() - - self.nodes[srcnode].current_addresses.remove(ip) - self.nodes[srcnode].set_imbalance(srcimbl) - - self.nodes[dstnode].current_addresses.add(ip) - self.nodes[dstnode].set_imbalance(dstimbl) - - verbose_print("%d [%d] -> %s -> %d [+%d]" % \ - (srcnode, - srcimbl - ini_srcimbl, - ip, - dstnode, - dstimbl - ini_dstimbl)) - - return True - - return False - - def ctdb_takeover_run_python(self): - - # Don't bother with the num_healthy stuff. It is an - # irrelevant detail. - - # We just keep the allocate IPs in the current_addresses field - # of the node. This needs to readable, not efficient! - - if self.deterministic_public_ips: - # Remap everything. - addr_list = sorted(list(self.all_public_ips)) - for (i, ip) in enumerate(addr_list): - self.quietly_remove_ip(ip) - # Add addresses to new node. - pnn = i % len(self.nodes) - self.nodes[pnn].current_addresses.add(ip) - verbose_print("%s -> %d" % (ip, pnn)) - - # Remove public addresses from unhealthy nodes. - for (pnn, n) in enumerate(self.nodes): - if not n.healthy: - verbose_print(["%s <- %d" % (ip, pnn) - for ip in n.current_addresses]) - n.current_addresses = set() - - # If a node can't serve an assigned address then remove it. - for n in self.nodes: - verbose_print(["%s <- %d" % (ip, pnn) - for ip in n.current_addresses - n.public_addresses]) - n.current_addresses &= n.public_addresses - - if options.lcp2: - newly_healthy = [pnn for (pnn, n) in enumerate(self.nodes) - if len(n.current_addresses) == 0 and n.healthy] - for n in self.nodes: - n.set_imbalance() - - # We'll only retry the balancing act up to options.retries - # times (for the basic non-deterministic algorithm). This - # nonsense gives us a reference on the retries count in - # Python. It will be easier in C. :-) - # For LCP2 we reassignas many IPs from heavily "loaded" nodes - # to nodes that are newly healthy, looping until we fail to - # reassign an IP. - retries_l = [0] - should_loop = True - while should_loop: - should_loop = False - - if options.lcp2: - self.lcp2_allocate_unassigned() - else: - self.basic_allocate_unassigned() - - if self.no_ip_failback or self.deterministic_public_ips: - break - - if options.lcp2: - if len(newly_healthy) == 0: - break - should_loop = self.lcp2_failback(newly_healthy) - else: - should_loop = self.basic_failback(retries_l) - - def ctdb_takeover_run_external(self): - - # Written while asleep... - - # Convert the cluster state to something that be fed to - # ctdb_takeover_tests ipalloc ... - - in_lines = [] - for ip in sorted(list(self.all_public_ips)): - allowed = [] - assigned = -1 - for (i, n) in enumerate(self.nodes): - if n.can_node_serve_ip(ip): - allowed.append("%s" % i) - if ip in n.current_addresses: - assigned = i - line = "%s\t%d\t%s" % (ip, assigned, ",".join(allowed)) - in_lines.append(line) - - nodestates = ",".join(["0" if n.healthy else "1" for n in self.nodes]) - - if options.lcp2: - os.environ["CTDB_LCP2"] = "yes" - if options.verbose > 1: - os.environ["CTDB_TEST_LOGLEVEL"] = "4" - elif options.verbose == 1: - os.environ["CTDB_TEST_LOGLEVEL"] = "3" - else: - os.environ["CTDB_TEST_LOGLEVEL"] = "0" - - p = subprocess.Popen("../../bin/ctdb_takeover_tests ipalloc %s 2>&1" % nodestates, - shell=True, - stdin=subprocess.PIPE, stdout=subprocess.PIPE) - p.stdin.write("\n".join(in_lines)) - p.stdin.close() - - # Flush all of the assigned IPs. - for n in self.nodes: - n.current_addresses = set() - - # Uses the results to populate the current_addresses for each - # node. - for line in p.stdout.read().split("\n"): - # Some lines are debug, some are the final IP - # configuration. Let's use a gross hack that assumes any - # line with 2 words is IP configuration. That will do for - # now. - words = re.split("\s+", line) - if len(words) == 2: - # Add the IP as current for the specified node. - self.nodes[int(words[1])].current_addresses.add(words[0]) - else: - # First 3 words are log date/time, remove them... - print " ".join(words[3:]) - - # Now fake up the LCP calculations. - for n in self.nodes: - n.set_imbalance() - - def ctdb_takeover_run(self): - - self.events += 1 - - if options.external: - return self.ctdb_takeover_run_external() - else: - return self.ctdb_takeover_run_python() - - def recover(self): - verbose_begin("TAKEOVER") - - self.ctdb_takeover_run() - - verbose_end() - - grat_ip_moves = 0 - - if self.prev is not None: - (ip_moves, grat_ip_moves, details) = self.diff() - self.ip_moves.append(ip_moves) - self.grat_ip_moves.append(grat_ip_moves) - - if options.diff: - print_begin("DIFF") - print "\n".join(details) - print_end() - - (imbalance, imbalance_groups) = self.calculate_imbalance() - self.imbalance.append(imbalance) - self.imbalance_groups.append(imbalance_groups) - - if imbalance > options.soft_limit: - self.imbalance_count += 1 - - # There must be a cleaner way... - t = [] - for (c, i) in zip(self.imbalance_groups_count, imbalance_groups): - if i > options.soft_limit: - t.append(c + i) - else: - t.append(c) - self.imbalance_groups_count = t - - imbalance_metric = max([n.get_imbalance() for n in self.nodes]) - self.imbalance_metric.append(imbalance_metric) - if options.balance: - print_begin("IMBALANCE") - print "ALL IPS:", imbalance - if self.have_ip_groups(): - print "IP GROUPS:", imbalance_groups - if options.lcp2: - print "LCP2 IMBALANCE:", imbalance_metric - print_end() - - num_unhealthy = len(self.nodes) - \ - len([n for n in self.nodes if n.healthy]) - self.num_unhealthy.append(num_unhealthy) - - if options.show: - print_begin("STATE") - print self - print_end() - - self.prev = None - self.prev = copy.deepcopy(self) - - # True is bad! - return (grat_ip_moves > 0) or \ - (not self.have_ip_groups() and imbalance > options.hard_limit) or \ - (self.have_ip_groups() and (max(imbalance_groups) > options.hard_limit)) diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/hey_jude.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/hey_jude.py --- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/hey_jude.py 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/hey_jude.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -from ctdb_takeover import Cluster, Node, process_args - -process_args() - -addresses10 = ['10.4.20.%d' % n for n in range(154, 168)] -addresses172a = ['172.20.106.%d' % n for n in range(110, 124)] -addresses172b = ['172.20.107.%d' % n for n in range(110, 117)] - -c = Cluster() - -#for i in range(7): -# c.add_node(Node([addresses10, addresses172])) - - -for i in range(4): - c.add_node(Node([addresses172a, addresses172b])) -for i in range(3): - c.add_node(Node(addresses10)) - -c.recover() - -c.random_iterations() diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ip_groups1.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ip_groups1.py --- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ip_groups1.py 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ip_groups1.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -# 2 IP groups, both on the same 5 nodes, with each group on different -# interfaces/VLANs. One group has many more addresses to test how -# well an "imbalanced" configuration will balance... - -from ctdb_takeover import Cluster, Node, process_args - -process_args() - -addresses20 = ['192.168.20.%d' % n for n in range(1, 13)] -addresses128 = ['192.168.128.%d' % n for n in range(1, 5)] - -c = Cluster() - -for i in range(5): - c.add_node(Node([addresses20, addresses128])) - -#for i in range(3): -# c.add_node(Node([addresses20])) - - -c.recover() - -c.random_iterations() diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ip_groups2.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ip_groups2.py --- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ip_groups2.py 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ip_groups2.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -#!/usr/bin/env python - -# 2 groups of addresses, combined into 1 pool so the checking -# algorithm doesn't know about the groups, across 2 nodes. - -from ctdb_takeover import Cluster, Node, process_args - -process_args() - -addresses20 = ['192.168.20.%d' % n for n in range(1, 13)] -addresses21 = ['192.168.21.%d' % n for n in range(1, 5)] - -c = Cluster() - -for i in range(2): - c.add_node(Node(addresses20 + addresses21)) - -c.recover() - -c.random_iterations() diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ip_groups3.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ip_groups3.py --- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ip_groups3.py 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ip_groups3.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -#!/usr/bin/env python - -# 4 IP groups, across 10 nodes, with each group on different -# interfaces/VLANs. 80 addresses in total but not evenly balanced, to -# help check some of the more extreme behaviour. - -from ctdb_takeover import Cluster, Node, process_args - -process_args() - -addresses1 = ['192.168.1.%d' % n for n in range(1, 41)] -addresses2 = ['192.168.2.%d' % n for n in range(1, 21)] -addresses3 = ['192.168.3.%d' % n for n in range(1, 11)] -addresses4 = ['192.168.4.%d' % n for n in range(1, 11)] - -# Try detecting imbalance with square root of number of nodes? Or -# just with a parameter indicating how unbalanced you're willing to -# accept... - -c = Cluster() - -for i in range(10): - c.add_node(Node([addresses1, addresses2, addresses3, addresses4])) - -c.recover() - -c.random_iterations() diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ip_groups4.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ip_groups4.py --- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ip_groups4.py 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ip_groups4.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -# 2 IP groups, across 2 nodes, with each group on different -# interfaces. 4 addresses per group. A nice little canonical 2 node -# configuration. - -from ctdb_takeover import Cluster, Node, process_args - -process_args() - -addresses1 = ['192.168.1.%d' % n for n in range(1, 5)] -addresses2 = ['192.168.2.%d' % n for n in range(1, 5)] - -# Try detecting imbalance with square root of number of nodes? Or -# just with a parameter indicating how unbalanced you're willing to -# accept... - -c = Cluster() - -for i in range(2): - c.add_node(Node([addresses1, addresses2])) - -c.recover() - -c.random_iterations() diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ip_groups5.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ip_groups5.py --- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ip_groups5.py 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ip_groups5.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -# 1 IP group, to test backward compatibility of LCP2 algorithm. 16 -# addresses across 4 nodes. - -from ctdb_takeover import Cluster, Node, process_args - -process_args() - -addresses1 = ['192.168.1.%d' % n for n in range(1, 17)] - -# Try detecting imbalance with square root of number of nodes? Or -# just with a parameter indicating how unbalanced you're willing to -# accept... - -c = Cluster() - -for i in range(4): - c.add_node(Node(addresses1)) - -c.recover() - -c.random_iterations() diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/mgmt_simple.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/mgmt_simple.py --- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/mgmt_simple.py 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/mgmt_simple.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -# This is an example showing a current SONAS configuration with 3 -# interface node and a management node. When run with deterministic -# IPs there are gratuitous IP reassignments. - -from ctdb_takeover import Cluster, Node, process_args - -process_args() - -addresses = ['A', 'B', 'C', 'D', 'E', 'F', 'G'] - -c = Cluster() - -for i in range(3): - c.add_node(Node(addresses)) - -c.add_node(Node([])) - -c.recover() - -c.random_iterations() diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/node_group_extra.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/node_group_extra.py --- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/node_group_extra.py 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/node_group_extra.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -#!/usr/bin/env python - -# This example demonstrates a node group configuration. Is it meant -# to be the same as node_group_simple.py, but with a couple of nodes -# added later, so they are listed after the management node. - -# When run with deterministic IPs (use "-d" to show the problem) it -# does many gratuitous IP reassignments. - -from ctdb_takeover import Cluster, Node, process_args - -process_args() - -addresses1 = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'] + ['P', 'Q', 'R', 'S', 'T', 'U'] -addresses2 = ['I', 'J', 'K', 'L'] - -c = Cluster() - -for i in range(4): - c.add_node(Node(addresses1)) - -for i in range(3): - c.add_node(Node(addresses2)) - -c.add_node(Node([])) -c.add_node(Node(addresses1)) -c.add_node(Node(addresses2)) - -c.recover() - -c.random_iterations() diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/node_group.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/node_group.py --- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/node_group.py 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/node_group.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -#!/usr/bin/env python - -# This demonstrates a node group configurations. -# -# Node groups can be defined with the syntax "-g N@IP0,IP1-IP2,IP3". -# This says to create a group of N nodes with IPs IP0, IP1, ..., IP2, -# IP3. Run it with deterministic IPs causes lots of gratuitous IP -# reassignments. Running with --nd fixes this. - -import ctdb_takeover -import sys -from optparse import make_option -import string - -ctdb_takeover.process_args([ - make_option("-g", "--group", - action="append", type="string", dest="groups", - help="define a node group using N@IPs syntax"), - ]) - -def expand_range(r): - sr = r.split("-", 1) - if len(sr) == 2: - all = string.ascii_uppercase + string.ascii_lowercase - sr = list(all[all.index(sr[0]):all.index(sr[1])+1]) - return sr - -def add_node_group(s): - (count, ips_str) = s.split("@", 1) - ips = [i for r in ips_str.split(",") \ - for i in expand_range(r) if r != ""] - for i in range(int(count)): - c.add_node(ctdb_takeover.Node(ips)) - -c = ctdb_takeover.Cluster() - -if ctdb_takeover.options.groups is None: - print "Error: no node groups defined." - sys.exit(1) - -for g in ctdb_takeover.options.groups: - add_node_group(g) - -c.recover() - -c.random_iterations() diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/node_group_simple.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/node_group_simple.py --- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/node_group_simple.py 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/node_group_simple.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -#!/usr/bin/env python - -# This example demonstrates a simple, sensible node group -# configuration. When run with deterministic IPs (use "-d" to show -# the problem) it does many gratuitous IP reassignments. - -from ctdb_takeover import Cluster, Node, process_args - -process_args() - -addresses1 = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'] -addresses2 = ['I', 'J', 'K'] - -c = Cluster() - -for i in range(4): - c.add_node(Node(addresses1)) - -for i in range(3): - c.add_node(Node(addresses2)) - -c.add_node(Node([])) - -c.recover() - -c.random_iterations() diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/nondet_path_01.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/nondet_path_01.py --- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/nondet_path_01.py 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/nondet_path_01.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -# This is a contrived example that makes the balancing algorithm fail -# for nondeterministic IPs (run with "-dv --nd" to see the failure). - -from ctdb_takeover import Cluster, Node, process_args - -process_args() - -addresses1 = ['A', 'B', 'C', 'D'] -addresses2 = ['B', 'E', 'F'] - -c = Cluster() - -for i in range(2): - c.add_node(Node(addresses1)) - -c.add_node(Node(addresses2)) - -c.recover() - -c.unhealthy(1) -c.recover() -c.healthy(1) -c.recover() diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/README samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/README --- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/README 2016-08-11 07:51:04.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/README 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -This contains a Python simulation of CTDB's IP reallocation algorithm. - -It is useful for experimenting with improvements. - -To use this on RHEL5 you'll need python2.6 from EPEL -. diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover_helper/000.sh samba-4.6.5+dfsg/ctdb/tests/takeover_helper/000.sh --- samba-4.5.8+dfsg/ctdb/tests/takeover_helper/000.sh 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/takeover_helper/000.sh 2017-01-11 07:55:14.000000000 +0000 @@ -0,0 +1,22 @@ +#!/bin/sh + +. "${TEST_SCRIPTS_DIR}/unit.sh" + +define_test "3 nodes, all ok, no IPs" + +setup_ctdbd </dev/null || echo) + if [ -n "$pid" ] ; then + kill $pid || true + rm -f "$ctdbd_pidfile" + fi + rm -f "$ctdbd_socket" +} + +setup_ctdbd () +{ + debug "Setting up fake ctdbd" + + $VALGRIND fake_ctdbd -d "$FAKE_CTDBD_DEBUGLEVEL" \ + -s "$ctdbd_socket" -p "$ctdbd_pidfile" + # This current translates to a 6 second timeout for the + # important controls + ctdb --socket $ctdbd_socket setvar TakeoverTimeout 2 + test_cleanup cleanup_ctdbd +} + +ctdbd_getpid () +{ + cat "$ctdbd_pidfile" +} + +# Render non-printable characters. The helper prints the status as +# binary, so render it for easy comparison. +result_filter () +{ + sed -e 's|ctdb-takeover\[[0-9]*\]: ||' +} + +ctdb_cmd () +{ + echo Running: ctdb -d "$CTDB_DEBUGLEVEL" --socket $ctdbd_socket "$@" + ctdb -d "$CTDB_DEBUGLEVEL" --socket $ctdbd_socket "$@" +} + +test_ctdb_ip_all () +{ + unit_test ctdb -d "$CTDB_DEBUGLEVEL" \ + --socket $ctdbd_socket ip all || exit $? +} + +takeover_helper_out="${TEST_VAR_DIR}/takover_helper.out" + +takeover_helper_format_outfd () +{ + od -A n -t d4 "$takeover_helper_out" | sed -e 's|^[[:space:]]*||' +} + +test_takeover_helper () +{ + ( + export CTDB_DEBUGLEVEL="$HELPER_DEBUGLEVEL" + export CTDB_LOGGING="file:" + unit_test ctdb_takeover_helper 3 "$ctdbd_socket" "$@" \ + 3>"$takeover_helper_out" + ) || exit $? + + case "$required_rc" in + 255) _t="-1" ;; + *) _t="$required_rc" ;; + esac + ok "$_t" + + unit_test_notrace takeover_helper_format_outfd + _ret=$? + rm "$takeover_helper_out" + [ $? -eq 0 ] || exit $? +} diff -Nru samba-4.5.8+dfsg/ctdb/tests/tool/ctdb.ip.001.sh samba-4.6.5+dfsg/ctdb/tests/tool/ctdb.ip.001.sh --- samba-4.5.8+dfsg/ctdb/tests/tool/ctdb.ip.001.sh 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.6.5+dfsg/ctdb/tests/tool/ctdb.ip.001.sh 2017-01-11 07:55:14.000000000 +0000 @@ -0,0 +1,17 @@ +#!/bin/sh + +. "${TEST_SCRIPTS_DIR}/unit.sh" + +define_test "3 nodes, all ok, no ips" + +setup_ctdbd < #include @@ -32,6 +33,7 @@ #include "ctdb_version.h" #include "lib/util/debug.h" #include "lib/util/samba_util.h" +#include "lib/util/sys_rw.h" #include "common/db_hash.h" #include "common/logging.h" @@ -644,28 +646,45 @@ return ret; } -static int run_helper(const char *command, const char *path, const char *arg1) +static int run_helper(TALLOC_CTX *mem_ctx, const char *command, + const char *path, int argc, const char **argv) { pid_t pid; int save_errno, status, ret; + const char **new_argv; + int i; + + new_argv = talloc_array(mem_ctx, const char *, argc + 2); + if (new_argv == NULL) { + return ENOMEM; + } + + new_argv[0] = path; + for (i=0; i= 64 && pstatus < 255) { + fprintf(stderr, "%s failed with error %d\n", + command, pstatus-64); + ret = pstatus - 64; + } else { + ret = pstatus; + } return ret; - } - - if (WIFSIGNALED(status)) { + } else if (WIFSIGNALED(status)) { fprintf(stderr, "%s terminated with signal %d\n", command, WTERMSIG(status)); return EINTR; @@ -774,7 +802,8 @@ } static void print_nodemap(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb, - struct ctdb_node_map *nodemap, uint32_t mypnn) + struct ctdb_node_map *nodemap, uint32_t mypnn, + bool print_header) { struct ctdb_node_and_flags *node; int num_deleted_nodes = 0; @@ -786,11 +815,14 @@ } } - if (num_deleted_nodes == 0) { - printf("Number of nodes:%d\n", nodemap->num); - } else { - printf("Number of nodes:%d (including %d deleted nodes)\n", - nodemap->num, num_deleted_nodes); + if (print_header) { + if (num_deleted_nodes == 0) { + printf("Number of nodes:%d\n", nodemap->num); + } else { + printf("Number of nodes:%d " + "(including %d deleted nodes)\n", + nodemap->num, num_deleted_nodes); + } } for (i=0; inum; i++) { @@ -816,7 +848,7 @@ { int i; - print_nodemap(mem_ctx, ctdb, nodemap, mypnn); + print_nodemap(mem_ctx, ctdb, nodemap, mypnn, true); if (vnnmap->generation == INVALID_GENERATION) { printf("Generation:INVALID\n"); @@ -1524,7 +1556,8 @@ for (i=0; iev, ctdb->client, - pnn_list[i], TIMEOUT(), &ips); + pnn_list[i], TIMEOUT(), + false, &ips); if (ret != 0) { goto failed; } @@ -1629,7 +1662,8 @@ ret = get_all_public_ips(ctdb, mem_ctx, &ips); } else { ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client, - ctdb->cmd_pnn, TIMEOUT(), &ips); + ctdb->cmd_pnn, TIMEOUT(), + false, &ips); } if (ret != 0) { return ret; @@ -2277,7 +2311,7 @@ return 1; } - return run_helper("LVS helper", lvs_helper, argv[0]); + return run_helper(mem_ctx, "LVS helper", lvs_helper, argc, argv); } static int control_disable_monitor(TALLOC_CTX *mem_ctx, @@ -2321,7 +2355,7 @@ static int control_setdebug(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb, int argc, const char **argv) { - enum debug_level log_level; + int log_level; int ret; bool found; @@ -2350,7 +2384,7 @@ static int control_getdebug(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb, int argc, const char **argv) { - enum debug_level loglevel; + int loglevel; const char *log_str; int ret; @@ -2920,7 +2954,7 @@ return ret; } - if (vnnmap->generation == 1) { + if (vnnmap->generation == INVALID_GENERATION) { talloc_free(vnnmap); sleep(1); goto again; @@ -3781,7 +3815,7 @@ } ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client, - pnn, TIMEOUT(), &pubip_list); + pnn, TIMEOUT(), false, &pubip_list); if (ret != 0) { fprintf(stderr, "Failed to get Public IPs from node %u\n", pnn); @@ -3909,7 +3943,8 @@ } ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client, - ctdb->cmd_pnn, TIMEOUT(), &pubip_list); + ctdb->cmd_pnn, TIMEOUT(), + false, &pubip_list); if (ret != 0) { fprintf(stderr, "Failed to get Public IPs from node %u\n", ctdb->cmd_pnn); @@ -3972,7 +4007,8 @@ } ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client, - ctdb->cmd_pnn, TIMEOUT(), &pubip_list); + ctdb->cmd_pnn, TIMEOUT(), + false, &pubip_list); if (ret != 0) { fprintf(stderr, "Failed to get Public IPs from node %u\n", ctdb->cmd_pnn); @@ -4006,31 +4042,6 @@ return 0; } -static int control_eventscript(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb, - int argc, const char **argv) -{ - int ret; - - if (argc != 1) { - usage("eventscript"); - } - - if (strcmp(argv[0], "monitor") != 0) { - fprintf(stderr, "Only monitor event can be run\n"); - return 1; - } - - ret = ctdb_ctrl_run_eventscripts(mem_ctx, ctdb->ev, ctdb->client, - ctdb->cmd_pnn, TIMEOUT(), argv[0]); - if (ret != 0) { - fprintf(stderr, "Failed to run monitor event on node %u\n", - ctdb->cmd_pnn); - return ret; - } - - return 0; -} - #define DB_VERSION 3 #define MAX_DB_NAME 64 #define MAX_REC_BUFFER_SIZE (100*1000) @@ -4623,239 +4634,70 @@ return 0; } -static void print_scriptstatus_one(struct ctdb_script_list *slist, - const char *event_str) -{ - int i; - int num_run = 0; - - if (slist == NULL) { - if (! options.machinereadable) { - printf("%s cycle never run\n", event_str); - } - return; - } - - for (i=0; inum_scripts; i++) { - if (slist->script[i].status != -ENOEXEC) { - num_run++; - } - } - - if (! options.machinereadable) { - printf("%d scripts were executed last %s cycle\n", - num_run, event_str); - } - - for (i=0; inum_scripts; i++) { - const char *status = NULL; - - switch (slist->script[i].status) { - case -ETIME: - status = "TIMEDOUT"; - break; - case -ENOEXEC: - status = "DISABLED"; - break; - case 0: - status = "OK"; - break; - default: - if (slist->script[i].status > 0) { - status = "ERROR"; - } - break; - } - - if (options.machinereadable) { - printf("%s%s%s%s%s%i%s%s%s%lu.%06lu%s%lu.%06lu%s%s%s\n", - options.sep, - event_str, options.sep, - slist->script[i].name, options.sep, - slist->script[i].status, options.sep, - status, options.sep, - slist->script[i].start.tv_sec, - slist->script[i].start.tv_usec, options.sep, - slist->script[i].finished.tv_sec, - slist->script[i].finished.tv_usec, options.sep, - slist->script[i].output, options.sep); - continue; - } - - if (status) { - printf("%-20s Status:%s ", - slist->script[i].name, status); - } else { - /* Some other error, eg from stat. */ - printf("%-20s Status:CANNOT RUN (%s)", - slist->script[i].name, - strerror(-slist->script[i].status)); - } - - if (slist->script[i].status >= 0) { - printf("Duration:%.3lf ", - timeval_delta(&slist->script[i].finished, - &slist->script[i].start)); - } - if (slist->script[i].status != -ENOEXEC) { - printf("%s", ctime(&slist->script[i].start.tv_sec)); - if (slist->script[i].status != 0) { - printf(" OUTPUT:%s\n", - slist->script[i].output); - } - } else { - printf("\n"); - } - } -} - -static void print_scriptstatus(struct ctdb_script_list **slist, - int count, const char **event_str) +static int control_event(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb, + int argc, const char **argv) { + char *t, *event_helper = NULL; + char *eventd_socket = NULL; + const char **new_argv; int i; - if (options.machinereadable) { - printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", - options.sep, - "Type", options.sep, - "Name", options.sep, - "Code", options.sep, - "Status", options.sep, - "Start", options.sep, - "End", options.sep, - "Error Output", options.sep); - } - - for (i=0; i 1) { - usage("scriptstatus"); - } - - if (argc == 0) { - event_str = "monitor"; + t = getenv("CTDB_EVENT_HELPER"); + if (t != NULL) { + event_helper = talloc_strdup(mem_ctx, t); } else { - event_str = argv[0]; + event_helper = talloc_asprintf(mem_ctx, "%s/ctdb_event", + CTDB_HELPER_BINDIR); } - valid = false; - for (i=0; iev, - ctdb->client, - ctdb->cmd_pnn, - TIMEOUT(), event, - &slist[num]); - if (ret != 0) { - fprintf(stderr, - "failed to get script status for %s event\n", - all_events[i]); - return 1; - } - - if (slist[num] == NULL) { - num++; - continue; - } - - /* The ETIME status is ignored for certain events. - * In that case the status is 0, but endtime is not set. - */ - for (j=0; jnum_scripts; j++) { - if (slist[num]->script[j].status == 0 && - timeval_is_zero(&slist[num]->script[j].finished)) { - slist[num]->script[j].status = -ETIME; - } - } - - num++; + new_argv[0] = eventd_socket; + for (i=0; iev, ctdb->client, - ctdb->cmd_pnn, TIMEOUT(), argv[0]); - if (ret != 0) { - fprintf(stderr, "Failed to enable script %s\n", argv[0]); - return ret; - } - - return 0; -} - -static int control_disablescript(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb, - int argc, const char **argv) -{ - int ret; + const char *new_argv[3]; - if (argc != 1) { - usage("disablescript"); + if (argc > 1) { + usage("scriptstatus"); } - ret = ctdb_ctrl_disable_script(mem_ctx, ctdb->ev, ctdb->client, - ctdb->cmd_pnn, TIMEOUT(), argv[0]); - if (ret != 0) { - fprintf(stderr, "Failed to disable script %s\n", argv[0]); - return ret; - } + new_argv[0] = "status"; + new_argv[1] = (argc == 0) ? "monitor" : argv[0]; + new_argv[2] = NULL; + (void) control_event(mem_ctx, ctdb, 2, new_argv); return 0; } @@ -4881,13 +4723,15 @@ return 1; } - return run_helper("NAT gateway helper", natgw_helper, argv[0]); + return run_helper(mem_ctx, "NAT gateway helper", natgw_helper, + argc, argv); } static int control_natgwlist(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb, int argc, const char **argv) { char *t, *natgw_helper = NULL; + const char *cmd_argv[] = { "natgwlist", NULL }; if (argc != 0) { usage("natgwlist"); @@ -4906,7 +4750,8 @@ return 1; } - return run_helper("NAT gateway helper", natgw_helper, "natgwlist"); + return run_helper(mem_ctx, "NAT gateway helper", natgw_helper, + 1, cmd_argv); } /* @@ -4969,7 +4814,7 @@ struct ctdb_context *ctdb, int argc, const char **argv) { - uint32_t lmasterrole; + uint32_t lmasterrole = 0; int ret; if (argc != 1) { @@ -4997,7 +4842,7 @@ struct ctdb_context *ctdb, int argc, const char **argv) { - uint32_t recmasterrole; + uint32_t recmasterrole = 0; int ret; if (argc != 1) { @@ -5499,9 +5344,9 @@ int argc, const char **argv) { struct tdb_context *tdb; - TDB_DATA key, data, value; + TDB_DATA key, data[2], value; struct ctdb_ltdb_header header; - size_t offset; + uint8_t header_buf[sizeof(struct ctdb_ltdb_header)]; int ret; if (argc < 3 || argc > 5) { @@ -5540,19 +5385,15 @@ header.flags = (uint32_t)atol(argv[5]); } - offset = ctdb_ltdb_header_len(&header); - data.dsize = offset + value.dsize; - data.dptr = talloc_size(mem_ctx, data.dsize); - if (data.dptr == NULL) { - fprintf(stderr, "Memory allocation error\n"); - tdb_close(tdb); - return 1; - } + ctdb_ltdb_header_push(&header, header_buf); + + data[0].dsize = ctdb_ltdb_header_len(&header); + data[0].dptr = header_buf; - ctdb_ltdb_header_push(&header, data.dptr); - memcpy(data.dptr + offset, value.dptr, value.dsize); + data[1].dsize = value.dsize; + data[1].dptr = value.dptr; - ret = tdb_store(tdb, key, data, TDB_REPLACE); + ret = tdb_storev(tdb, key, data, 2, TDB_REPLACE); if (ret != 0) { fprintf(stderr, "Failed to write record %s to file %s\n", argv[1], argv[0]); @@ -5813,6 +5654,7 @@ const char *nodestring = NULL; struct ctdb_node_map *nodemap; int ret, i; + bool print_hdr = false; if (argc > 1) { usage("nodestatus"); @@ -5820,21 +5662,19 @@ if (argc == 1) { nodestring = argv[0]; + if (strcmp(nodestring, "all") == 0) { + print_hdr = true; + } } if (! parse_nodestring(mem_ctx, ctdb, nodestring, &nodemap)) { return 1; } - nodemap = get_nodemap(ctdb, false); - if (nodemap == NULL) { - return 1; - } - if (options.machinereadable) { print_nodemap_machine(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn); } else { - print_nodemap(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn); + print_nodemap(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn, print_hdr); } ret = 0; @@ -6267,8 +6107,6 @@ "add an ip address to a node", " " }, { "delip", control_delip, false, true, "delete an ip address from a node", "" }, - { "eventscript", control_eventscript, false, true, - "run an event", "monitor" }, { "backupdb", control_backupdb, false, false, "backup a database into a file", " " }, { "restoredb", control_restoredb, false, false, @@ -6279,13 +6117,11 @@ "wipe the contents of a database.", ""}, { "recmaster", control_recmaster, false, true, "show the pnn for the recovery master", NULL }, - { "scriptstatus", control_scriptstatus, false, true, + { "event", control_event, true, false, + "event and event script commands", NULL }, + { "scriptstatus", control_scriptstatus, true, false, "show event script status", "[init|setup|startup|monitor|takeip|releaseip|ipreallocated]" }, - { "enablescript", control_enablescript, false, true, - "enable an eventscript", "