-
Notifications
You must be signed in to change notification settings - Fork 1
/
wakeevent.c
148 lines (131 loc) · 3.68 KB
/
wakeevent.c
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
141
142
143
144
145
146
147
148
/* Register an fd which produces wake events with
* eventlib.
* Whenever the fd is readable, we block suspend,
* call the handler, then allow suspend.
* Meanwhile we open a socket to the event daemon passing
* it the same fd.
* At a lower priority, when we read 'S' from the daemon we reply
* with 'R'.
*
* Copyright (C) 2011 Neil Brown <neilb@suse.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <event.h>
#include <fcntl.h>
#include <errno.h>
#include "libsus.h"
struct han {
struct event ev;
struct event sev;
int sock;
int disable;
void (*fn)(int,short,void*);
void *data;
};
static void wakeup_call(int fd, short ev, void *data)
{
/* A (potential) wakeup event can be read from this fd.
* We won't go to sleep because we haven't replied to
* 'S' yet as that is handle with a lower priority.
*/
struct han *han = data;
han->fn(fd, ev, han->data);
}
static void wakeup_sock(int fd, short ev, void *data)
{
char buf;
struct han *han = data;
int n = read(fd, &buf, 1);
if (n < 0 && errno == EAGAIN)
return;
if (n != 1) {
/* How do I signal an error ?*/
event_del(&han->sev);
return;
}
if (buf == 'S')
/* As we are at a lower priority (higher number)
* than the main event, we must have handled everything
*/
write(fd, "R", 1);
}
static void send_fd(int sock, int fd)
{
struct msghdr msg = {0};
struct iovec iov;
struct cmsghdr *cmsg;
int myfds[1];
char buf[CMSG_SPACE(sizeof myfds)];
int *fdptr;
msg.msg_control = buf;
msg.msg_controllen = sizeof buf;
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
fdptr = (int*)CMSG_DATA(cmsg);
fdptr[0] = fd;
msg.msg_controllen = cmsg->cmsg_len;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
iov.iov_base = "W";
iov.iov_len = 1;
sendmsg(sock, &msg, 0);
}
struct event *wake_set(int fd, void(*fn)(int,short,void*), void *data, int prio)
{
struct sockaddr_un addr;
struct han *h = malloc(sizeof(*h));
if (!h)
return NULL;
h->fn = fn;
h->data = data;
h->disable = suspend_open();
h->sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (h->sock < 0 || h->disable < 0)
goto abort;
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, "/run/suspend/registration");
if (connect(h->sock, (struct sockaddr*)&addr, sizeof(addr)) != 0)
goto abort;
fcntl(h->sock, F_SETFL, fcntl(h->sock, F_GETFL, 0) | O_NONBLOCK);
send_fd(h->sock, fd);
event_set(&h->ev, fd, EV_READ|EV_PERSIST, wakeup_call, h);
event_set(&h->sev, h->sock, EV_READ|EV_PERSIST, wakeup_sock, h);
event_priority_set(&h->ev, prio);
event_priority_set(&h->sev, prio+1);
event_add(&h->ev, NULL);
event_add(&h->sev, NULL);
return &h->ev;
abort:
suspend_close(h->disable);
if (h->sock >= 0)
close(h->sock);
free(h);
return NULL;
}
void wake_destroy(struct event *ev)
{
struct han *h = (struct han *)ev;
event_del(&h->ev);
event_del(&h->sev);
close(h->sock);
suspend_close(h->disable);
free(h);
}