2013-07-22 19:35:28 +04:00
|
|
|
#define _XOPEN_SOURCE
|
|
|
|
|
2011-09-23 12:00:45 +04:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <dirent.h>
|
2012-04-13 17:52:35 +04:00
|
|
|
#include <sys/sendfile.h>
|
2011-09-23 12:00:45 +04:00
|
|
|
#include <fcntl.h>
|
2014-09-02 00:25:13 +04:00
|
|
|
#include <poll.h>
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2011-12-19 21:05:02 +04:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/ptrace.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/resource.h>
|
2011-09-23 12:00:45 +04:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <sys/vfs.h>
|
|
|
|
#include <sys/ptrace.h>
|
|
|
|
#include <sys/wait.h>
|
2011-12-05 12:07:52 +04:00
|
|
|
#include <sys/resource.h>
|
2011-12-19 21:05:02 +04:00
|
|
|
#include <sys/wait.h>
|
2015-11-10 11:43:00 +03:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/tcp.h>
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
#include "compiler.h"
|
2013-01-09 17:02:47 +04:00
|
|
|
#include "asm/types.h"
|
2011-09-23 12:00:45 +04:00
|
|
|
#include "list.h"
|
|
|
|
#include "util.h"
|
2013-11-02 01:04:07 +04:00
|
|
|
#include "rst-malloc.h"
|
2013-11-05 12:33:03 +04:00
|
|
|
#include "image.h"
|
|
|
|
#include "vma.h"
|
2014-01-31 15:11:28 +04:00
|
|
|
#include "mem.h"
|
2015-07-20 12:34:15 +03:00
|
|
|
#include "namespaces.h"
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2013-11-06 17:21:11 +04:00
|
|
|
#include "cr_options.h"
|
2013-11-05 20:17:47 +04:00
|
|
|
#include "servicefd.h"
|
2014-01-28 22:36:53 +04:00
|
|
|
#include "cr-service.h"
|
Add inherit fd support
There are cases where a process's file descriptor cannot be restored
from the checkpoint images. For example, a pipe file descriptor with
one end in the checkpointed process and the other end in a separate
process (that was not part of the checkpointed process tree) cannot be
restored because after checkpoint the pipe will be broken.
There are also cases where the user wants to use a new file during
restore instead of the original file at checkpoint time. For example,
the user wants to change the log file of a process from /path/to/oldlog
to /path/to/newlog.
In these cases, criu's caller should set up a new file descriptor to be
inherited by the restored process and specify the file descriptor with the
--inherit-fd command line option. The argument of --inherit-fd has the
format fd[%d]:%s, where %d tells criu which of its own file descriptors
to use for restoring the file identified by %s.
As a debugging aid, if the argument has the format debug[%d]:%s, it tells
criu to write out the string after colon to the file descriptor %d. This
can be used, for example, as an easy way to leave a "restore marker"
in the output stream of the process.
It's important to note that inherit fd support breaks applications
that depend on the state of the file descriptor being inherited. So,
consider inherit fd only for specific use cases that you know for sure
won't break the application.
For examples please visit http://criu.org/Category:HOWTO.
v2: Added a check in send_fd_to_self() to avoid closing an inherit fd.
Also, as an extra measure of caution, added checks in the inherit fd
look up functions to make sure that the inherit fd hasn't been reused.
The patch also includes minor cosmetic changes.
Signed-off-by: Saied Kazemi <saied@google.com>
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2014-12-09 23:20:00 +03:00
|
|
|
#include "files.h"
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2014-12-11 22:55:11 +02:00
|
|
|
#include "cr-errno.h"
|
|
|
|
|
2012-11-29 18:16:00 +03:00
|
|
|
#define VMA_OPT_LEN 128
|
|
|
|
|
|
|
|
static void vma_opt_str(const struct vma_area *v, char *opt)
|
|
|
|
{
|
|
|
|
int p = 0;
|
|
|
|
|
|
|
|
#define opt2s(_o, _s) do { \
|
2014-02-04 00:08:16 +04:00
|
|
|
if (v->e->status & _o) \
|
2012-11-29 18:16:00 +03:00
|
|
|
p += sprintf(opt + p, _s " "); \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
opt[p] = '\0';
|
|
|
|
opt2s(VMA_AREA_REGULAR, "reg");
|
|
|
|
opt2s(VMA_AREA_STACK, "stk");
|
|
|
|
opt2s(VMA_AREA_VSYSCALL, "vsys");
|
|
|
|
opt2s(VMA_AREA_VDSO, "vdso");
|
2014-06-20 19:35:06 +04:00
|
|
|
opt2s(VMA_AREA_VVAR, "vvar");
|
2012-11-29 18:16:00 +03:00
|
|
|
opt2s(VMA_AREA_HEAP, "heap");
|
|
|
|
opt2s(VMA_FILE_PRIVATE, "fp");
|
|
|
|
opt2s(VMA_FILE_SHARED, "fs");
|
|
|
|
opt2s(VMA_ANON_SHARED, "as");
|
|
|
|
opt2s(VMA_ANON_PRIVATE, "ap");
|
|
|
|
opt2s(VMA_AREA_SYSVIPC, "sysv");
|
|
|
|
opt2s(VMA_AREA_SOCKET, "sk");
|
|
|
|
|
|
|
|
#undef opt2s
|
|
|
|
}
|
|
|
|
|
2012-03-06 14:20:00 +04:00
|
|
|
void pr_vma(unsigned int loglevel, const struct vma_area *vma_area)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
2012-11-29 18:16:00 +03:00
|
|
|
char opt[VMA_OPT_LEN];
|
2013-01-16 19:20:08 +04:00
|
|
|
memset(opt, 0, VMA_OPT_LEN);
|
2012-11-29 18:16:00 +03:00
|
|
|
|
2011-09-23 12:00:45 +04:00
|
|
|
if (!vma_area)
|
|
|
|
return;
|
|
|
|
|
2012-11-29 18:16:00 +03:00
|
|
|
vma_opt_str(vma_area, opt);
|
2014-12-17 18:11:09 +03:00
|
|
|
print_on_level(loglevel, "%#"PRIx64"-%#"PRIx64" (%"PRIi64"K) prot %#x flags %#x st %#x off %#"PRIx64" "
|
2013-01-16 19:20:08 +04:00
|
|
|
"%s shmid: %#"PRIx64"\n",
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->start, vma_area->e->end,
|
2012-11-29 18:16:00 +03:00
|
|
|
KBYTES(vma_area_len(vma_area)),
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->prot,
|
|
|
|
vma_area->e->flags,
|
2014-12-17 18:11:09 +03:00
|
|
|
vma_area->e->status,
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->pgoff,
|
|
|
|
opt, vma_area->e->shmid);
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
|
2011-09-27 18:28:09 +04:00
|
|
|
int close_safe(int *fd)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
Add inherit fd support
There are cases where a process's file descriptor cannot be restored
from the checkpoint images. For example, a pipe file descriptor with
one end in the checkpointed process and the other end in a separate
process (that was not part of the checkpointed process tree) cannot be
restored because after checkpoint the pipe will be broken.
There are also cases where the user wants to use a new file during
restore instead of the original file at checkpoint time. For example,
the user wants to change the log file of a process from /path/to/oldlog
to /path/to/newlog.
In these cases, criu's caller should set up a new file descriptor to be
inherited by the restored process and specify the file descriptor with the
--inherit-fd command line option. The argument of --inherit-fd has the
format fd[%d]:%s, where %d tells criu which of its own file descriptors
to use for restoring the file identified by %s.
As a debugging aid, if the argument has the format debug[%d]:%s, it tells
criu to write out the string after colon to the file descriptor %d. This
can be used, for example, as an easy way to leave a "restore marker"
in the output stream of the process.
It's important to note that inherit fd support breaks applications
that depend on the state of the file descriptor being inherited. So,
consider inherit fd only for specific use cases that you know for sure
won't break the application.
For examples please visit http://criu.org/Category:HOWTO.
v2: Added a check in send_fd_to_self() to avoid closing an inherit fd.
Also, as an extra measure of caution, added checks in the inherit fd
look up functions to make sure that the inherit fd hasn't been reused.
The patch also includes minor cosmetic changes.
Signed-off-by: Saied Kazemi <saied@google.com>
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2014-12-09 23:20:00 +03:00
|
|
|
|
2011-09-27 18:28:09 +04:00
|
|
|
if (*fd > -1) {
|
|
|
|
ret = close(*fd);
|
|
|
|
if (!ret)
|
|
|
|
*fd = -1;
|
|
|
|
else
|
2012-02-01 02:08:04 +04:00
|
|
|
pr_perror("Unable to close fd %d", *fd);
|
2011-09-27 18:28:09 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-04-12 19:50:00 +04:00
|
|
|
int reopen_fd_as_safe(char *file, int line, int new_fd, int old_fd, bool allow_reuse_fd)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
2012-01-13 13:15:57 +04:00
|
|
|
int tmp;
|
|
|
|
|
2011-09-23 12:00:45 +04:00
|
|
|
if (old_fd != new_fd) {
|
Add inherit fd support
There are cases where a process's file descriptor cannot be restored
from the checkpoint images. For example, a pipe file descriptor with
one end in the checkpointed process and the other end in a separate
process (that was not part of the checkpointed process tree) cannot be
restored because after checkpoint the pipe will be broken.
There are also cases where the user wants to use a new file during
restore instead of the original file at checkpoint time. For example,
the user wants to change the log file of a process from /path/to/oldlog
to /path/to/newlog.
In these cases, criu's caller should set up a new file descriptor to be
inherited by the restored process and specify the file descriptor with the
--inherit-fd command line option. The argument of --inherit-fd has the
format fd[%d]:%s, where %d tells criu which of its own file descriptors
to use for restoring the file identified by %s.
As a debugging aid, if the argument has the format debug[%d]:%s, it tells
criu to write out the string after colon to the file descriptor %d. This
can be used, for example, as an easy way to leave a "restore marker"
in the output stream of the process.
It's important to note that inherit fd support breaks applications
that depend on the state of the file descriptor being inherited. So,
consider inherit fd only for specific use cases that you know for sure
won't break the application.
For examples please visit http://criu.org/Category:HOWTO.
v2: Added a check in send_fd_to_self() to avoid closing an inherit fd.
Also, as an extra measure of caution, added checks in the inherit fd
look up functions to make sure that the inherit fd hasn't been reused.
The patch also includes minor cosmetic changes.
Signed-off-by: Saied Kazemi <saied@google.com>
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2014-12-09 23:20:00 +03:00
|
|
|
/* make sure we won't clash with an inherit fd */
|
|
|
|
if (inherit_fd_resolve_clash(new_fd) < 0)
|
|
|
|
return -1;
|
2012-01-13 13:15:57 +04:00
|
|
|
|
|
|
|
if (!allow_reuse_fd) {
|
|
|
|
if (fcntl(new_fd, F_GETFD) != -1 || errno != EBADF) {
|
2015-07-02 12:47:11 +03:00
|
|
|
pr_err("fd %d already in use (called at %s:%d)\n",
|
|
|
|
new_fd, file, line);
|
|
|
|
return -1;
|
2012-01-10 16:41:00 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = dup2(old_fd, new_fd);
|
2011-09-27 18:39:56 +04:00
|
|
|
if (tmp < 0) {
|
2012-04-12 19:50:00 +04:00
|
|
|
pr_perror("Dup %d -> %d failed (called at %s:%d)",
|
|
|
|
old_fd, new_fd, file, line);
|
2011-09-23 12:00:45 +04:00
|
|
|
return tmp;
|
2011-09-27 18:39:56 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Just to have error message if failed */
|
|
|
|
close_safe(&old_fd);
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
|
2011-12-26 21:47:00 +04:00
|
|
|
return 0;
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
|
2011-12-26 21:48:00 +04:00
|
|
|
int move_img_fd(int *img_fd, int want_fd)
|
|
|
|
{
|
|
|
|
if (*img_fd == want_fd) {
|
|
|
|
int tmp;
|
|
|
|
|
|
|
|
tmp = dup(*img_fd);
|
|
|
|
if (tmp < 0) {
|
2012-01-31 15:13:05 +04:00
|
|
|
pr_perror("Can't dup file");
|
2011-12-26 21:48:00 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-01-10 16:41:00 +04:00
|
|
|
close(*img_fd);
|
|
|
|
|
2011-12-26 21:48:00 +04:00
|
|
|
*img_fd = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-09-23 20:33:32 +04:00
|
|
|
/*
|
|
|
|
* Cached opened /proc/$pid and /proc/self files.
|
|
|
|
* Used for faster access to /proc/.../foo files
|
|
|
|
* by using openat()-s
|
|
|
|
*/
|
|
|
|
|
2014-06-05 20:15:24 +04:00
|
|
|
static pid_t open_proc_pid = PROC_NONE;
|
2012-02-17 01:39:36 +04:00
|
|
|
static int open_proc_fd = -1;
|
2015-05-29 16:03:00 +03:00
|
|
|
static pid_t open_proc_self_pid;
|
2014-09-23 20:33:32 +04:00
|
|
|
static int open_proc_self_fd = -1;
|
2012-02-17 01:39:36 +04:00
|
|
|
|
2014-09-23 20:33:32 +04:00
|
|
|
static inline void set_proc_self_fd(int fd)
|
2012-02-17 01:39:36 +04:00
|
|
|
{
|
2014-09-23 20:33:32 +04:00
|
|
|
if (open_proc_self_fd >= 0)
|
|
|
|
close(open_proc_self_fd);
|
2012-02-17 01:39:36 +04:00
|
|
|
|
2014-09-23 20:33:32 +04:00
|
|
|
open_proc_self_fd = fd;
|
2015-05-29 16:03:00 +03:00
|
|
|
open_proc_self_pid = getpid();
|
2014-09-23 20:33:32 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void set_proc_pid_fd(int pid, int fd)
|
|
|
|
{
|
2012-02-17 01:39:36 +04:00
|
|
|
if (open_proc_fd >= 0)
|
2014-09-23 20:33:32 +04:00
|
|
|
close(open_proc_fd);
|
2012-02-17 01:39:36 +04:00
|
|
|
|
2014-09-23 20:33:32 +04:00
|
|
|
open_proc_pid = pid;
|
|
|
|
open_proc_fd = fd;
|
|
|
|
}
|
2012-02-17 01:39:36 +04:00
|
|
|
|
2014-09-23 20:33:32 +04:00
|
|
|
static inline int get_proc_fd(int pid)
|
|
|
|
{
|
2015-05-29 16:03:00 +03:00
|
|
|
if (pid == PROC_SELF) {
|
|
|
|
if (open_proc_self_fd != -1 && open_proc_self_pid != getpid()) {
|
|
|
|
close(open_proc_self_fd);
|
|
|
|
open_proc_self_fd = -1;
|
|
|
|
}
|
2014-09-23 20:33:32 +04:00
|
|
|
return open_proc_self_fd;
|
2015-05-29 16:03:00 +03:00
|
|
|
} else if (pid == open_proc_pid)
|
2014-09-23 20:33:32 +04:00
|
|
|
return open_proc_fd;
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int close_pid_proc(void)
|
|
|
|
{
|
|
|
|
set_proc_self_fd(-1);
|
|
|
|
set_proc_pid_fd(PROC_NONE, -1);
|
|
|
|
return 0;
|
2012-02-17 01:39:36 +04:00
|
|
|
}
|
|
|
|
|
2012-06-19 15:53:00 +04:00
|
|
|
void close_proc()
|
|
|
|
{
|
|
|
|
close_pid_proc();
|
2013-01-11 18:16:20 +04:00
|
|
|
close_service_fd(PROC_FD_OFF);
|
2012-06-19 15:53:00 +04:00
|
|
|
}
|
|
|
|
|
2012-09-07 19:16:34 +04:00
|
|
|
int set_proc_fd(int fd)
|
|
|
|
{
|
2013-08-11 19:51:02 +04:00
|
|
|
if (install_service_fd(PROC_FD_OFF, fd) < 0)
|
|
|
|
return -1;
|
|
|
|
return 0;
|
2012-09-07 19:16:34 +04:00
|
|
|
}
|
|
|
|
|
2013-08-11 20:02:33 +04:00
|
|
|
static int open_proc_sfd(char *path)
|
2012-06-19 15:53:00 +04:00
|
|
|
{
|
2013-01-11 18:16:20 +04:00
|
|
|
int fd, ret;
|
2012-06-19 15:53:00 +04:00
|
|
|
|
2014-09-23 20:33:03 +04:00
|
|
|
close_proc();
|
2012-06-19 15:53:00 +04:00
|
|
|
fd = open(path, O_DIRECTORY | O_RDONLY);
|
|
|
|
if (fd == -1) {
|
|
|
|
pr_err("Can't open %s\n", path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-01-11 18:16:20 +04:00
|
|
|
ret = install_service_fd(PROC_FD_OFF, fd);
|
2012-06-19 15:53:00 +04:00
|
|
|
close(fd);
|
2013-01-11 18:16:20 +04:00
|
|
|
if (ret < 0)
|
2012-06-19 15:53:00 +04:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-17 01:39:36 +04:00
|
|
|
inline int open_pid_proc(pid_t pid)
|
2012-01-12 23:50:45 +04:00
|
|
|
{
|
|
|
|
char path[18];
|
|
|
|
int fd;
|
2013-01-11 18:16:20 +04:00
|
|
|
int dfd;
|
2012-01-12 23:50:45 +04:00
|
|
|
|
2014-09-23 20:33:32 +04:00
|
|
|
fd = get_proc_fd(pid);
|
|
|
|
if (fd >= 0)
|
|
|
|
return fd;
|
2012-06-19 15:53:00 +04:00
|
|
|
|
2013-01-11 18:16:20 +04:00
|
|
|
dfd = get_service_fd(PROC_FD_OFF);
|
|
|
|
if (dfd < 0) {
|
2013-08-11 20:02:33 +04:00
|
|
|
if (open_proc_sfd("/proc") < 0)
|
2013-01-11 18:16:20 +04:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
dfd = get_service_fd(PROC_FD_OFF);
|
2012-06-19 15:53:00 +04:00
|
|
|
}
|
|
|
|
|
2014-06-05 20:15:24 +04:00
|
|
|
if (pid == PROC_GEN)
|
|
|
|
/*
|
|
|
|
* Don't cache it, close_pid_proc() would
|
|
|
|
* close service descriptor otherwise.
|
|
|
|
*/
|
|
|
|
return dfd;
|
|
|
|
|
|
|
|
if (pid == PROC_SELF)
|
|
|
|
snprintf(path, sizeof(path), "self");
|
|
|
|
else
|
|
|
|
snprintf(path, sizeof(path), "%d", pid);
|
|
|
|
|
2013-01-11 18:16:20 +04:00
|
|
|
fd = openat(dfd, path, O_RDONLY);
|
2014-09-23 20:33:32 +04:00
|
|
|
if (fd < 0) {
|
2012-01-31 15:13:05 +04:00
|
|
|
pr_perror("Can't open %s", path);
|
2014-12-11 22:55:11 +02:00
|
|
|
set_cr_errno(ESRCH);
|
2014-09-23 20:33:32 +04:00
|
|
|
return -1;
|
2012-02-17 01:39:36 +04:00
|
|
|
}
|
|
|
|
|
2014-09-23 20:33:32 +04:00
|
|
|
if (pid == PROC_SELF)
|
|
|
|
set_proc_self_fd(fd);
|
|
|
|
else
|
|
|
|
set_proc_pid_fd(pid, fd);
|
|
|
|
|
2012-01-12 23:50:45 +04:00
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2012-02-17 01:39:36 +04:00
|
|
|
int do_open_proc(pid_t pid, int flags, const char *fmt, ...)
|
2012-01-12 23:50:45 +04:00
|
|
|
{
|
2012-02-17 01:39:35 +04:00
|
|
|
char path[128];
|
|
|
|
va_list args;
|
2014-09-23 20:33:03 +04:00
|
|
|
int dirfd;
|
2012-02-17 01:39:36 +04:00
|
|
|
|
2014-09-23 20:33:03 +04:00
|
|
|
dirfd = open_pid_proc(pid);
|
2012-02-17 01:39:36 +04:00
|
|
|
if (dirfd < 0)
|
|
|
|
return -1;
|
2012-01-12 23:50:45 +04:00
|
|
|
|
2012-02-17 01:39:35 +04:00
|
|
|
va_start(args, fmt);
|
|
|
|
vsnprintf(path, sizeof(path), fmt, args);
|
|
|
|
va_end(args);
|
2012-01-12 23:50:45 +04:00
|
|
|
|
2012-02-17 01:39:35 +04:00
|
|
|
return openat(dirfd, path, flags);
|
2012-01-12 23:50:45 +04:00
|
|
|
}
|
2012-03-16 17:21:00 +04:00
|
|
|
|
2012-09-13 14:45:06 +04:00
|
|
|
static int service_fd_rlim_cur;
|
2013-01-11 18:16:19 +04:00
|
|
|
static int service_fd_id = 0;
|
2012-09-13 14:45:06 +04:00
|
|
|
|
|
|
|
int init_service_fd(void)
|
2012-03-16 17:21:00 +04:00
|
|
|
{
|
|
|
|
struct rlimit rlimit;
|
|
|
|
|
|
|
|
/*
|
2013-04-12 13:00:06 -07:00
|
|
|
* Service FDs are those that most likely won't
|
2012-03-16 17:21:00 +04:00
|
|
|
* conflict with any 'real-life' ones
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (getrlimit(RLIMIT_NOFILE, &rlimit)) {
|
|
|
|
pr_perror("Can't get rlimit");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-09-13 14:45:06 +04:00
|
|
|
service_fd_rlim_cur = (int)rlimit.rlim_cur;
|
|
|
|
BUG_ON(service_fd_rlim_cur < SERVICE_FD_MAX);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-11 18:16:19 +04:00
|
|
|
static int __get_service_fd(enum sfd_type type, int service_fd_id)
|
2012-09-29 11:01:53 +04:00
|
|
|
{
|
2013-01-11 18:16:19 +04:00
|
|
|
return service_fd_rlim_cur - type - SERVICE_FD_MAX * service_fd_id;
|
2012-09-29 11:01:53 +04:00
|
|
|
}
|
|
|
|
|
2013-01-11 18:16:20 +04:00
|
|
|
static DECLARE_BITMAP(sfd_map, SERVICE_FD_MAX);
|
|
|
|
|
2013-03-18 23:04:09 +04:00
|
|
|
int reserve_service_fd(enum sfd_type type)
|
|
|
|
{
|
|
|
|
int sfd = __get_service_fd(type, service_fd_id);
|
|
|
|
|
|
|
|
BUG_ON((int)type <= SERVICE_FD_MIN || (int)type >= SERVICE_FD_MAX);
|
|
|
|
|
|
|
|
set_bit(type, sfd_map);
|
|
|
|
return sfd;
|
|
|
|
}
|
|
|
|
|
2013-01-11 18:16:20 +04:00
|
|
|
int install_service_fd(enum sfd_type type, int fd)
|
|
|
|
{
|
|
|
|
int sfd = __get_service_fd(type, service_fd_id);
|
|
|
|
|
|
|
|
BUG_ON((int)type <= SERVICE_FD_MIN || (int)type >= SERVICE_FD_MAX);
|
|
|
|
|
2014-03-22 20:14:00 +04:00
|
|
|
if (dup3(fd, sfd, O_CLOEXEC) != sfd) {
|
2013-01-11 18:16:20 +04:00
|
|
|
pr_perror("Dup %d -> %d failed", fd, sfd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
set_bit(type, sfd_map);
|
|
|
|
return sfd;
|
|
|
|
}
|
|
|
|
|
2012-09-13 14:45:06 +04:00
|
|
|
int get_service_fd(enum sfd_type type)
|
|
|
|
{
|
|
|
|
BUG_ON((int)type <= SERVICE_FD_MIN || (int)type >= SERVICE_FD_MAX);
|
2013-01-11 18:16:20 +04:00
|
|
|
|
|
|
|
if (!test_bit(type, sfd_map))
|
|
|
|
return -1;
|
|
|
|
|
2013-01-11 18:16:19 +04:00
|
|
|
return __get_service_fd(type, service_fd_id);
|
|
|
|
}
|
|
|
|
|
2013-12-19 21:35:02 +04:00
|
|
|
int criu_get_image_dir(void)
|
|
|
|
{
|
|
|
|
return get_service_fd(IMG_FD_OFF);
|
|
|
|
}
|
|
|
|
|
2013-01-11 18:16:20 +04:00
|
|
|
int close_service_fd(enum sfd_type type)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = get_service_fd(type);
|
|
|
|
if (fd < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (close_safe(&fd))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
clear_bit(type, sfd_map);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-11 18:16:19 +04:00
|
|
|
int clone_service_fd(int id)
|
|
|
|
{
|
|
|
|
int ret = -1, i;
|
|
|
|
|
|
|
|
if (service_fd_id == id)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = SERVICE_FD_MIN + 1; i < SERVICE_FD_MAX; i++) {
|
|
|
|
int old = __get_service_fd(i, service_fd_id);
|
|
|
|
int new = __get_service_fd(i, id);
|
|
|
|
|
|
|
|
ret = dup2(old, new);
|
|
|
|
if (ret == -1) {
|
|
|
|
if (errno == EBADF)
|
|
|
|
continue;
|
2013-05-02 22:44:24 +04:00
|
|
|
pr_perror("Unable to clone %d->%d", old, new);
|
2013-01-11 18:16:19 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
service_fd_id = id;
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
return ret;
|
2012-09-29 11:01:53 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool is_any_service_fd(int fd)
|
|
|
|
{
|
2013-01-11 18:16:19 +04:00
|
|
|
return fd > __get_service_fd(SERVICE_FD_MAX, service_fd_id) &&
|
|
|
|
fd < __get_service_fd(SERVICE_FD_MIN, service_fd_id);
|
2012-09-13 14:45:06 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool is_service_fd(int fd, enum sfd_type type)
|
|
|
|
{
|
|
|
|
return fd == get_service_fd(type);
|
2012-03-16 17:21:00 +04:00
|
|
|
}
|
2012-04-13 17:52:35 +04:00
|
|
|
|
|
|
|
int copy_file(int fd_in, int fd_out, size_t bytes)
|
|
|
|
{
|
|
|
|
ssize_t written = 0;
|
|
|
|
size_t chunk = bytes ? bytes : 4096;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
ret = sendfile(fd_out, fd_in, NULL, chunk);
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_perror("Can't send data to ghost file");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
if (bytes && (written != bytes)) {
|
2013-01-18 11:08:33 +04:00
|
|
|
pr_err("Ghost file size mismatch %zu/%zu\n",
|
2012-04-13 17:52:35 +04:00
|
|
|
written, bytes);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
written += ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2012-05-04 13:38:00 +04:00
|
|
|
|
2013-03-15 15:03:16 +04:00
|
|
|
int read_fd_link(int lfd, char *buf, size_t size)
|
|
|
|
{
|
|
|
|
char t[32];
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
snprintf(t, sizeof(t), "/proc/self/fd/%d", lfd);
|
|
|
|
ret = readlink(t, buf, size);
|
|
|
|
if (ret < 0) {
|
2013-04-05 15:56:18 +04:00
|
|
|
pr_perror("Can't read link of fd %d", lfd);
|
2013-03-15 15:03:16 +04:00
|
|
|
return -1;
|
2013-05-07 19:01:22 +04:00
|
|
|
} else if ((size_t)ret == size) {
|
|
|
|
pr_err("Buffer for read link of fd %d is too small\n", lfd);
|
|
|
|
return -1;
|
2013-03-15 15:03:16 +04:00
|
|
|
}
|
|
|
|
buf[ret] = 0;
|
|
|
|
|
2013-05-07 19:01:24 +04:00
|
|
|
return ret;
|
2013-03-15 15:03:16 +04:00
|
|
|
}
|
|
|
|
|
2014-02-02 22:14:29 +04:00
|
|
|
int is_anon_link_type(char *link, char *type)
|
2012-05-04 14:22:18 +04:00
|
|
|
{
|
2014-02-02 22:14:29 +04:00
|
|
|
char aux[32];
|
2013-03-15 15:03:18 +04:00
|
|
|
|
2012-05-04 14:22:18 +04:00
|
|
|
snprintf(aux, sizeof(aux), "anon_inode:%s", type);
|
|
|
|
return !strcmp(link, aux);
|
|
|
|
}
|
2012-08-16 20:41:34 +04:00
|
|
|
|
|
|
|
void *shmalloc(size_t bytes)
|
|
|
|
{
|
2013-11-02 01:04:07 +04:00
|
|
|
return rst_mem_alloc(bytes, RM_SHARED);
|
2012-08-16 20:41:34 +04:00
|
|
|
}
|
2012-09-02 17:23:27 +04:00
|
|
|
|
|
|
|
/* Only last chunk can be released */
|
|
|
|
void shfree_last(void *ptr)
|
|
|
|
{
|
2013-11-02 01:04:07 +04:00
|
|
|
rst_mem_free_last(RM_SHARED);
|
2012-09-02 17:23:27 +04:00
|
|
|
}
|
2012-09-17 20:05:39 +04:00
|
|
|
|
2012-09-28 14:09:57 +04:00
|
|
|
#define DUP_SAFE(fd, out) \
|
|
|
|
({ \
|
|
|
|
int ret__; \
|
|
|
|
ret__ = dup(fd); \
|
|
|
|
if (ret__ == -1) { \
|
|
|
|
pr_perror("dup(%d) failed", fd); \
|
|
|
|
goto out; \
|
|
|
|
} \
|
|
|
|
ret__; \
|
|
|
|
})
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If "in" is negative, stdin will be closed.
|
|
|
|
* If "out" or "err" are negative, a log file descriptor will be used.
|
|
|
|
*/
|
|
|
|
int cr_system(int in, int out, int err, char *cmd, char *const argv[])
|
2015-07-20 12:34:15 +03:00
|
|
|
{
|
|
|
|
return cr_system_userns(in, out, err, cmd, argv, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int cr_system_userns(int in, int out, int err, char *cmd,
|
|
|
|
char *const argv[], int userns_pid)
|
2012-09-28 14:09:57 +04:00
|
|
|
{
|
|
|
|
sigset_t blockmask, oldmask;
|
|
|
|
int ret = -1, status;
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
sigemptyset(&blockmask);
|
|
|
|
sigaddset(&blockmask, SIGCHLD);
|
|
|
|
if (sigprocmask(SIG_BLOCK, &blockmask, &oldmask) == -1) {
|
|
|
|
pr_perror("Can not set mask of blocked signals");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pid = fork();
|
|
|
|
if (pid == -1) {
|
2013-05-02 22:44:24 +04:00
|
|
|
pr_perror("fork() failed");
|
2012-09-28 14:09:57 +04:00
|
|
|
goto out;
|
|
|
|
} else if (pid == 0) {
|
2015-07-20 12:34:15 +03:00
|
|
|
if (userns_pid > 0) {
|
|
|
|
if (switch_ns(userns_pid, &user_ns_desc, NULL))
|
|
|
|
goto out_chld;
|
|
|
|
if (setuid(0) || setgid(0)) {
|
|
|
|
pr_perror("Unable to set uid or gid");
|
|
|
|
goto out_chld;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-28 14:09:57 +04:00
|
|
|
if (out < 0)
|
|
|
|
out = log_get_fd();
|
|
|
|
if (err < 0)
|
|
|
|
err = log_get_fd();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* out, err, in should be a separate fds,
|
|
|
|
* because reopen_fd_as() closes an old fd
|
|
|
|
*/
|
|
|
|
if (err == out || err == in)
|
|
|
|
err = DUP_SAFE(err, out_chld);
|
|
|
|
|
|
|
|
if (out == in)
|
|
|
|
out = DUP_SAFE(out, out_chld);
|
|
|
|
|
2013-01-15 18:52:59 +04:00
|
|
|
if (move_img_fd(&out, STDIN_FILENO) ||
|
|
|
|
move_img_fd(&err, STDIN_FILENO))
|
|
|
|
goto out_chld;
|
|
|
|
|
2012-09-28 14:09:57 +04:00
|
|
|
if (in < 0) {
|
|
|
|
close(STDIN_FILENO);
|
|
|
|
} else {
|
|
|
|
if (reopen_fd_as_nocheck(STDIN_FILENO, in))
|
|
|
|
goto out_chld;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (move_img_fd(&err, STDOUT_FILENO))
|
|
|
|
goto out_chld;
|
|
|
|
|
|
|
|
if (reopen_fd_as_nocheck(STDOUT_FILENO, out))
|
|
|
|
goto out_chld;
|
|
|
|
|
|
|
|
if (reopen_fd_as_nocheck(STDERR_FILENO, err))
|
|
|
|
goto out_chld;
|
|
|
|
|
|
|
|
execvp(cmd, argv);
|
|
|
|
|
|
|
|
pr_perror("exec failed");
|
|
|
|
out_chld:
|
|
|
|
_exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
ret = waitpid(pid, &status, 0);
|
|
|
|
if (ret == -1) {
|
|
|
|
pr_perror("waitpid() failed");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (WIFEXITED(status)) {
|
|
|
|
if (WEXITSTATUS(status))
|
|
|
|
pr_err("exited, status=%d\n", WEXITSTATUS(status));
|
|
|
|
break;
|
|
|
|
} else if (WIFSIGNALED(status)) {
|
|
|
|
pr_err("killed by signal %d\n", WTERMSIG(status));
|
|
|
|
break;
|
|
|
|
} else if (WIFSTOPPED(status)) {
|
|
|
|
pr_err("stopped by signal %d\n", WSTOPSIG(status));
|
|
|
|
} else if (WIFCONTINUED(status)) {
|
|
|
|
pr_err("continued\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = status ? -1 : 0;
|
|
|
|
out:
|
|
|
|
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1) {
|
|
|
|
pr_perror("Can not unset mask of blocked signals");
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2013-12-13 00:02:00 +04:00
|
|
|
|
2014-09-04 20:39:07 +04:00
|
|
|
int cr_daemon(int nochdir, int noclose, int *keep_fd, int close_fd)
|
2014-01-28 22:35:59 +04:00
|
|
|
{
|
|
|
|
int pid;
|
|
|
|
|
|
|
|
pid = fork();
|
|
|
|
if (pid < 0) {
|
|
|
|
pr_perror("Can't fork");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pid > 0)
|
|
|
|
return pid;
|
|
|
|
|
|
|
|
setsid();
|
|
|
|
if (!nochdir)
|
2014-02-04 13:40:00 +04:00
|
|
|
if (chdir("/") == -1)
|
|
|
|
pr_perror("Can't change directory");
|
2014-01-28 22:35:59 +04:00
|
|
|
if (!noclose) {
|
|
|
|
int fd;
|
|
|
|
|
2014-09-04 20:39:07 +04:00
|
|
|
if (close_fd != -1)
|
|
|
|
close(close_fd);
|
|
|
|
|
|
|
|
if (*keep_fd != -1)
|
|
|
|
*keep_fd = dup2(*keep_fd, 3);
|
|
|
|
|
2014-01-28 22:35:59 +04:00
|
|
|
fd = open("/dev/null", O_RDWR);
|
2014-04-09 14:46:13 +04:00
|
|
|
if (fd < 0) {
|
|
|
|
pr_perror("Can't open /dev/null");
|
|
|
|
return -1;
|
|
|
|
}
|
2014-01-28 22:35:59 +04:00
|
|
|
dup2(fd, 0);
|
|
|
|
dup2(fd, 1);
|
|
|
|
dup2(fd, 2);
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-13 00:02:00 +04:00
|
|
|
int is_root_user()
|
|
|
|
{
|
|
|
|
if (geteuid() != 0) {
|
|
|
|
pr_err("You need to be root to run this command\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
2014-01-30 23:34:53 +04:00
|
|
|
|
|
|
|
int vaddr_to_pfn(unsigned long vaddr, u64 *pfn)
|
|
|
|
{
|
|
|
|
int fd, ret = -1;
|
|
|
|
off_t off;
|
|
|
|
|
|
|
|
fd = open_proc(getpid(), "pagemap");
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
2015-04-20 18:09:00 +03:00
|
|
|
off = (vaddr / page_size()) * sizeof(u64);
|
2014-01-30 23:34:53 +04:00
|
|
|
if (lseek(fd, off, SEEK_SET) != off) {
|
2014-08-20 14:52:00 +04:00
|
|
|
pr_perror("Failed to seek address %lx", vaddr);
|
2014-01-30 23:34:53 +04:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = read(fd, pfn, sizeof(*pfn));
|
|
|
|
if (ret != sizeof(*pfn)) {
|
|
|
|
pr_perror("Can't read pme for pid %d", getpid());
|
|
|
|
ret = -1;
|
2014-01-31 15:11:28 +04:00
|
|
|
} else {
|
|
|
|
*pfn &= PME_PFRAME_MASK;
|
2014-01-30 23:34:53 +04:00
|
|
|
ret = 0;
|
2014-01-31 15:11:28 +04:00
|
|
|
}
|
2014-01-30 23:34:53 +04:00
|
|
|
out:
|
|
|
|
close(fd);
|
|
|
|
return ret;
|
|
|
|
}
|
2014-01-31 20:57:11 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Note since VMA_AREA_NONE = 0 we can skip assignment
|
|
|
|
* here and simply rely on xzalloc
|
|
|
|
*/
|
|
|
|
struct vma_area *alloc_vma_area(void)
|
|
|
|
{
|
|
|
|
struct vma_area *p;
|
|
|
|
|
2014-02-04 00:08:16 +04:00
|
|
|
p = xzalloc(sizeof(*p) + sizeof(VmaEntry));
|
2014-01-31 20:57:11 +04:00
|
|
|
if (p) {
|
2014-02-04 00:08:16 +04:00
|
|
|
p->e = (VmaEntry *)(p + 1);
|
|
|
|
vma_entry__init(p->e);
|
2014-01-31 20:57:11 +04:00
|
|
|
p->vm_file_fd = -1;
|
2014-02-04 00:08:16 +04:00
|
|
|
p->e->fd = -1;
|
2014-01-31 20:57:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
2014-07-10 17:00:28 +04:00
|
|
|
|
2014-10-06 09:03:29 -05:00
|
|
|
int mkdirpat(int fd, const char *path)
|
2014-07-10 17:00:28 +04:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
char made_path[PATH_MAX], *pos;
|
|
|
|
|
|
|
|
if (strlen(path) >= PATH_MAX) {
|
|
|
|
pr_err("path %s is longer than PATH_MAX", path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(made_path, path);
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
if (made_path[0] == '/')
|
|
|
|
i++;
|
|
|
|
|
|
|
|
for (; i < strlen(made_path); i++) {
|
|
|
|
pos = strchr(made_path + i, '/');
|
|
|
|
if (pos)
|
|
|
|
*pos = '\0';
|
2014-10-06 09:03:29 -05:00
|
|
|
if (mkdirat(fd, made_path, 0755) < 0 && errno != EEXIST) {
|
2014-09-17 11:59:00 +04:00
|
|
|
pr_perror("couldn't mkdirpat directory %s", made_path);
|
2014-07-10 17:00:28 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (pos) {
|
|
|
|
*pos = '/';
|
|
|
|
i = pos - made_path;
|
|
|
|
} else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_path_prefix(const char *path, const char *prefix)
|
|
|
|
{
|
|
|
|
if (strstartswith(path, prefix)) {
|
|
|
|
size_t len = strlen(prefix);
|
|
|
|
switch (path[len]) {
|
|
|
|
case '\0':
|
|
|
|
case '/':
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
FILE *fopenat(int dirfd, char *path, char *cflags)
|
|
|
|
{
|
|
|
|
int tmp, flags = 0;
|
|
|
|
char *iter;
|
|
|
|
|
|
|
|
for (iter = cflags; *iter; iter++) {
|
|
|
|
switch (*iter) {
|
|
|
|
case 'r':
|
|
|
|
flags |= O_RDONLY;
|
|
|
|
break;
|
|
|
|
case 'a':
|
|
|
|
flags |= O_APPEND;
|
|
|
|
break;
|
|
|
|
case 'w':
|
|
|
|
flags |= O_WRONLY | O_CREAT;
|
|
|
|
break;
|
|
|
|
case '+':
|
|
|
|
flags = O_RDWR | O_CREAT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = openat(dirfd, path, flags, S_IRUSR | S_IWUSR);
|
|
|
|
if (tmp < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return fdopen(tmp, cflags);
|
|
|
|
}
|
2014-07-11 20:11:00 +04:00
|
|
|
|
|
|
|
void split(char *str, char token, char ***out, int *n)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
char *cur;
|
|
|
|
|
|
|
|
*n = 0;
|
|
|
|
for (cur = str; cur != NULL; cur = strchr(cur, token)) {
|
|
|
|
(*n)++;
|
|
|
|
cur++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
*out = xmalloc((*n) * sizeof(char *));
|
|
|
|
if (!*out) {
|
|
|
|
*n = -1;
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
cur = str;
|
|
|
|
i = 0;
|
|
|
|
do {
|
|
|
|
char *prev = cur;
|
|
|
|
cur = strchr(cur, token);
|
|
|
|
|
|
|
|
if (cur)
|
|
|
|
*cur = '\0';
|
|
|
|
(*out)[i] = xstrdup(prev);
|
|
|
|
if (cur) {
|
|
|
|
*cur = token;
|
|
|
|
cur++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(*out)[i]) {
|
|
|
|
int j;
|
|
|
|
for (j = 0; j < i; j++)
|
|
|
|
xfree((*out)[j]);
|
2014-08-05 12:59:12 +04:00
|
|
|
xfree(*out);
|
2014-07-11 20:11:00 +04:00
|
|
|
*out = NULL;
|
|
|
|
*n = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
i++;
|
|
|
|
} while(cur);
|
|
|
|
}
|
2014-09-02 00:25:13 +04:00
|
|
|
|
|
|
|
int fd_has_data(int lfd)
|
|
|
|
{
|
|
|
|
struct pollfd pfd = {lfd, POLLIN, 0};
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = poll(&pfd, 1, 0);
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_perror("poll() failed");
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2015-11-10 11:43:00 +03:00
|
|
|
|
|
|
|
void tcp_cork(int sk, bool on)
|
|
|
|
{
|
|
|
|
int val = on ? 1 : 0;
|
|
|
|
setsockopt(sk, SOL_TCP, TCP_CORK, &val, sizeof(val));
|
|
|
|
}
|
|
|
|
|
|
|
|
void tcp_nodelay(int sk, bool on)
|
|
|
|
{
|
|
|
|
int val = on ? 1 : 0;
|
|
|
|
setsockopt(sk, SOL_TCP, TCP_NODELAY, &val, sizeof(val));
|
|
|
|
}
|