1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/common/cross_process_lock.h>
#include <aws/common/byte_buf.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <unistd.h>
#include <aws/common/error.h>
#include <aws/common/file.h>
#include <aws/common/logging.h>
struct aws_cross_process_lock {
struct aws_allocator *allocator;
int locked_fd;
};
struct aws_cross_process_lock *aws_cross_process_lock_try_acquire(
struct aws_allocator *allocator,
struct aws_byte_cursor instance_nonce) {
/* validate we don't have a directory slash. */
struct aws_byte_cursor to_find = aws_byte_cursor_from_c_str("/");
struct aws_byte_cursor found;
AWS_ZERO_STRUCT(found);
if (aws_byte_cursor_find_exact(&instance_nonce, &to_find, &found) != AWS_OP_ERR &&
aws_last_error() != AWS_ERROR_STRING_MATCH_NOT_FOUND) {
AWS_LOGF_ERROR(
AWS_LS_COMMON_GENERAL,
"static: Lock " PRInSTR "creation has illegal character /",
AWS_BYTE_CURSOR_PRI(instance_nonce));
aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
return NULL;
}
/*
* The unix standard says /tmp has to be there and be writable. However, while it may be tempting to just use the
* /tmp/ directory, it often has the sticky bit set which would prevent a subprocess from being able to call open
* with create on the file. The solution is simple, just write it to a subdirectory inside
* /tmp and override umask via. chmod of 0777.
*/
struct aws_byte_cursor path_prefix = aws_byte_cursor_from_c_str("/tmp/aws_crt_cross_process_lock/");
struct aws_string *path_to_create = aws_string_new_from_cursor(allocator, &path_prefix);
/* It's probably there already and we don't care if it is. */
if (!aws_directory_exists(path_to_create)) {
/* if this call fails just let it fail on open below. */
aws_directory_create(path_to_create);
/* bypass umask by setting the perms we actually requested */
chmod(aws_string_c_str(path_to_create), S_IRWXU | S_IRWXG | S_IRWXO);
}
aws_string_destroy(path_to_create);
struct aws_byte_cursor path_suffix = aws_byte_cursor_from_c_str(".lock");
struct aws_byte_buf nonce_buf;
aws_byte_buf_init_copy_from_cursor(&nonce_buf, allocator, path_prefix);
aws_byte_buf_append_dynamic(&nonce_buf, &instance_nonce);
aws_byte_buf_append_dynamic(&nonce_buf, &path_suffix);
aws_byte_buf_append_null_terminator(&nonce_buf);
struct aws_cross_process_lock *instance_lock = NULL;
errno = 0;
int fd = open((const char *)nonce_buf.buffer, O_CREAT | O_RDWR, 0666);
if (fd < 0) {
AWS_LOGF_DEBUG(
AWS_LS_COMMON_GENERAL,
"static: Lock file %s failed to open with errno %d",
(const char *)nonce_buf.buffer,
errno);
aws_translate_and_raise_io_error_or(errno, AWS_ERROR_MUTEX_FAILED);
if (aws_last_error() == AWS_ERROR_NO_PERMISSION) {
AWS_LOGF_DEBUG(
AWS_LS_COMMON_GENERAL,
"static: Lock file %s couldn't be opened due to file ownership permissions. Attempting to open as read "
"only",
(const char *)nonce_buf.buffer);
errno = 0;
fd = open((const char *)nonce_buf.buffer, O_RDONLY);
if (fd < 0) {
AWS_LOGF_ERROR(
AWS_LS_COMMON_GENERAL,
"static: Lock file %s failed to open with read-only permissions with errno %d",
(const char *)nonce_buf.buffer,
errno);
aws_translate_and_raise_io_error_or(errno, AWS_ERROR_MUTEX_FAILED);
goto cleanup;
}
} else {
AWS_LOGF_ERROR(
AWS_LS_COMMON_GENERAL,
"static: Lock file %s failed to open. The lock cannot be acquired.",
(const char *)nonce_buf.buffer);
goto cleanup;
}
}
if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
AWS_LOGF_TRACE(
AWS_LS_COMMON_GENERAL,
"static: Lock file %s already acquired by another instance",
(const char *)nonce_buf.buffer);
close(fd);
aws_raise_error(AWS_ERROR_MUTEX_CALLER_NOT_OWNER);
goto cleanup;
}
instance_lock = aws_mem_calloc(allocator, 1, sizeof(struct aws_cross_process_lock));
instance_lock->locked_fd = fd;
instance_lock->allocator = allocator;
AWS_LOGF_TRACE(
AWS_LS_COMMON_GENERAL,
"static: Lock file %s acquired by this instance with fd %d",
(const char *)nonce_buf.buffer,
fd);
cleanup:
aws_byte_buf_clean_up(&nonce_buf);
return instance_lock;
}
void aws_cross_process_lock_release(struct aws_cross_process_lock *instance_lock) {
if (instance_lock) {
close(instance_lock->locked_fd);
AWS_LOGF_TRACE(AWS_LS_COMMON_GENERAL, "static: Lock file released for fd %d", instance_lock->locked_fd);
aws_mem_release(instance_lock->allocator, instance_lock);
}
}
|