4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 #include <sys/types.h>
43 #include <sys/socket.h>
48 #include <nbdkit-filter.h>
50 #include "ascii-ctype.h"
53 #include "unix-path-max.h"
55 static char *sockfile
;
70 pause_config (nbdkit_next_config
*next
, nbdkit_backend
*nxdata
,
71 const char *key
, const char *value
)
73 if (strcmp (key
, "pause-control") == 0) {
75 sockfile
= nbdkit_absolute_path (value
);
81 return next (nxdata
, key
, value
);
85 pause_config_complete (nbdkit_next_config_complete
*next
,
86 nbdkit_backend
*nxdata
)
89 struct sockaddr_un addr
;
91 if (sockfile
== NULL
) {
92 nbdkit_error ("pause-control socket was not set");
95 len
= strlen (sockfile
);
96 if (len
>= UNIX_PATH_MAX
) {
97 nbdkit_error ("pause-control socket path too long: "
98 "length %zu > max %d bytes",
99 len
, UNIX_PATH_MAX
-1);
103 /* If the socket already exists, remove it. */
107 sock
= socket (AF_UNIX
, SOCK_STREAM
|SOCK_CLOEXEC
, 0);
109 /* Fortunately, this code is only run at startup, so there is no
110 * risk of the fd leaking to a plugin's fork()
112 sock
= set_cloexec (socket (AF_UNIX
, SOCK_STREAM
, 0));
115 nbdkit_error ("socket: %m");
119 addr
.sun_family
= AF_UNIX
;
120 memcpy (addr
.sun_path
, sockfile
, len
+1 /* trailing \0 */);
122 if (bind (sock
, (struct sockaddr
*) &addr
, sizeof addr
) == -1) {
123 nbdkit_error ("%s: %m", sockfile
);
127 if (listen (sock
, SOMAXCONN
) == -1) {
128 nbdkit_error ("listen: %m");
132 return next (nxdata
);
135 #define pause_config_help \
136 "pause-control=SOCKET Control socket."
138 /* This is locked by the background thread when we're paused, causing
139 * all requests in the main threads to hang.
141 static pthread_mutex_t paused
= PTHREAD_MUTEX_INITIALIZER
;
142 static bool is_paused
= false;
144 /* This keeps track of the number of NBD requests in flight. */
145 static pthread_mutex_t count_lock
= PTHREAD_MUTEX_INITIALIZER
;
146 static pthread_cond_t count_cond
= PTHREAD_COND_INITIALIZER
;
147 static unsigned count_requests
= 0;
152 if (is_paused
) return;
154 /* Grabbing the paused lock is enough to stop request processing. */
155 pthread_mutex_lock (&paused
);
158 /* However we must also wait until all outstanding requests have
159 * been completed before we send the acknowledgement.
161 nbdkit_debug ("pause: pausing, waiting for requests to complete");
162 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&count_lock
);
163 while (count_requests
> 0)
164 pthread_cond_wait (&count_cond
, &count_lock
);
165 nbdkit_debug ("pause: paused");
171 if (!is_paused
) return;
173 /* Release the worker threads. */
175 pthread_mutex_unlock (&paused
);
176 nbdkit_debug ("pause: resumed");
179 /* Background thread which monitors the control socket. This can only
180 * accept one connection at a time.
183 control_socket_thread (void *arg
)
191 s
= accept4 (sock
, NULL
, NULL
, SOCK_CLOEXEC
);
193 /* This isn't thread-safe but there's not a lot we can do. */
194 s
= set_cloexec (accept (sock
, NULL
, NULL
));
196 if (s
== -1) goto out
;
198 /* Read commands (which are single bytes) until end of file. */
199 while ((n
= read (s
, &c
, 1)) == 1) {
210 /* For convenience of interactive use, ignore and don't
211 * respond to some whitespace characters.
214 default: /* Unknown command. */
217 /* Send the response. */
218 c
= ascii_toupper (c
);
219 n
= write (s
, &c
, 1);
220 if (n
== -1) goto out
;
225 if (errno
&& (errno
!= EINTR
&& errno
!= EAGAIN
))
226 nbdkit_error ("accept: %m");
235 /* Start the background thread after fork. */
237 pause_after_fork (nbdkit_backend
*nxdata
)
242 err
= pthread_create (&thread
, NULL
, control_socket_thread
, NULL
);
245 nbdkit_error ("pthread_create: %m");
251 /* This is called before processing each NBD request. */
255 /* This will hang if we're paused. */
256 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&paused
);
258 /* Count the number of requests in flight. */
259 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&count_lock
);
263 /* This is called after processing each NBD request. */
267 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&count_lock
);
269 pthread_cond_signal (&count_cond
);
274 pause_pread (nbdkit_next
*next
,
275 void *handle
, void *buf
, uint32_t count
, uint64_t offset
,
276 uint32_t flags
, int *err
)
281 r
= next
->pread (next
, buf
, count
, offset
, flags
, err
);
288 pause_pwrite (nbdkit_next
*next
,
290 const void *buf
, uint32_t count
, uint64_t offset
, uint32_t flags
,
296 r
= next
->pwrite (next
, buf
, count
, offset
, flags
, err
);
303 pause_zero (nbdkit_next
*next
,
304 void *handle
, uint32_t count
, uint64_t offset
, uint32_t flags
,
310 r
= next
->zero (next
, count
, offset
, flags
, err
);
317 pause_trim (nbdkit_next
*next
,
318 void *handle
, uint32_t count
, uint64_t offset
,
319 uint32_t flags
, int *err
)
324 r
= next
->trim (next
, count
, offset
, flags
, err
);
331 pause_extents (nbdkit_next
*next
,
332 void *handle
, uint32_t count
, uint64_t offset
, uint32_t flags
,
333 struct nbdkit_extents
*extents
, int *err
)
338 r
= next
->extents (next
, count
, offset
, flags
, extents
, err
);
345 pause_cache (nbdkit_next
*next
,
346 void *handle
, uint32_t count
, uint64_t offset
, uint32_t flags
,
352 r
= next
->cache (next
, count
, offset
, flags
, err
);
357 static struct nbdkit_filter filter
= {
359 .longname
= "nbdkit pause filter",
360 .unload
= pause_unload
,
361 .config
= pause_config
,
362 .config_complete
= pause_config_complete
,
363 .config_help
= pause_config_help
,
364 .after_fork
= pause_after_fork
,
365 .pread
= pause_pread
,
366 .pwrite
= pause_pwrite
,
369 .extents
= pause_extents
,
370 .cache
= pause_cache
,
373 NBDKIT_REGISTER_FILTER (filter
)