refactored notify.c for clarity
[irssi-notify.git] / listen.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <errno.h>
5 #include <string.h>
6 #include <sys/types.h>
7 #include <sys/socket.h>
8 #include <netinet/in.h>
9 #include <netdb.h>
10 #include <arpa/inet.h>
11 #include <sys/wait.h>
12 #include <signal.h>
13 #include <libnotify/notify.h>
14
15 #define PORT "3490"
16 #define QUEUE_LENGTH 10
17
18 // a function to reap future child processes
19 void sigchld_handler(int s)
20 {
21 while(waitpid(-1, NULL, WNOHANG) > 0);
22 }
23
24 // look for an IPv4/6 host:port in an address structure
25 void *get_in_addr(struct sockaddr *sa)
26 {
27 if (sa->sa_family == AF_INET) {
28 return &(((struct sockaddr_in*)sa)->sin_addr);
29 }
30 return &(((struct sockaddr_in6*)sa)->sin6_addr);
31 }
32
33 int main(void)
34 {
35 // new connections on activefd, always listening on sockfd
36 int sockfd, activefd;
37 struct addrinfo hints, *servinfo, *port;
38 struct sockaddr_storage their_addr;
39 socklen_t sin_size;
40 struct sigaction sa;
41 char source[INET6_ADDRSTRLEN];
42 int yes=1;
43 int rv;
44
45 memset(&hints, 0, sizeof hints);
46 hints.ai_family = AF_INET;
47 hints.ai_socktype = SOCK_STREAM;
48 hints.ai_flags = AI_PASSIVE;
49
50 // look up local address information
51 if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
52 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
53 return 1;
54 }
55
56 // loop over available interfaces and try to bind to port
57 for (port = servinfo; port != NULL; port = port->ai_next) {
58 if ((sockfd = socket(port->ai_family, port->ai_socktype,
59 port->ai_protocol)) == -1) {
60 perror("irssi-notify: socket");
61 continue;
62 }
63 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
64 sizeof(int)) == -1) {
65 perror("setsockopt");
66 exit(1);
67 }
68 if (bind(sockfd, port->ai_addr, port->ai_addrlen) == -1) {
69 close(sockfd);
70 perror("irssi-notify: bind");
71 continue;
72 }
73 break;
74 }
75
76 if (port == NULL) {
77 fprintf(stderr, "irssi-notify: failed to bind\n");
78 return 2;
79 }
80
81 freeaddrinfo(servinfo);
82
83 if (listen(sockfd, QUEUE_LENGTH) == -1) {
84 perror("listen");
85 exit(1);
86 }
87
88 // bind sigchld_handler to reap future child processes
89 sa.sa_handler = sigchld_handler;
90 sigemptyset(&sa.sa_mask);
91 sa.sa_flags = SA_RESTART;
92 if (sigaction(SIGCHLD, &sa, NULL) == -1) {
93 perror("sigaction");
94 exit(1);
95 }
96
97 printf("irssi-notify: waiting for connections...\n");
98
99 while (1) {
100 sin_size = sizeof their_addr;
101 activefd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
102 if (activefd == -1) {
103 perror("accept");
104 continue;
105 }
106
107 inet_ntop(their_addr.ss_family,
108 get_in_addr((struct sockaddr *)&their_addr),
109 source, sizeof source);
110
111 // whitelist allowed
112 if (strcmp(source, "50.16.219.8") != 0 &&
113 strcmp(source, "127.0.0.1") != 0){
114 close(activefd);
115 continue;
116 }
117
118 // start a child process to handle notifications
119 if (!fork()) {
120 // the child process doesn't need the listener
121 close(sockfd);
122 char buf[4096];
123 if (recv(activefd, buf, 99, 0) == -1) {
124 perror("recv");
125 exit(1);
126 }
127 buf[4096] = '\0';
128 printf("irssi-notify: recieved '%s'\n", buf);
129 notify_init("irssi-notify");
130 NotifyNotification * notification =
131 notify_notification_new("", buf, "irssi-notify");
132 notify_notification_show(notification, NULL);
133 close(activefd);
134 exit(0);
135 }
136 close(activefd);
137 }
138
139 return 0;
140 }