Merge /home/maan/scm/paraslash_fml/paraslash
[paraslash.git] / dccp_send.c
1 /*
2 * Copyright (C) 2006-2007 Andre Noll <maan@systemlinux.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
17 */
18
19 /** \file dccp_send.c paraslash's dccp sender */
20
21 /*
22 * based on server.c of dccp-cs-0.01.tar.bz2,
23 * (C) 2005 Ian McDonald <imcdnzl@gmail.com>
24 */
25
26 #include "server.h"
27 #include "net.h"
28 #include "list.h"
29 #include "vss.h"
30 #include "send.h"
31 #include "dccp.h"
32 #include "error.h"
33 #include "string.h"
34 #include "fd.h"
35 #include "close_on_fork.h"
36 #include "server.cmdline.h"
37
38 /** the list of connected clients **/
39 static struct list_head clients;
40 static int listen_fd = -1;
41 static struct sender *self;
42
43 /** describes one connected client */
44 struct dccp_client {
45 /** the dccp socket */
46 int fd;
47 /** address information about the client */
48 struct sockaddr_in addr;
49 /** the position of this client in the client list */
50 struct list_head node;
51 /** non-zero if audio file header has been sent */
52 int header_sent;
53 };
54
55 static void dccp_pre_select( int *max_fileno, fd_set *rfds,
56 __a_unused fd_set *wfds)
57 {
58 if (listen_fd < 0)
59 return;
60 FD_SET(listen_fd, rfds);
61 *max_fileno = PARA_MAX(*max_fileno, listen_fd);
62 }
63
64 static void dccp_post_select(fd_set *rfds, __a_unused fd_set *wfds)
65 {
66 struct dccp_client *dc;
67 int ret;
68
69 if (!FD_ISSET(listen_fd, rfds))
70 return;
71 dc = para_calloc(sizeof(struct dccp_client));
72 ret = para_accept(listen_fd, &dc->addr, sizeof(struct sockaddr_in));
73 if (ret < 0) {
74 PARA_ERROR_LOG("%s", PARA_STRERROR(-ret));
75 return;
76 }
77 PARA_NOTICE_LOG("connection from %s\n", inet_ntoa(dc->addr.sin_addr));
78 dc->fd = ret;
79 para_list_add(&dc->node, &clients);
80 add_close_on_fork_list(dc->fd);
81 mark_fd_nonblock(dc->fd);
82 }
83
84 static int dccp_open(void)
85 {
86 struct sockaddr_in servaddr;
87 int ret;
88
89 ret = dccp_get_socket();
90 if (ret < 0)
91 return ret;
92 listen_fd = ret;
93
94 bzero(&servaddr, sizeof(servaddr));
95 servaddr.sin_family = AF_INET;
96 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
97 servaddr.sin_port = htons(conf.dccp_port_arg);
98 ret = bind(listen_fd, (struct sockaddr *)&servaddr, sizeof(servaddr));
99 if (ret < 0)
100 return -E_DCCP_BIND;
101 ret = dccp_set_socket(listen_fd);
102 if (ret < 0)
103 return ret;
104 ret = listen(listen_fd, 0);
105 if (ret < 0)
106 return -E_DCCP_LISTEN;
107 PARA_DEBUG_LOG("listening on fd %d\n", listen_fd);
108 add_close_on_fork_list(listen_fd);
109 mark_fd_nonblock(listen_fd);
110 return 1;
111 }
112
113 static void dccp_shutdown_client(struct dccp_client *dc)
114 {
115 PARA_DEBUG_LOG("shutting down %s (fd %d)\n", inet_ntoa(dc->addr.sin_addr),
116 dc->fd);
117 close(dc->fd);
118 del_close_on_fork_list(dc->fd);
119 list_del(&dc->node);
120 free(dc);
121 }
122
123 /** give up if write would block that many times */
124 #define DCCP_WRITE_RETRIES 100
125
126 static int dccp_write(int fd, const char *buf, size_t len)
127 {
128 size_t size, written = 0;
129 int ret, retries = 0;
130 again:
131 size = PARA_MIN(1024, len - written);
132 ret = write(fd, buf + written, size);
133 if (ret < 0) {
134 if (errno != EAGAIN || !retries++ > DCCP_WRITE_RETRIES)
135 goto err_out;
136 PARA_DEBUG_LOG("EAGAIN #%d@%zd/%zd\n", retries, written, len);
137 goto again;
138 }
139 retries = 0;
140 written += ret;
141 if (written >= len)
142 return written;
143 ret = write_ok(fd);
144 if (ret > 0)
145 goto again;
146 err_out:
147 return -E_DCCP_WRITE;
148 }
149
150 static void dccp_send(long unsigned current_chunk,
151 __a_unused long unsigned chunks_sent, const char *buf, size_t len)
152 {
153 struct dccp_client *dc, *tmp;
154 int ret, header_len;
155 char *header_buf;
156
157 if (listen_fd < 0 || !len)
158 return;
159
160 list_for_each_entry_safe(dc, tmp, &clients, node) {
161 ret = write_ok(dc->fd);
162 if (ret < 0) {
163 dccp_shutdown_client(dc);
164 continue;
165 }
166 if (!ret)
167 continue;
168 if (!dc->header_sent && current_chunk) {
169 header_buf = vss_get_header(&header_len);
170 if (header_buf && header_len > 0) {
171 ret = dccp_write(dc->fd, header_buf, header_len);
172 if (ret != header_len) {
173 int err = errno;
174 PARA_ERROR_LOG("header write: %d/%d (%s)\n",
175 ret, header_len, ret < 0?
176 strerror(err) : "");
177 dccp_shutdown_client(dc);
178 continue;
179 }
180 dc->header_sent = 1;
181 ret = write_ok(dc->fd);
182 if (ret < 0) {
183 dccp_shutdown_client(dc);
184 continue;
185 }
186 if (!ret)
187 continue;
188 }
189 }
190 // PARA_DEBUG_LOG("writing %d bytes to fd %d\n", len, dc->fd);
191 ret = dccp_write(dc->fd, buf, len);
192 if (ret != len)
193 dccp_shutdown_client(dc);
194 }
195 }
196
197 static void dccp_shutdown_clients(void)
198 {
199 struct dccp_client *dc, *tmp;
200
201 list_for_each_entry_safe(dc, tmp, &clients, node)
202 dccp_shutdown_client(dc);
203 }
204
205 static char *dccp_info(void)
206 {
207 static char *buf;
208 int num_clients = 0;
209 struct dccp_client *dc, *tmp;
210
211 free(buf);
212 list_for_each_entry_safe(dc, tmp, &clients, node)
213 num_clients++;
214 buf = make_message("dccp connected clients: %d\n", num_clients);
215 return buf;
216 }
217
218 static char *dccp_help(void)
219 {
220 return make_message("no help available\n");
221 }
222
223 /**
224 * the init function of the dccp sender
225 *
226 * \param s pointer to the dccp sender struct
227 *
228 * It initializes all function pointers of \a s and starts
229 * listening on the given port.
230 */
231 void dccp_send_init(struct sender *s)
232 {
233 int ret;
234
235 INIT_LIST_HEAD(&clients);
236 s->info = dccp_info;
237 s->send = dccp_send;
238 s->pre_select = dccp_pre_select;
239 s->post_select = dccp_post_select;
240 s->shutdown_clients = dccp_shutdown_clients;
241 s->help = dccp_help;
242 s->client_cmds[SENDER_ON] = NULL;
243 s->client_cmds[SENDER_OFF] = NULL;
244 s->client_cmds[SENDER_DENY] = NULL;
245 s->client_cmds[SENDER_ALLOW] = NULL;
246 s->client_cmds[SENDER_ADD] = NULL;
247 s->client_cmds[SENDER_DELETE] = NULL;
248 self = s;
249 ret = dccp_open();
250 if (ret < 0) {
251 PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
252 s->status = SENDER_OFF;
253 } else
254 s->status = SENDER_ON;
255 }