dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / smbsrv / smbd / smbd_nicmon.c
blob3bcc52f0c0fc6815e52485cd13658d8e8133dc76
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
26 * smbd NIC monitor.
29 #include <sys/types.h>
30 #include <stdlib.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <signal.h>
35 #include <stdio.h>
36 #include <net/if.h>
37 #include <net/route.h>
38 #include <sys/sockio.h>
39 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 #include <fcntl.h>
43 #include <pthread.h>
44 #include <syslog.h>
45 #include <smbsrv/libsmb.h>
46 #include <poll.h>
47 #include "smbd.h"
49 #define SMBD_NICMON_ENABLE "nicmon_enable"
50 #define SMBD_NICMON_THROTTLE 100
51 #define SMBD_NICMON_DEBOUNCE 2
53 extern smbd_t smbd;
55 static boolean_t smbd_nicmon_enabled = B_TRUE;
57 /* Use this to stop monitoring */
58 static int eventpipe_write = -1;
60 /* Use this to refresh service instance */
61 static char *smbd_nicmon_caller_fmri = NULL;
63 static void smbd_nicmon_run_check(void);
64 static int smbd_nicmon_setup_rtsock(int);
65 static int smbd_nicmon_needscan(int);
66 static int smbd_nicmon_setup_eventpipe(int *, int *);
67 static void *smbd_nicmon_daemon(void *);
70 * Start the nic monitor thread.
72 int
73 smbd_nicmon_start(const char *svc_fmri)
75 pthread_t smbd_nicmon_tid;
76 int rc;
78 if (smb_nic_init() != SMB_NIC_SUCCESS)
79 return (-1);
81 rc = pthread_create(&smbd_nicmon_tid, NULL, smbd_nicmon_daemon, NULL);
82 if (rc != 0)
83 return (-1);
85 if (svc_fmri)
86 smbd_nicmon_caller_fmri = (char *)svc_fmri;
88 smbd_nicmon_run_check();
89 return (0);
92 void
93 smbd_nicmon_stop(void)
95 uchar_t buf = 1;
97 if (eventpipe_write < 0)
98 return;
100 (void) write(eventpipe_write, &buf, sizeof (buf));
101 smbd_nicmon_caller_fmri = NULL;
102 smb_nic_fini();
106 smbd_nicmon_refresh(void)
108 if (smb_nic_init() != SMB_NIC_SUCCESS)
109 return (-1);
111 smbd_nicmon_run_check();
112 return (0);
116 * The monitor is enabled unless it is explicitly
117 * disabled by setting smbd/nicmon_enable to false.
118 * smbd/nicmon_enable is not defined by default.
120 static void
121 smbd_nicmon_run_check(void)
123 smb_scfhandle_t *hd;
124 uint8_t status;
125 int rc;
127 smbd_nicmon_enabled = B_TRUE;
129 if ((hd = smb_smf_scf_init(SMBD_FMRI_PREFIX)) == NULL) {
130 syslog(LOG_DEBUG,
131 "smbd_nicmon: smb_smf_scf_init failed");
132 return;
135 rc = smb_smf_create_service_pgroup(hd, SMBD_PG_NAME);
136 if (rc != SMBD_SMF_OK) {
137 smb_smf_scf_fini(hd);
138 syslog(LOG_DEBUG,
139 "smbd_nicmon: smb_smf_create_service_pgroup failed");
140 return;
143 rc = smb_smf_get_boolean_property(hd, SMBD_NICMON_ENABLE, &status);
144 if (rc == SMBD_SMF_OK && status == 0)
145 smbd_nicmon_enabled = B_FALSE;
147 smb_smf_scf_fini(hd);
151 * Setup routing socket for getting RTM messages.
153 static int
154 smbd_nicmon_setup_rtsock(int af)
156 int sd;
157 int flags;
159 if ((sd = socket(PF_ROUTE, SOCK_RAW, af)) == -1) {
160 syslog(LOG_ERR,
161 "smbd_nicmon: routing socket failed: %d", errno);
162 return (-1);
165 if ((flags = fcntl(sd, F_GETFL, 0)) < 0) {
166 syslog(LOG_ERR,
167 "smbd_nicmon: fcntl F_GETFL failed: %d", errno);
168 (void) close(sd);
169 return (-1);
172 if ((fcntl(sd, F_SETFL, flags | O_NONBLOCK)) < 0) {
173 syslog(LOG_ERR,
174 "smbd_nicmon: fcntl F_SETFL failed: %d", errno);
175 (void) close(sd);
176 return (-1);
179 return (sd);
182 static int
183 smbd_nicmon_needscan(int sock)
185 static uint32_t throttle;
186 struct rt_msghdr *rtm;
187 int64_t msg[2048 / 8];
188 int need_if_scan = 0;
189 int nbytes;
191 /* Read as many messages as possible and try to empty the sockets */
192 for (;;) {
193 nbytes = read(sock, msg, sizeof (msg));
194 if (nbytes <= 0)
195 break;
197 rtm = (struct rt_msghdr *)msg;
198 if (rtm->rtm_version != RTM_VERSION)
199 continue;
201 if (nbytes < rtm->rtm_msglen) {
202 if ((throttle % SMBD_NICMON_THROTTLE) == 0) {
203 syslog(LOG_DEBUG,
204 "smbd_nicmon: short read: %d of %d",
205 nbytes, rtm->rtm_msglen);
207 ++throttle;
208 continue;
211 switch (rtm->rtm_type) {
212 case RTM_NEWADDR:
213 case RTM_DELADDR:
214 case RTM_IFINFO:
215 need_if_scan = 1;
216 break;
217 default:
218 break;
222 return (need_if_scan);
226 * Create pipe for signal delivery and set up signal handlers.
228 static int
229 smbd_nicmon_setup_eventpipe(int *read_pipe, int *write_pipe)
231 int fds[2];
233 if ((pipe(fds)) < 0) {
234 syslog(LOG_ERR,
235 "smbd_nicmon: event pipe failed: %d", errno);
236 return (-1);
239 *read_pipe = fds[0];
240 *write_pipe = fds[1];
241 return (0);
245 * Create the global routing socket to monitor changes in NIC interfaces.
246 * We are only interested in new inerface addition/deletion and changes
247 * in UP/DOWN status.
249 * Note: only supports AF_INET routing socket. Need to add AF_INET6 to
250 * support IPv6.
252 /*ARGSUSED*/
253 static void *
254 smbd_nicmon_daemon(void *arg)
256 static uint32_t throttle;
257 static int rtsock_v4;
258 static int eventpipe_read = -1;
259 struct pollfd pollfds[2];
260 int pollfd_num = 2;
261 int i, nic_changed;
262 int rc;
264 if ((rtsock_v4 = smbd_nicmon_setup_rtsock(AF_INET)) == -1)
265 return (NULL);
267 rc = smbd_nicmon_setup_eventpipe(&eventpipe_read, &eventpipe_write);
268 if (rc != 0)
269 return (NULL);
272 * Listen for activity on any of the sockets.
273 * The delay before checking the rtsock will hopefully
274 * smooth things out when there is a lot of activity.
276 for (;;) {
277 errno = 0;
278 nic_changed = 0;
279 pollfds[0].fd = rtsock_v4;
280 pollfds[0].events = POLLIN;
281 pollfds[1].fd = eventpipe_read;
282 pollfds[1].events = POLLIN;
284 if (poll(pollfds, pollfd_num, -1) < 0) {
285 if (errno == EINTR)
286 continue;
287 if ((throttle % SMBD_NICMON_THROTTLE) == 0)
288 syslog(LOG_DEBUG,
289 "smbd_nicmon: poll failed: %d", errno);
290 ++throttle;
291 break;
294 for (i = 0; i < pollfd_num; i++) {
295 if ((pollfds[i].fd < 0) ||
296 !(pollfds[i].revents & POLLIN))
297 continue;
298 if (pollfds[i].fd == rtsock_v4) {
299 (void) sleep(SMBD_NICMON_DEBOUNCE);
300 nic_changed = smbd_nicmon_needscan(rtsock_v4);
302 if (pollfds[i].fd == eventpipe_read)
303 goto done;
307 * If the monitor is enabled and something has changed,
308 * refresh the registered SMF service.
310 if (smbd_nicmon_enabled && nic_changed &&
311 smbd_nicmon_caller_fmri) {
312 if (smf_refresh_instance(smbd_nicmon_caller_fmri) != 0)
313 syslog(LOG_ERR,
314 "smbd_nicmon: %s refresh failed",
315 smbd_nicmon_caller_fmri);
318 done:
319 (void) close(rtsock_v4);
320 (void) close(eventpipe_read);
321 (void) close(eventpipe_write);
322 eventpipe_write = -1;
323 return (NULL);