Example #1
0
/*
 * Callback routine when required locks are not obtained within timeout
 * Called from parent context
 */
static void ctdb_lock_timeout_handler(struct tevent_context *ev,
				    struct tevent_timer *ttimer,
				    struct timeval current_time,
				    void *private_data)
{
	static const char * debug_locks = NULL;
	struct lock_context *lock_ctx;
	struct ctdb_context *ctdb;
	pid_t pid;

	lock_ctx = talloc_get_type_abort(private_data, struct lock_context);
	ctdb = lock_ctx->ctdb;

	if (lock_ctx->type == LOCK_RECORD || lock_ctx->type == LOCK_DB) {
		DEBUG(DEBUG_WARNING,
		      ("Unable to get %s lock on database %s for %.0lf seconds\n",
		       (lock_ctx->type == LOCK_RECORD ? "RECORD" : "DB"),
		       lock_ctx->ctdb_db->db_name,
		       timeval_elapsed(&lock_ctx->start_time)));
	} else {
		DEBUG(DEBUG_WARNING,
		      ("Unable to get ALLDB locks for %.0lf seconds\n",
		       timeval_elapsed(&lock_ctx->start_time)));
	}

	/* Fire a child process to find the blocking process. */
	if (debug_locks == NULL) {
		debug_locks = getenv("CTDB_DEBUG_LOCKS");
		if (debug_locks == NULL) {
			debug_locks = talloc_asprintf(ctdb,
						      "%s/debug_locks.sh",
						      getenv("CTDB_BASE"));
		}
	}
	if (debug_locks != NULL) {
		pid = vfork();
		if (pid == 0) {
			execl(debug_locks, debug_locks, NULL);
			_exit(0);
		}
		ctdb_track_child(ctdb, pid);
	} else {
		DEBUG(DEBUG_WARNING,
		      (__location__
		       " Unable to setup lock debugging - no memory?\n"));
	}

	/* reset the timeout timer */
	// talloc_free(lock_ctx->ttimer);
	lock_ctx->ttimer = tevent_add_timer(ctdb->ev,
					    lock_ctx,
					    timeval_current_ofs(10, 0),
					    ctdb_lock_timeout_handler,
					    (void *)lock_ctx);
}
Example #2
0
/*
 * This function forks a child process and drops the realtime 
 * scheduler for the child process.
 */
pid_t ctdb_fork(struct ctdb_context *ctdb)
{
	pid_t pid;

	pid = fork();
	if (pid == -1) {
		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.
		 * tevent needs to be stopped before closing sockets.
		 */
		if (ctdb->ev != NULL) {
			talloc_free(ctdb->ev);
			ctdb->ev = NULL;
		}
		if (ctdb->daemon.sd != -1) {
			close(ctdb->daemon.sd);
			ctdb->daemon.sd = -1;
		}
		if (ctdb->methods != NULL) {
			ctdb->methods->shutdown(ctdb);
		}

		/* The child does not need to be realtime */
		if (ctdb->do_setsched) {
			reset_scheduler();
		}
		ctdb->can_send_controls = false;

		return 0;
	}

	ctdb_track_child(ctdb, pid);
	return pid;
}
Example #3
0
/*
 * Schedule a new lock child process
 * Set up callback handler and timeout handler
 */
