/*
* Copyright (c) 2001-2020 Mellanox Technologies, Ltd. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#ifdef HAVE_SYS_INOTIFY_H
#include <sys/inotify.h>
#endif
#ifdef HAVE_SYS_FANOTIFY_H
#include <sys/fanotify.h>
#endif
#include "hash.h"
#include "tc.h"
#include "daemon.h"
#ifndef KERNEL_O_LARGEFILE
#if defined(__aarch64__) || defined(__powerpc__)
/* Check architecture: if we are running on ARM,
* omit KERNEL_O_LARGEFILE from fanotify_init invocation because
* KERNEL_O_LARGEFILE breaks program on armv running at least kernel 4.4+
*/
#define KERNEL_O_LARGEFILE O_LARGEFILE
#else
/* work around kernels which do not have this fix yet:
* http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=1e2ee49f7
* O_LARGEFILE is usually 0, so hardcode it here
*/
#define KERNEL_O_LARGEFILE 00100000
#endif
#endif
struct rst_info {
struct sockaddr_in local_addr;
struct sockaddr_in remote_addr;
uint32_t seqno;
};
#pragma pack(push, 1)
struct tcp_msg {
struct iphdr ip;
struct tcphdr tcp;
uint8_t data[8192];
};
#pragma pack( pop )
#pragma pack(push, 1)
struct pseudo_header {
uint32_t source_address;
uint32_t dest_address;
uint8_t placeholder;
uint8_t protocol;
uint16_t tcp_length;
struct tcphdr tcp;
} pseudo_header;
#pragma pack( pop )
int open_notify(void);
void close_notify(void);
int proc_notify(void);
extern int add_flow(struct store_pid *pid_value, struct store_flow *value);
extern int del_flow(struct store_pid *pid_value, struct store_flow *value);
static int setup_notify(void);
static int create_raw_socket(void);
static int clean_process(pid_t pid);
static int check_process(pid_t pid);
static unsigned short calc_csum(unsigned short *ptr, int nbytes);
static int get_seqno(struct rst_info *rst);
static int send_rst(struct rst_info *rst);
#ifdef HAVE_SYS_INOTIFY_H
static int open_inotify(void);
static int proc_inotify(void *buf, int size);
#endif
#ifdef HAVE_SYS_FANOTIFY_H
static int open_fanotify(void);
static int proc_fanotify(void *buf, int size);
#endif
#if !defined(HAVE_SYS_FANOTIFY_H) && !defined(HAVE_SYS_INOTIFY_H)
#error neither inotify nor fanotify is supported
#endif
static int (*do_open_notify)(void) = NULL;
static int (*do_proc_notify)(void*, int) = NULL;
int open_notify(void)
{
int rc = 0;
rc = setup_notify();
if (rc < 0) {
goto err;
}
rc = create_raw_socket();
if (rc < 0) {
goto err;
}
log_debug("setting raw socket ...\n");
rc = do_open_notify();
if (rc < 0) {
goto err;
}
err:
return rc;
}
void close_notify(void)
{
log_debug("closing raw socket ...\n");
if (daemon_cfg.notify_fd > 0) {
close(daemon_cfg.notify_fd);
}
if (daemon_cfg.raw_fd > 0) {
close(daemon_cfg.raw_fd);
}
}
int proc_notify(void)
{
int rc = 0;
int len = 0;
char msg_recv[4096];
memset((void *)&msg_recv, 0, sizeof(msg_recv));
again:
/* coverity[tainted_string_argument] */
len = read(daemon_cfg.notify_fd, msg_recv, sizeof(msg_recv));
if (len <= 0) {
if (errno == EINTR) {
goto again;
}
rc = -errno;
log_error("Failed read events() errno %d (%s)\n", errno,
strerror(errno));
goto err;
}
rc = do_proc_notify((void *)msg_recv, len);
if (rc < 0) {
goto err;
}
err:
return rc;
}
static int setup_notify(void)
{
int fd = -1;
/* Set method for processing
* fanotify has the highest priority because it has better
* performance
*/
errno = 0;
#if defined(HAVE_SYS_FANOTIFY_H)
fd = fanotify_init(0, KERNEL_O_LARGEFILE);
if (fd >= 0) {
do_open_notify = open_fanotify;
do_proc_notify = proc_fanotify;
close(fd);
return 0;
} else {
log_debug("fanotify_init() errno %d (%s)\n", errno,
(ENOSYS == errno ? "missing support for fanotify (check CONFIG_FANOTIFY=y)\n" : strerror(errno)));
}
#endif
#if defined(HAVE_SYS_INOTIFY_H)
fd = inotify_init();
if (fd >= 0) {
do_open_notify = open_inotify;
do_proc_notify = proc_inotify;
close(fd);
return 0;
} else {
log_debug("inotify_init() errno %d (%s)\n", errno,
(ENOSYS == errno ? "missing support for inotify (check CONFIG_INOTIFY_USER=y)\n" : strerror(errno)));
}
#endif
log_error("Failed notify way selection, check kernel configuration errno %d (%s)\n", errno,
strerror(errno));
return -ENOSYS;
}
static int create_raw_socket(void)
{
int rc = 0;
int optval = 1;
/* Create RAW socket to use for sending RST to peers */
daemon_cfg.raw_fd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
if (daemon_cfg.raw_fd < 0) {
/* socket creation failed, may be because of non-root privileges */
log_error("Failed to call socket() errno %d (%s)\n", errno,
strerror(errno));
rc = -errno;
goto err;
}
optval = 1;
rc = setsockopt(daemon_cfg.raw_fd, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(optval));
if (rc < 0) {
log_error("Failed to call setsockopt() errno %d (%s)\n", errno,
strerror(errno));
rc = -errno;
goto err;
}
err:
return rc;
}
static int clean_process(pid_t pid)
{
int rc = -ESRCH;
int wait = 0;
wait = 100;
do {
/* Wait for parent process completion */
if (!check_process(pid)) {
struct store_pid *pid_value = NULL;
log_debug("[%d] detect abnormal termination\n", pid);
pid_value = hash_get(daemon_cfg.ht, pid);
if (pid_value) {
struct rst_info rst;
struct store_fid *fid_value = NULL;
struct store_flow *flow_value = NULL;
struct list_head *cur_entry = NULL;
struct list_head *tmp_entry = NULL;
int i, j;
/* Cleanup flow store */
j = 0;
list_for_each_safe(cur_entry, tmp_entry, &pid_value->flow_list) {
flow_value = list_entry(cur_entry, struct store_flow, item);
j++;
log_debug("[%d] #%d found handle: 0x%08X type: %d if_id: %d tap_id: %d\n",
pid_value->pid, j,
flow_value->handle, flow_value->type, flow_value->if_id, flow_value->tap_id);
list_del_init(&flow_value->item);
del_flow(pid_value, flow_value);
free(flow_value);
}
/* Cleanup fid store */
j = 0;
for (i = 0; (i < hash_size(pid_value->ht)) &&
(j < hash_count(pid_value->ht)); i++) {
fid_value = hash_enum(pid_value->ht, i);
if (NULL == fid_value) {
continue;
}
j++;
log_debug("[%d] #%d found fid: %d type: %d state: %d\n",
pid_value->pid, j,
fid_value->fid, fid_value->type, fid_value->state);
if (STATE_ESTABLISHED != fid_value->state) {
log_debug("[%d] #%d skip fid: %d\n",
pid_value->pid, j, fid_value->fid);
continue;
}
log_debug("[%d] #%d process fid: %d\n",
pid_value->pid, j, fid_value->fid);
/* Notification is based on sending RST packet to all peers
* and looks as spoofing attacks that uses a technique
* called Sequence Number Prediction.
* This actively sends spoofed SYN packets and learns the
* SeqNo number from the answer. It then sends RST packets.
* P1 - terminated process
* P2 - peer of terminated process
* H - host (kernel) of terminated process
* 1. [P1] sends SYN to [P2] with source IP of [H].
* 2. [P2] replies to [H] by SYN/ACK.
* 3. There is a possibility of:
* 3.1 [H] should reply to an unknown SYN/ACK by RST.
* 3.2 [P1] sends RST using SeqNo from SYN/ACK.
*/
rst.local_addr.sin_family = AF_INET;
rst.local_addr.sin_port = fid_value->src_port;
rst.local_addr.sin_addr.s_addr = fid_value->src_ip;
rst.remote_addr.sin_family = AF_INET;
rst.remote_addr.sin_port = fid_value->dst_port;
rst.remote_addr.sin_addr.s_addr = fid_value->dst_ip;
rst.seqno = 1;
if (0 == get_seqno(&rst) && daemon_cfg.opt.force_rst) {
send_rst(&rst);
}
}
hash_del(daemon_cfg.ht, pid);
log_debug("[%d] remove from the storage\n", pid);
/* Set OK */
rc = 0;
}
break;
}
usleep(10000);
} while (wait--);
return rc;
}
static int check_process(pid_t pid)
{
char process_file[PATH_MAX];
int rc = 0;
rc = snprintf(process_file, sizeof(process_file), "/proc/%d/stat", pid);
if ((0 < rc) && (rc < (int)sizeof(process_file))) {
FILE* fd = fopen(process_file, "r");
if (fd) {
int pid_v = 0;
char name_v[32];
char stat_v = 0;
rc = fscanf(fd, "%d %30s %c", &pid_v, name_v, &stat_v);
fclose(fd);
if (rc == 3 && stat_v != 'Z') {
return 1;
}
}
}
return 0;
}
static unsigned short calc_csum(unsigned short *ptr, int nbytes)
{
register long sum;
unsigned short oddbyte;
register short answer;
sum = 0;
while (nbytes > 1) {
sum += *ptr++;
nbytes -= 2;
}
if (nbytes == 1) {
oddbyte = 0;
*((u_char*) &oddbyte) = *(u_char*) ptr;
sum += oddbyte;
}
sum = (sum >> 16) + (sum & 0xffff);
sum = sum + (sum >> 16);
answer = (short) ~sum;
return (answer);
}
static int get_seqno(struct rst_info *rst)
{
int rc = 0;
struct tcp_msg msg;
struct pseudo_header pheader;
int attempt = 3; /* Do maximum number of attempts */
struct timeval t_end = TIMEVAL_INITIALIZER;
struct timeval t_now = TIMEVAL_INITIALIZER;
struct timeval t_wait = TIMEVAL_INITIALIZER; /* Defines wait interval, holds difference between t_now and t_end */
/* zero out the packet */
memset(&msg, 0, sizeof(msg));
/* IP Header */
msg.ip.version = 4;
msg.ip.ihl = 5;
msg.ip.tos = 0;
msg.ip.tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr);
msg.ip.id = 0;
msg.ip.frag_off = htons(0x4000); /* Flag: "Don't Fragment" */
msg.ip.ttl = 0x40;
msg.ip.protocol = IPPROTO_TCP;
msg.ip.check = 0;
msg.ip.saddr = rst->local_addr.sin_addr.s_addr;
msg.ip.daddr = rst->remote_addr.sin_addr.s_addr;
/* Calculate IP header checksum */
msg.ip.check = calc_csum((unsigned short *)&msg.ip, sizeof(msg.ip));
/* TCP Header */
msg.tcp.source = rst->local_addr.sin_port;
msg.tcp.dest = rst->remote_addr.sin_port;
msg.tcp.seq = rst->seqno;
msg.tcp.ack_seq = 0;
msg.tcp.doff = 5;
msg.tcp.fin = 0;
msg.tcp.syn = 1;
msg.tcp.rst = 0;
msg.tcp.psh = 0;
msg.tcp.ack = 0;
msg.tcp.urg = 0;
msg.tcp.window = 0;
msg.tcp.check = 0;
msg.tcp.urg_ptr = 0;
/* Calculate TCP header checksum */
pheader.source_address = msg.ip.saddr;
pheader.dest_address = msg.ip.daddr;
pheader.placeholder = 0;
pheader.protocol = IPPROTO_TCP;
pheader.tcp_length = htons(sizeof(struct tcphdr));
bcopy((const void *)&msg.tcp, (void *)&pheader.tcp, sizeof(struct tcphdr));
msg.tcp.check = calc_csum((unsigned short *)&pheader, sizeof(pheader));
do {
/* Send invalid SYN packet */
rc = sys_sendto(daemon_cfg.raw_fd, &msg, sizeof(msg) - sizeof(msg.data), 0,
(struct sockaddr *) &rst->remote_addr, sizeof(rst->remote_addr));
if (rc < 0) {
goto out;
}
log_debug("send SYN to: %s\n", sys_addr2str(&rst->remote_addr));
t_wait.tv_sec = daemon_cfg.opt.retry_interval / 1000;
t_wait.tv_usec = (daemon_cfg.opt.retry_interval % 1000) * 1000;
gettimeofday(&t_end, NULL);
/* Account for wrapping of tv_usec, use libvma utils macro for timeradd() */
tv_add(&t_end, &t_wait, &t_end);
do {
struct tcp_msg msg_recv;
struct sockaddr_in gotaddr;
socklen_t addrlen = sizeof(gotaddr);
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(daemon_cfg.raw_fd, &readfds);
/* Use t_difference to determine timeout for select so we don't wait longer than t_wait */
rc = select(daemon_cfg.raw_fd + 1, &readfds, NULL, NULL, &t_wait);
gettimeofday(&t_now, NULL);
/**
* Determine and save difference between t_now and t_end for select on next iteration.
*/
tv_sub(&t_end, &t_now, &t_wait);
if (rc == 0) {
continue;
}
memcpy(&gotaddr, &rst->remote_addr, addrlen);
memset(&msg_recv, 0, sizeof(msg_recv));
rc = recvfrom(daemon_cfg.raw_fd, &msg_recv, sizeof(msg_recv), 0, (struct sockaddr *)&gotaddr, &addrlen);
if (rc < 0) {
goto out;
}
if (msg_recv.ip.version == 4 &&
msg_recv.ip.ihl == 5 &&
msg_recv.ip.protocol == IPPROTO_TCP &&
msg_recv.ip.saddr == msg.ip.daddr &&
msg_recv.ip.daddr == msg.ip.saddr &&
msg_recv.tcp.source == msg.tcp.dest &&
msg_recv.tcp.dest == msg.tcp.source &&
msg_recv.tcp.ack == 1) {
rst->seqno = msg_recv.tcp.ack_seq;
log_debug("recv SYN|ACK from: %s with SegNo: %d\n",
sys_addr2str(&gotaddr), ntohl(rst->seqno));
return 0;
}
} while (tv_cmp(&t_now, &t_end, <));
} while (--attempt);
out:
return -EAGAIN;
}
static int send_rst(struct rst_info *rst) {
int rc = 0;
struct tcp_msg msg;
struct pseudo_header pheader;
/* zero out the packet */
memset(&msg, 0, sizeof(msg));
/* IP Header */
msg.ip.version = 4;
msg.ip.ihl = 5;
msg.ip.tos = 0;
msg.ip.tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr);
msg.ip.id = 0;
msg.ip.frag_off = htons(0x4000); /* Flag: "Don't Fragment" */
msg.ip.ttl = 0x40;
msg.ip.protocol = IPPROTO_TCP;
msg.ip.check = 0;
msg.ip.saddr = rst->local_addr.sin_addr.s_addr;
msg.ip.daddr = rst->remote_addr.sin_addr.s_addr;
/* Calculate IP header checksum */
msg.ip.check = calc_csum((unsigned short *)&msg.ip, sizeof(msg.ip));
/* TCP Header */
msg.tcp.source = rst->local_addr.sin_port;
msg.tcp.dest = rst->remote_addr.sin_port;
msg.tcp.seq = rst->seqno;
msg.tcp.ack_seq = 0;
msg.tcp.doff = 5;
msg.tcp.fin = 0;
msg.tcp.syn = 0;
msg.tcp.rst = 1;
msg.tcp.psh = 0;
msg.tcp.ack = 0;
msg.tcp.urg = 0;
msg.tcp.window = 0;
msg.tcp.check = 0;
msg.tcp.urg_ptr = 0;
/* Calculate TCP header checksum */
pheader.source_address = msg.ip.saddr;
pheader.dest_address = msg.ip.daddr;
pheader.placeholder = 0;
pheader.protocol = IPPROTO_TCP;
pheader.tcp_length = htons(sizeof(struct tcphdr));
bcopy((const void *)&msg.tcp, (void *)&pheader.tcp, sizeof(struct tcphdr));
msg.tcp.check = calc_csum((unsigned short *)&pheader, sizeof(pheader));
rc = sys_sendto(daemon_cfg.raw_fd, &msg, sizeof(msg) - sizeof(msg.data), 0,
(struct sockaddr *) &rst->remote_addr, sizeof(rst->remote_addr));
if (rc < 0) {
goto out;
}
log_debug("send RST to: %s\n", sys_addr2str(&rst->remote_addr));
rc = 0;
out:
return rc;
}
#ifdef HAVE_SYS_FANOTIFY_H
static int open_fanotify(void)
{
int rc = 0;
log_debug("selected fanotify ...\n");
if ((daemon_cfg.notify_fd = fanotify_init(0, KERNEL_O_LARGEFILE)) < 0) {
log_error("Cannot initialize fanotify_init() errno %d (%s)\n", errno,
strerror(errno));
rc = -errno;
goto err;
}
rc = fanotify_mark(daemon_cfg.notify_fd, FAN_MARK_ADD,
FAN_CLOSE | FAN_EVENT_ON_CHILD, AT_FDCWD,
daemon_cfg.notify_dir);
if (rc < 0) {
rc = -errno;
log_error("Failed to add watch for directory %s errno %d (%s)\n",
daemon_cfg.notify_dir, errno, strerror(errno));
goto err;
}
err:
return rc;
}
static int proc_fanotify(void *buffer, int nbyte)
{
int rc = 0;
int len = nbyte;
struct fanotify_event_metadata *data = (struct fanotify_event_metadata *)buffer;
while (FAN_EVENT_OK(data, len)) {
/* Check that run-time and compile-time structures match */
if (data->vers != FANOTIFY_METADATA_VERSION) {
rc = -EPROTO;
log_error("Mismatch of fanotify metadata version\n");
goto err;
}
/* Current check is based on monitoring special pid file events
* This file is created on library startup
* Event about closing this file should come if process exits
* either after work completion or as result of unexpected termination
*/
if ((data->mask & FAN_CLOSE_WRITE || data->mask & FAN_CLOSE_NOWRITE) &&
hash_get(daemon_cfg.ht, data->pid)) {
char buf[PATH_MAX];
char pathname[PATH_MAX];
memset(buf, 0, sizeof(buf));
memset(pathname, 0, sizeof(pathname));
rc = snprintf(buf, sizeof(buf) - 1, "/proc/self/fd/%d", data->fd);
if ((rc < 0 ) || (rc == (sizeof(buf) - 1) )) {
rc = -ENOMEM;
log_error("Cannot read process name errno %d (%s)\n", errno,
strerror(errno));
goto err;
}
rc = readlink(buf, pathname, sizeof(pathname) - 1);
if (rc < 0) {
rc = -ENOMEM;
log_error("Cannot read process name errno %d (%s)\n", errno,
strerror(errno));
goto err;
}
log_debug("getting event ([0x%x] pid: %d fd: %d name: %s)\n",
data->mask, data->pid, data->fd, pathname);
rc = snprintf(buf, sizeof(buf) - 1, "%s/%s.%d.pid",
daemon_cfg.notify_dir, VMA_AGENT_BASE_NAME, data->pid);
if ((rc < 0 ) || (rc == (sizeof(buf) - 1) )) {
rc = -ENOMEM;
log_error("failed allocate pid file errno %d (%s)\n", errno,
strerror(errno));
goto err;
}
/* Process event related pid file only */
rc = 0;
if (!strncmp(buf, pathname, strlen(buf))) {
log_debug("[%d] check the event\n", data->pid);
/* Check if termination is unexpected and send RST to peers
* Return status should be 0 in case we send RST and
* nonzero in case we decide that processes exited accurately
* or some internal error happens during RST send
*/
rc = clean_process(data->pid);
if (0 == rc) {
/* Cleanup unexpected termination */
log_debug("[%d] cleanup after unexpected termination\n", data->pid);
/* To suppress TOCTOU (time-of-check, time-of-use race condition) */
strcpy(pathname, buf);
unlink(pathname);
if (snprintf(pathname, sizeof(pathname) - 1, "%s/%s.%d.sock",
daemon_cfg.notify_dir, VMA_AGENT_BASE_NAME, data->pid) > 0) {
unlink(pathname);
}
} else if (-ESRCH == rc) {
/* No need in peer notification */
log_debug("[%d] no need in peer notification\n", data->pid);
rc = 0;
}
} else {
log_debug("[%d] skip the event\n", data->pid);
}
}
/* Close the file descriptor of the event */
close(data->fd);
data = FAN_EVENT_NEXT(data, len);
}
err:
return rc;
}
#endif
#ifdef HAVE_SYS_INOTIFY_H
static int open_inotify(void)
{
int rc = 0;
log_debug("selected inotify ...\n");
if ((daemon_cfg.notify_fd = inotify_init()) < 0) {
log_error("Cannot initialize inotify_init() errno %d (%s)\n", errno,
strerror(errno));
rc = -errno;
goto err;
}
rc = inotify_add_watch(daemon_cfg.notify_fd,
daemon_cfg.notify_dir,
IN_CLOSE_WRITE | IN_CLOSE_NOWRITE | IN_DELETE);
if (rc < 0) {
rc = -errno;
log_error("Failed to add watch for directory %s errno %d (%s)\n",
daemon_cfg.notify_dir, errno, strerror(errno));
goto err;
}
err:
return rc;
}
static int proc_inotify(void *buffer, int nbyte)
{
int rc = 0;
struct inotify_event *data = (struct inotify_event *)buffer;
while ((uintptr_t)data < ((uintptr_t)buffer + nbyte)) {
pid_t pid;
/* Monitor only events from files */
if ((data->len > 0) &&
!(data->mask & IN_ISDIR ) &&
(1 == sscanf(data->name, VMA_AGENT_BASE_NAME ".%d.pid", &pid)) &&
hash_get(daemon_cfg.ht, pid)) {
char buf[PATH_MAX];
char pathname[PATH_MAX];
memset(buf, 0, sizeof(buf));
memset(pathname, 0, sizeof(pathname));
rc = snprintf(pathname, sizeof(pathname) - 1, "%s/%s",
daemon_cfg.notify_dir, data->name);
if ((rc < 0 ) || (rc == (sizeof(pathname) - 1) )) {
rc = -ENOMEM;
log_error("failed allocate pid file errno %d (%s)\n", errno,
strerror(errno));
goto err;
}
log_debug("getting event ([0x%x] pid: %d name: %s)\n",
data->mask, pid, pathname);
rc = snprintf(buf, sizeof(buf) - 1, "%s/%s.%d.pid",
daemon_cfg.notify_dir, VMA_AGENT_BASE_NAME, pid);
if ((rc < 0 ) || (rc == (sizeof(buf) - 1) )) {
rc = -ENOMEM;
log_error("failed allocate pid file errno %d (%s)\n", errno,
strerror(errno));
goto err;
}
/* Process event related pid file only */
rc = 0;
if (!strncmp(buf, pathname, strlen(buf))) {
log_debug("[%d] check the event\n", pid);
/* Check if termination is unexpected and send RST to peers
* Return status should be 0 in case we send RST and
* nonzero in case we decide that processes exited accurately
* or some internal error happens during RST send
*/
rc = clean_process(pid);
if (0 == rc) {
/* Cleanup unexpected termination */
log_debug("[%d] cleanup after unexpected termination\n", pid);
unlink(buf);
if (snprintf(buf, sizeof(buf) - 1, "%s/%s.%d.sock",
daemon_cfg.notify_dir, VMA_AGENT_BASE_NAME, pid) > 0) {
unlink(buf);
}
} else if (-ESRCH == rc) {
/* No need in peer notification */
log_debug("[%d] no need in peer notification\n", pid);
rc = 0;
}
} else {
log_debug("[%d] skip the event\n", pid);
}
}
/* Move to the next event */
data = (struct inotify_event *)((uintptr_t)data + sizeof(*data) + data->len);
}
err:
return rc;
}
#endif