]>
Commit | Line | Data |
---|---|---|
72d0e1cb SG |
1 | /* liblxcapi |
2 | * | |
3 | * Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>. | |
4 | * Copyright © 2012 Canonical Ltd. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2, as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License along | |
16 | * with this program; if not, write to the Free Software Foundation, Inc., | |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
18 | */ | |
19 | #include "../lxc/lxclock.h" | |
20 | #include <unistd.h> | |
21 | #include <signal.h> | |
22 | #include <stdio.h> | |
23 | #include <sys/types.h> | |
24 | #include <sys/wait.h> | |
25 | #include <stdlib.h> | |
26 | ||
27 | #define mycontainername "lxctest.sem" | |
28 | #define TIMEOUT_SECS 3 | |
29 | ||
30 | int timedout; | |
31 | int pid_to_kill; | |
32 | ||
33 | void timeouthandler(int sig) | |
34 | { | |
35 | // timeout received | |
36 | timedout = 1; | |
37 | kill(pid_to_kill, SIGTERM); | |
38 | } | |
39 | ||
40 | void starttimer(int secs) | |
41 | { | |
42 | timedout = 0; | |
43 | signal(SIGALRM, timeouthandler); | |
44 | alarm(secs); | |
45 | } | |
46 | void stoptimer(void) | |
47 | { | |
48 | alarm(0); | |
49 | signal(SIGALRM, NULL); | |
50 | } | |
51 | ||
52 | int test_one_lock(sem_t *lock) | |
53 | { | |
54 | int ret; | |
55 | starttimer(TIMEOUT_SECS); | |
56 | ret = lxclock(lock, TIMEOUT_SECS*2); | |
57 | stoptimer(); | |
58 | if (ret == 0) { | |
59 | lxcunlock(lock); | |
60 | return 0; | |
61 | } | |
62 | if (timedout) | |
63 | fprintf(stderr, "%d: timed out waiting for lock\n", __LINE__); | |
64 | else | |
65 | fprintf(stderr, "%d: failed to get single lock\n", __LINE__); | |
66 | return 1; | |
67 | } | |
68 | ||
69 | /* | |
70 | * get one lock. Fork a second task to try to get a second lock, | |
71 | * with infinite timeout. If our alarm hits, kill the second | |
72 | * task. If second task does not | |
73 | */ | |
74 | int test_two_locks(sem_t *lock) | |
75 | { | |
76 | int status; | |
77 | int ret; | |
78 | ||
79 | ret = lxclock(lock, 1); | |
80 | if (ret) { | |
81 | fprintf(stderr, "%d: Error getting first lock\n", __LINE__); | |
82 | return 2; | |
83 | } | |
84 | ||
85 | pid_to_kill = fork(); | |
86 | if (pid_to_kill < 0) { | |
87 | fprintf(stderr, "%d: Failed to fork\n", __LINE__); | |
88 | lxcunlock(lock); | |
89 | return 3; | |
90 | } | |
91 | ||
92 | if (pid_to_kill == 0) { // child | |
93 | ret = lxclock(lock, TIMEOUT_SECS*2); | |
94 | if (ret == 0) { | |
95 | lxcunlock(lock); | |
96 | exit(0); | |
97 | } | |
98 | fprintf(stderr, "%d: child, was not able to get lock\n", __LINE__); | |
99 | exit(1); | |
100 | } | |
101 | starttimer(TIMEOUT_SECS); | |
102 | waitpid(pid_to_kill, &status, 0); | |
103 | stoptimer(); | |
104 | if (WIFEXITED(status)) { | |
105 | // child exited normally - timeout didn't kill it | |
106 | if (WEXITSTATUS(status) == 0) | |
107 | fprintf(stderr, "%d: child was able to get the lock\n", __LINE__); | |
108 | else | |
109 | fprintf(stderr, "%d: child timed out too early\n", __LINE__); | |
110 | lxcunlock(lock); | |
111 | return 1; | |
112 | } | |
113 | lxcunlock(lock); | |
114 | return 0; | |
115 | } | |
116 | ||
117 | /* | |
118 | * get one lock. try to get second lock, but asking for timeout. If | |
119 | * should return failure. If our own alarm, set at twice the lock | |
120 | * request's timeout, hits, then lxclock() did not properly time out. | |
121 | */ | |
122 | int test_with_timeout(sem_t *lock) | |
123 | { | |
124 | int status; | |
125 | int ret = 0; | |
126 | ||
127 | ret = lxclock(lock, 0); | |
128 | if (ret) { | |
129 | fprintf(stderr, "%d: Error getting first lock\n", __LINE__); | |
130 | return 2; | |
131 | } | |
132 | pid_to_kill = fork(); | |
133 | if (pid_to_kill < 0) { | |
134 | fprintf(stderr, "%d: Error on fork\n", __LINE__); | |
135 | lxcunlock(lock); | |
136 | return 2; | |
137 | } | |
138 | if (pid_to_kill == 0) { | |
139 | ret = lxclock(lock, TIMEOUT_SECS); | |
140 | if (ret == 0) { | |
141 | lxcunlock(lock); | |
142 | exit(0); | |
143 | } | |
144 | exit(1); | |
145 | } | |
146 | starttimer(TIMEOUT_SECS * 2); | |
147 | waitpid(pid_to_kill, &status, 0); | |
148 | stoptimer(); | |
149 | if (!WIFEXITED(status)) { | |
150 | fprintf(stderr, "%d: lxclock did not honor its timeout\n", __LINE__); | |
151 | lxcunlock(lock); | |
152 | return 1; | |
153 | } | |
154 | if (WEXITSTATUS(status) == 0) { | |
155 | fprintf(stderr, "%d: child was able to get lock, should have failed with timeout\n", __LINE__); | |
156 | ret = 1; | |
157 | } | |
158 | lxcunlock(lock); | |
159 | return ret; | |
160 | } | |
161 | ||
162 | int main(int argc, char *argv[]) | |
163 | { | |
164 | int ret, sval, r; | |
165 | sem_t *lock; | |
166 | ||
167 | lock = lxc_newlock(NULL); | |
168 | if (!lock) { | |
169 | fprintf(stderr, "%d: failed to get unnamed lock\n", __LINE__); | |
170 | exit(1); | |
171 | } | |
172 | ret = lxclock(lock, 0); | |
173 | if (ret) { | |
174 | fprintf(stderr, "%d: failed to take unnamed lock (%d)\n", __LINE__, ret); | |
175 | exit(1); | |
176 | } | |
177 | ||
178 | ret = lxcunlock(lock); | |
179 | if (ret) { | |
180 | fprintf(stderr, "%d: failed to put unnamed lock (%d)\n", __LINE__, ret); | |
181 | exit(1); | |
182 | } | |
183 | ||
184 | sem_destroy(lock); | |
185 | free(lock); | |
186 | ||
187 | lock = lxc_newlock(mycontainername); | |
188 | if (!lock) { | |
189 | fprintf(stderr, "%d: failed to get lock\n", __LINE__); | |
190 | exit(1); | |
191 | } | |
192 | r = sem_getvalue(lock, &sval); | |
193 | if (!r) { | |
194 | fprintf(stderr, "%d: sem value at start is %d\n", __LINE__, sval); | |
195 | } else { | |
196 | fprintf(stderr, "%d: failed to get initial value\n", __LINE__); | |
197 | } | |
198 | ||
199 | ret = test_one_lock(lock); | |
200 | if (ret) { | |
201 | fprintf(stderr, "%d: test failed\n", __LINE__); | |
202 | goto out; | |
203 | } | |
204 | r = sem_getvalue(lock, &sval); | |
205 | if (!r) { | |
206 | fprintf(stderr, "%d: sem value is %d\n", __LINE__, sval); | |
207 | } else { | |
208 | fprintf(stderr, "%d: failed to get sem value\n", __LINE__); | |
209 | } | |
210 | ||
211 | ret = test_two_locks(lock); | |
212 | if (ret) { | |
213 | fprintf(stderr, "%d: test failed\n", __LINE__); | |
214 | goto out; | |
215 | } | |
216 | r = sem_getvalue(lock, &sval); | |
217 | if (!r) { | |
218 | fprintf(stderr, "%d: sem value is %d\n", __LINE__, sval); | |
219 | } else { | |
220 | fprintf(stderr, "%d: failed to get value\n", __LINE__); | |
221 | } | |
222 | ||
223 | ret = test_with_timeout(lock); | |
224 | if (ret) { | |
225 | fprintf(stderr, "%d: test failed\n", __LINE__); | |
226 | goto out; | |
227 | } | |
228 | r = sem_getvalue(lock, &sval); | |
229 | if (!r) { | |
230 | fprintf(stderr, "%d: sem value is %d\n", __LINE__, sval); | |
231 | } else { | |
232 | fprintf(stderr, "%d: failed to get value\n", __LINE__); | |
233 | } | |
234 | ||
235 | fprintf(stderr, "all tests passed\n"); | |
236 | ||
237 | out: | |
238 | exit(ret); | |
239 | } |