static void VerifySQLPromise(Attributes a, Promise *pp) { char database[CF_MAXVARSIZE], table[CF_MAXVARSIZE], query[CF_BUFSIZE]; char *sp; int count = 0; CfdbConn cfdb; CfLock thislock; char lockname[CF_BUFSIZE]; snprintf(lockname, CF_BUFSIZE - 1, "db-%s", pp->promiser); thislock = AcquireLock(lockname, VUQNAME, CFSTARTTIME, a, pp, false); if (thislock.lock == NULL) { return; } database[0] = '\0'; table[0] = '\0'; for (sp = pp->promiser; *sp != '\0'; sp++) { if (strchr("./\\", *sp)) { count++; strncpy(table, sp + 1, CF_MAXVARSIZE - 1); sscanf(pp->promiser, "%[^.\\/]", database); if (strlen(database) == 0) { cfPS(cf_error, CF_FAIL, "", pp, a, "SQL database promiser syntax should be of the form \"database.table\""); PromiseRef(cf_error, pp); YieldCurrentLock(thislock); return; } } } if (count > 1) { cfPS(cf_error, CF_FAIL, "", pp, a, "SQL database promiser syntax should be of the form \"database.table\""); PromiseRef(cf_error, pp); } if (strlen(database) == 0) { strncpy(database, pp->promiser, CF_MAXVARSIZE - 1); } if (a.database.operation == NULL) { cfPS(cf_error, CF_FAIL, "", pp, a , "Missing database_operation in database promise"); PromiseRef(cf_error, pp); YieldCurrentLock(thislock); return; } if (strcmp(a.database.operation, "delete") == 0) { /* Just deal with one */ strcpy(a.database.operation, "drop"); } /* Connect to the server */ CfConnectDB(&cfdb, a.database.db_server_type, a.database.db_server_host, a.database.db_server_owner, a.database.db_server_password, database); if (!cfdb.connected) { /* If we haven't said create then db should already exist */ if ((a.database.operation) && (strcmp(a.database.operation, "create") != 0)) { CfOut(cf_error, "", "Could not connect an existing database %s - check server configuration?\n", database); PromiseRef(cf_error, pp); CfCloseDB(&cfdb); YieldCurrentLock(thislock); return; } } /* Check change of existential constraints */ if ((a.database.operation) && (strcmp(a.database.operation, "create") == 0)) { CfConnectDB(&cfdb, a.database.db_server_type, a.database.db_server_host, a.database.db_server_owner, a.database.db_server_password, a.database.db_connect_db); if (!cfdb.connected) { CfOut(cf_error, "", "Could not connect to the sql_db server for %s\n", database); return; } /* Don't drop the db if we really want to drop a table */ if ((strlen(table) == 0) || ((strlen(table) > 0) && (strcmp(a.database.operation, "drop") != 0))) { VerifyDatabasePromise(&cfdb, database, a, pp); } /* Close the database here to commit the change - might have to reopen */ CfCloseDB(&cfdb); } /* Now check the structure of the named table, if any */ if (strlen(table) == 0) { YieldCurrentLock(thislock); return; } CfConnectDB(&cfdb, a.database.db_server_type, a.database.db_server_host, a.database.db_server_owner, a.database.db_server_password, database); if (!cfdb.connected) { CfOut(cf_inform, "", "Database %s is not connected\n", database); } else { snprintf(query, CF_MAXVARSIZE - 1, "%s.%s", database, table); if (VerifyTablePromise(&cfdb, query, a.database.columns, a, pp)) { cfPS(cf_inform, CF_NOP, "", pp, a, " -> Table \"%s\" is as promised", query); } else { cfPS(cf_inform, CF_FAIL, "", pp, a, " -> Table \"%s\" is not as promised", query); } /* Finally check any row constraints on this table */ if (a.database.rows) { CfOut(cf_inform, "", " !! Database row operations are not currently supported. Please contact cfengine with suggestions."); } CfCloseDB(&cfdb); } YieldCurrentLock(thislock); }
static PromiseResult VerifySQLPromise(EvalContext *ctx, const Attributes *a, const Promise *pp) { assert(a != NULL); char database[CF_MAXVARSIZE], table[CF_MAXVARSIZE], query[CF_BUFSIZE]; char *sp; int count = 0; CfdbConn cfdb; CfLock thislock; char lockname[CF_BUFSIZE]; snprintf(lockname, CF_BUFSIZE - 1, "db-%s", pp->promiser); thislock = AcquireLock(ctx, lockname, VUQNAME, CFSTARTTIME, a->transaction.ifelapsed, a->transaction.expireafter, pp, false); if (thislock.lock == NULL) { return PROMISE_RESULT_SKIPPED; } database[0] = '\0'; table[0] = '\0'; for (sp = pp->promiser; *sp != '\0'; sp++) { if (strchr("./\\", *sp)) { count++; strlcpy(table, sp + 1, CF_MAXVARSIZE); sscanf(pp->promiser, "%[^.\\/]", database); if (strlen(database) == 0) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "SQL database promiser syntax should be of the form \"database.table\""); PromiseRef(LOG_LEVEL_ERR, pp); YieldCurrentLock(thislock); return PROMISE_RESULT_FAIL; } } } PromiseResult result = PROMISE_RESULT_NOOP; if (count > 1) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "SQL database promiser syntax should be of the form \"database.table\""); PromiseRef(LOG_LEVEL_ERR, pp); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } if (strlen(database) == 0) { strlcpy(database, pp->promiser, CF_MAXVARSIZE); } if (a->database.operation == NULL) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Missing database_operation in database promise"); PromiseRef(LOG_LEVEL_ERR, pp); YieldCurrentLock(thislock); return PROMISE_RESULT_FAIL; } if (strcmp(a->database.operation, "delete") == 0) { /* Just deal with one */ strcpy(a->database.operation, "drop"); } /* Connect to the server */ CfConnectDB(&cfdb, a->database.db_server_type, a->database.db_server_host, a->database.db_server_owner, a->database.db_server_password, database); if (!cfdb.connected) { /* If we haven't said create then db should already exist */ if ((a->database.operation) && (strcmp(a->database.operation, "create") != 0)) { Log(LOG_LEVEL_ERR, "Could not connect an existing database '%s' - check server configuration?", database); PromiseRef(LOG_LEVEL_ERR, pp); CfCloseDB(&cfdb); YieldCurrentLock(thislock); return PROMISE_RESULT_FAIL; } } /* Check change of existential constraints */ if ((a->database.operation) && (strcmp(a->database.operation, "create") == 0)) { CfConnectDB(&cfdb, a->database.db_server_type, a->database.db_server_host, a->database.db_server_owner, a->database.db_server_password, a->database.db_connect_db); if (!cfdb.connected) { Log(LOG_LEVEL_ERR, "Could not connect to the sql_db server for '%s'", database); return PROMISE_RESULT_FAIL; } /* Don't drop the db if we really want to drop a table */ if ((strlen(table) == 0) || ((strlen(table) > 0) && (strcmp(a->database.operation, "drop") != 0))) { VerifyDatabasePromise(&cfdb, database, a); } /* Close the database here to commit the change - might have to reopen */ CfCloseDB(&cfdb); } /* Now check the structure of the named table, if any */ if (strlen(table) == 0) { YieldCurrentLock(thislock); return result; } CfConnectDB(&cfdb, a->database.db_server_type, a->database.db_server_host, a->database.db_server_owner, a->database.db_server_password, database); if (!cfdb.connected) { Log(LOG_LEVEL_INFO, "Database '%s' is not connected", database); } else { snprintf(query, CF_MAXVARSIZE - 1, "%s.%s", database, table); if (VerifyTablePromise(ctx, &cfdb, query, a->database.columns, a, pp, &result)) { cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_NOOP, pp, a, "Table '%s' is as promised", query); } else { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Table '%s' is not as promised", query); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } /* Finally check any row constraints on this table */ if (a->database.rows) { Log(LOG_LEVEL_INFO, "Database row operations are not currently supported. Please contact cfengine with suggestions."); } CfCloseDB(&cfdb); } YieldCurrentLock(thislock); return result; }