| /*------------------------------------------------------------------------- |
| * |
| * backend_cancel.c |
| * Backend cancellation messaging |
| * |
| * |
| * Module for supporting passing a user defined message to a cancelled, |
| * or terminated, backend from the user/administrator. |
| * |
| * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group |
| * Portions Copyright (c) 1994, Regents of the University of California |
| * |
| * IDENTIFICATION |
| * src/backend/utils/misc/backend_cancel.c |
| * |
| *------------------------------------------------------------------------- |
| */ |
| #include "postgres.h" |
| |
| #include "miscadmin.h" |
| #include "storage/ipc.h" |
| #include "storage/spin.h" |
| #include "storage/shmem.h" |
| #include "utils/backend_cancel.h" |
| |
| /* |
| * Each backend is registered per pid in the array which is indexed by Backend |
| * ID. Reading and writing the message is protected by a per-slot spinlock. |
| */ |
| typedef struct |
| { |
| pid_t pid; |
| slock_t mutex; |
| char message[MAX_CANCEL_MSG]; |
| int len; |
| } BackendCancelShmemStruct; |
| |
| static BackendCancelShmemStruct *BackendCancelSlots = NULL; |
| static volatile BackendCancelShmemStruct *MyCancelSlot = NULL; |
| |
| static void CleanupCancelBackend(int status, Datum argument); |
| |
| Size |
| CancelBackendMsgShmemSize(void) |
| { |
| return MaxBackends * sizeof(BackendCancelShmemStruct); |
| } |
| |
| void |
| BackendCancelShmemInit(void) |
| { |
| Size size = CancelBackendMsgShmemSize(); |
| bool found; |
| int i; |
| |
| BackendCancelSlots = (BackendCancelShmemStruct *) |
| ShmemInitStruct("BackendCancelSlots", size, &found); |
| |
| if (!found) |
| { |
| MemSet(BackendCancelSlots, 0, size); |
| |
| for (i = 0; i < MaxBackends; i++) |
| SpinLockInit(&(BackendCancelSlots[i].mutex)); |
| } |
| } |
| |
| void |
| BackendCancelInit(int backend_id) |
| { |
| volatile BackendCancelShmemStruct *slot; |
| |
| slot = &BackendCancelSlots[backend_id - 1]; |
| |
| slot->message[0] = '\0'; |
| slot->len = 0; |
| slot->pid = MyProcPid; |
| |
| MyCancelSlot = slot; |
| |
| on_shmem_exit(CleanupCancelBackend, Int32GetDatum(backend_id)); |
| } |
| |
| static void |
| CleanupCancelBackend(int status, Datum argument) |
| { |
| int backend_id = DatumGetInt32(argument); |
| volatile BackendCancelShmemStruct *slot; |
| |
| slot = &BackendCancelSlots[backend_id - 1]; |
| |
| Assert(slot == MyCancelSlot); |
| |
| MyCancelSlot = NULL; |
| |
| if (slot->len > 0) |
| slot->message[0] = '\0'; |
| |
| slot->len = 0; |
| slot->pid = 0; |
| } |
| |
| /* |
| * Sets a cancellation message for the backend with the specified pid, and |
| * returns the length of message actually created. If the returned length |
| * is less than the length of the message parameter, truncation has occurred. |
| * If the backend wasn't found and no message was set, -1 is returned. If two |
| * backends collide in setting a message, the existing message will be |
| * overwritten by the last one in. |
| */ |
| int |
| SetBackendCancelMessage(pid_t backend, char *message) |
| { |
| BackendCancelShmemStruct *slot; |
| int i; |
| int message_len; |
| |
| if (!message) |
| return 0; |
| |
| for (i = 0; i < MaxBackends; i++) |
| { |
| slot = &BackendCancelSlots[i]; |
| |
| if (slot->pid != 0 && slot->pid == backend) |
| { |
| SpinLockAcquire(&slot->mutex); |
| if (slot->pid != backend) |
| { |
| SpinLockRelease(&slot->mutex); |
| goto error; |
| } |
| |
| strlcpy(slot->message, message, sizeof(slot->message)); |
| slot->len = strlen(slot->message); |
| message_len = slot->len; |
| SpinLockRelease(&slot->mutex); |
| |
| return message_len; |
| } |
| } |
| |
| error: |
| |
| elog(LOG, "Cancellation message requested for missing backend %d by %d", |
| (int) backend, MyProcPid); |
| |
| return -1; |
| } |
| |
| bool |
| HasCancelMessage(void) |
| { |
| volatile BackendCancelShmemStruct *slot = MyCancelSlot; |
| bool has_message = false; |
| |
| if (slot != NULL) |
| { |
| SpinLockAcquire(&slot->mutex); |
| has_message = (slot->len > 0); |
| SpinLockRelease(&slot->mutex); |
| } |
| |
| return has_message; |
| } |
| |
| /* |
| * Return the configured cancellation message and its length. If the returned |
| * length is greater than the size of the passed buffer, truncation has been |
| * performed. The message is cleared on reading. |
| */ |
| int |
| GetCancelMessage(char **buffer, size_t buf_len) |
| { |
| volatile BackendCancelShmemStruct *slot = MyCancelSlot; |
| int msg_length = 0; |
| |
| if (slot != NULL && slot->len > 0) |
| { |
| SpinLockAcquire(&slot->mutex); |
| strlcpy(*buffer, (const char *) slot->message, buf_len); |
| msg_length = slot->len; |
| slot->len = 0; |
| slot->message[0] = '\0'; |
| SpinLockRelease(&slot->mutex); |
| } |
| |
| return msg_length; |
| } |