static void ctdb_lock_schedule(struct ctdb_context *ctdb)
{
	struct lock_context *lock_ctx, *next_ctx, *active_ctx;
	int ret;
	TALLOC_CTX *tmp_ctx;
	const char *helper = BINDIR "/ctdb_lock_helper";
	static const char *prog = NULL;
	char **args;

	if (prog == NULL) {
		const char *t;

		t = getenv("CTDB_LOCK_HELPER");
		if (t != NULL) {
			prog = talloc_strdup(ctdb, t);
		} else {
			prog = talloc_strdup(ctdb, helper);
		}
		CTDB_NO_MEMORY_VOID(ctdb, prog);
	}

	if (ctdb->lock_pending == NULL) {
		return;
	}

	/* Find a lock context with requests */
	lock_ctx = ctdb->lock_pending;
	while (lock_ctx != NULL) {
		next_ctx = lock_ctx->next;
		if (! lock_ctx->req_queue) {
			DEBUG(DEBUG_INFO, ("Removing lock context without lock requests\n"));
			DLIST_REMOVE(ctdb->lock_pending, lock_ctx);
			ctdb->lock_num_pending--;
			CTDB_DECREMENT_STAT(ctdb, locks.num_pending);
			if (lock_ctx->ctdb_db) {
				CTDB_DECREMENT_DB_STAT(lock_ctx->ctdb_db, locks.num_pending);
			}
			talloc_free(lock_ctx);
		} else {
			active_ctx = find_lock_context(ctdb->lock_current, lock_ctx->ctdb_db,
						       lock_ctx->key, lock_ctx->priority,
						       lock_ctx->type);
			if (active_ctx == NULL) {
				if (lock_ctx->ctdb_db == NULL ||
				    lock_ctx->ctdb_db->lock_num_current < MAX_LOCK_PROCESSES_PER_DB) {
					/* Found a lock context with lock requests */
					break;
				}
			}

			/* There is already a child waiting for the
			 * same key.  So don't schedule another child
			 * just yet.
			 */
		}
		lock_ctx = next_ctx;
	}

	if (lock_ctx == NULL) {
		return;
	}

	lock_ctx->child = -1;
	ret = pipe(lock_ctx->fd);
	if (ret != 0) {
		DEBUG(DEBUG_ERR, ("Failed to create pipe in ctdb_lock_schedule\n"));
		return;
	}

	set_close_on_exec(lock_ctx->fd[0]);

	/* Create data for child process */
	tmp_ctx = talloc_new(lock_ctx);
	if (tmp_ctx == NULL) {
		DEBUG(DEBUG_ERR, ("Failed to allocate memory for helper args\n"));
		close(lock_ctx->fd[0]);
		close(lock_ctx->fd[1]);
		return;
	}

	/* Create arguments for lock helper */
	args = lock_helper_args(tmp_ctx, lock_ctx, lock_ctx->fd[1]);
	if (args == NULL) {
		DEBUG(DEBUG_ERR, ("Failed to create lock helper args\n"));
		close(lock_ctx->fd[0]);
		close(lock_ctx->fd[1]);
		talloc_free(tmp_ctx);
		return;
	}

	lock_ctx->child = vfork();

	if (lock_ctx->child == (pid_t)-1) {
		DEBUG(DEBUG_ERR, ("Failed to create a child in ctdb_lock_schedule\n"));
		close(lock_ctx->fd[0]);
		close(lock_ctx->fd[1]);
		talloc_free(tmp_ctx);
		return;
	}


	/* Child process */
	if (lock_ctx->child == 0) {
		ret = execv(prog, args);
		if (ret < 0) {
			DEBUG(DEBUG_ERR, ("Failed to execute helper %s (%d, %s)\n",
					  prog, errno, strerror(errno)));
		}
		_exit(1);
	}

	/* Parent process */
	ctdb_track_child(ctdb, lock_ctx->child);
	close(lock_ctx->fd[1]);

	talloc_set_destructor(lock_ctx, ctdb_lock_context_destructor);

	talloc_free(tmp_ctx);

	/* Set up timeout handler */
	lock_ctx->ttimer = tevent_add_timer(ctdb->ev,
					    lock_ctx,
					    timeval_current_ofs(10, 0),
					    ctdb_lock_timeout_handler,
					    (void *)lock_ctx);
	if (lock_ctx->ttimer == NULL) {
		ctdb_kill(ctdb, lock_ctx->child, SIGKILL);
		lock_ctx->child = -1;
		talloc_set_destructor(lock_ctx, NULL);
		close(lock_ctx->fd[0]);
		return;
	}

	/* Set up callback */
	lock_ctx->tfd = tevent_add_fd(ctdb->ev,
				      lock_ctx,
				      lock_ctx->fd[0],
				      EVENT_FD_READ,
				      ctdb_lock_handler,
				      (void *)lock_ctx);
	if (lock_ctx->tfd == NULL) {
		TALLOC_FREE(lock_ctx->ttimer);
		ctdb_kill(ctdb, lock_ctx->child, SIGKILL);
		lock_ctx->child = -1;
		talloc_set_destructor(lock_ctx, NULL);
		close(lock_ctx->fd[0]);
		return;
	}
	tevent_fd_set_auto_close(lock_ctx->tfd);

	/* Move the context from pending to current */
	DLIST_REMOVE(ctdb->lock_pending, lock_ctx);
	ctdb->lock_num_pending--;
	DLIST_ADD_END(ctdb->lock_current, lock_ctx, NULL);
	if (lock_ctx->ctdb_db) {
		lock_ctx->ctdb_db->lock_num_current++;
		CTDB_INCREMENT_STAT(lock_ctx->ctdb, locks.num_current);
		CTDB_INCREMENT_DB_STAT(lock_ctx->ctdb_db, locks.num_current);
	}
}