FreeBSD/Linux Kernel Cross Reference
sys/netncp/ncp_conn.c
1 /*-
2 * Copyright (c) 1999, Boris Popov
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Boris Popov.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * Connection tables
33 */
34
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/malloc.h>
42 #include <sys/priv.h>
43 #include <sys/proc.h>
44 #include <sys/lock.h>
45 #include <sys/sysctl.h>
46
47 #include <netncp/ncp.h>
48 #include <netncp/nwerror.h>
49 #include <netncp/ncp_subr.h>
50 #include <netncp/ncp_conn.h>
51 #include <netncp/ncp_sock.h>
52 #include <netncp/ncp_ncp.h>
53
54 SLIST_HEAD(ncp_handle_head,ncp_handle);
55
56 int ncp_burst_enabled = 1;
57
58 struct ncp_conn_head conn_list={NULL};
59 static int ncp_conn_cnt = 0;
60 static int ncp_next_ref = 1;
61 static struct lock listlock;
62
63 struct ncp_handle_head lhlist={NULL};
64 static int ncp_next_handle = 1;
65 static struct lock lhlock;
66
67 static int ncp_sysctl_connstat(SYSCTL_HANDLER_ARGS);
68 static int ncp_conn_lock_any(struct ncp_conn *conn, struct thread *td,
69 struct ucred *cred);
70
71 SYSCTL_DECL(_net_ncp);
72 SYSCTL_INT (_net_ncp, OID_AUTO, burst_enabled, CTLFLAG_RD, &ncp_burst_enabled, 0, "");
73 SYSCTL_INT (_net_ncp, OID_AUTO, conn_cnt, CTLFLAG_RD, &ncp_conn_cnt, 0, "");
74 SYSCTL_PROC(_net_ncp, OID_AUTO, conn_stat, CTLFLAG_RD|CTLTYPE_OPAQUE,
75 NULL, 0, ncp_sysctl_connstat, "S,connstat", "Connections list");
76
77 MALLOC_DEFINE(M_NCPDATA, "ncp_data", "NCP private data");
78
79 int
80 ncp_conn_init(void)
81 {
82 lockinit(&listlock, PSOCK, "ncpll", 0, 0);
83 lockinit(&lhlock, PSOCK, "ncplh", 0, 0);
84 return 0;
85 }
86
87 int
88 ncp_conn_destroy(void)
89 {
90 if (ncp_conn_cnt) {
91 NCPERROR("There are %d connections active\n", ncp_conn_cnt);
92 return EBUSY;
93 }
94 lockdestroy(&listlock);
95 lockdestroy(&lhlock);
96 return 0;
97 }
98
99 int
100 ncp_conn_locklist(int flags, struct thread *td)
101 {
102 return lockmgr(&listlock, flags | LK_CANRECURSE, 0, td);
103 }
104
105 void
106 ncp_conn_unlocklist(struct thread *td)
107 {
108 lockmgr(&listlock, LK_RELEASE, 0, td);
109 }
110
111 int
112 ncp_conn_access(struct ncp_conn *conn, struct ucred *cred, mode_t mode)
113 {
114 int error;
115
116 if (cred == NOCRED || ncp_suser(cred) == 0 ||
117 cred->cr_uid == conn->nc_owner->cr_uid)
118 return 0;
119 mode >>= 3;
120 if (!groupmember(conn->nc_group, cred))
121 mode >>= 3;
122 error = (conn->li.access_mode & mode) == mode ? 0 : EACCES;
123 return error;
124 }
125
126 int
127 ncp_conn_lock_any(struct ncp_conn *conn, struct thread *td, struct ucred *cred)
128 {
129 int error;
130
131 if (conn->nc_id == 0) return EACCES;
132 error = lockmgr(&conn->nc_lock, LK_EXCLUSIVE | LK_CANRECURSE, 0, td);
133 if (error == ERESTART)
134 return EINTR;
135 error = ncp_chkintr(conn, td);
136 if (error) {
137 lockmgr(&conn->nc_lock, LK_RELEASE, 0, td);
138 return error;
139 }
140
141 if (conn->nc_id == 0) {
142 lockmgr(&conn->nc_lock, LK_RELEASE, 0, td);
143 return EACCES;
144 }
145 conn->td = td; /* who currently operates */
146 conn->ucred = cred;
147 return 0;
148 }
149
150 int
151 ncp_conn_lock(struct ncp_conn *conn, struct thread *td, struct ucred *cred, int mode)
152 {
153 int error;
154
155 error = ncp_conn_access(conn, cred, mode);
156 if (error) return error;
157 return ncp_conn_lock_any(conn, td, cred);
158 }
159
160 /*
161 * Lock conn but unlock connlist
162 */
163 static int
164 ncp_conn_lock2(struct ncp_conn *conn, struct thread *td, struct ucred *cred, int mode)
165 {
166 int error;
167
168 error = ncp_conn_access(conn, cred, mode);
169 if (error) {
170 ncp_conn_unlocklist(td);
171 return error;
172 }
173 conn->nc_lwant++;
174 ncp_conn_unlocklist(td);
175 error = ncp_conn_lock_any(conn, td, cred);
176 conn->nc_lwant--;
177 if (conn->nc_lwant == 0) {
178 wakeup(&conn->nc_lwant);
179 }
180 return error;
181 }
182
183 void
184 ncp_conn_unlock(struct ncp_conn *conn, struct thread *td)
185 {
186 /*
187 * note, that LK_RELASE will do wakeup() instead of wakeup_one().
188 * this will do a little overhead
189 */
190 lockmgr(&conn->nc_lock, LK_RELEASE, 0, td);
191 }
192
193 int
194 ncp_conn_assert_locked(struct ncp_conn *conn, const char *checker, struct thread *td)
195 {
196 if (conn->nc_lock.lk_flags & LK_HAVE_EXCL) return 0;
197 printf("%s: connection isn't locked!\n", checker);
198 return EIO;
199 }
200
201 void
202 ncp_conn_invalidate(struct ncp_conn *ncp)
203 {
204 ncp->flags &= ~(NCPFL_ATTACHED | NCPFL_LOGGED | NCPFL_INVALID);
205 }
206
207 int
208 ncp_conn_invalid(struct ncp_conn *ncp)
209 {
210 return ncp->flags & NCPFL_INVALID;
211 }
212
213 /*
214 * create, fill with defaults and return in locked state
215 */
216 int
217 ncp_conn_alloc(struct ncp_conn_args *cap, struct thread *td, struct ucred *cred,
218 struct ncp_conn **conn)
219 {
220 struct ncp_conn *ncp;
221 struct ucred *owner;
222 int error, isroot;
223
224 if (cap->saddr.sa_family != AF_INET && cap->saddr.sa_family != AF_IPX)
225 return EPROTONOSUPPORT;
226 /*
227 * Only root can change ownership.
228 */
229 isroot = ncp_suser(cred) == 0;
230 if (cap->owner != NCP_DEFAULT_OWNER && !isroot)
231 return EPERM;
232 if (cap->group != NCP_DEFAULT_GROUP &&
233 !groupmember(cap->group, cred) && !isroot)
234 return EPERM;
235 if (cap->owner != NCP_DEFAULT_OWNER) {
236 owner = crget();
237 crcopy(owner, cred);
238 owner->cr_uid = cap->owner;
239 } else
240 owner = crhold(cred);
241 MALLOC(ncp, struct ncp_conn *, sizeof(struct ncp_conn),
242 M_NCPDATA, M_WAITOK | M_ZERO);
243 error = 0;
244 lockinit(&ncp->nc_lock, PZERO, "ncplck", 0, 0);
245 ncp_conn_cnt++;
246 ncp->nc_id = ncp_next_ref++;
247 ncp->nc_owner = owner;
248 ncp->seq = 0;
249 ncp->connid = 0xFFFF;
250 ncp->li = *cap;
251 ncp->nc_group = (cap->group != NCP_DEFAULT_GROUP) ?
252 cap->group : cred->cr_groups[0];
253
254 if (cap->retry_count == 0)
255 ncp->li.retry_count = NCP_RETRY_COUNT;
256 if (cap->timeout == 0)
257 ncp->li.timeout = NCP_RETRY_TIMEOUT;
258 ncp_conn_lock_any(ncp, td, ncp->nc_owner);
259 *conn = ncp;
260 ncp_conn_locklist(LK_EXCLUSIVE, td);
261 SLIST_INSERT_HEAD(&conn_list,ncp,nc_next);
262 ncp_conn_unlocklist(td);
263 return (error);
264 }
265
266 /*
267 * Remove the connection, on entry it must be locked
268 */
269 int
270 ncp_conn_free(struct ncp_conn *ncp)
271 {
272 struct thread *td;
273 int error;
274
275 if (ncp == NULL) {
276 NCPFATAL("ncp == NULL\n");
277 return 0;
278 }
279 if (ncp->nc_id == 0) {
280 NCPERROR("nc_id == 0\n");
281 return EACCES;
282 }
283 td = ncp->td;
284 error = ncp_conn_assert_locked(ncp, __func__, td);
285 if (error)
286 return error;
287 if (ncp->ref_cnt != 0 || (ncp->flags & NCPFL_PERMANENT))
288 return EBUSY;
289 if (ncp_conn_access(ncp, ncp->ucred, NCPM_WRITE))
290 return EACCES;
291
292 if (ncp->flags & NCPFL_ATTACHED)
293 ncp_ncp_disconnect(ncp);
294 ncp_sock_disconnect(ncp);
295
296 /*
297 * Mark conn as dead and wait for other process
298 */
299 ncp->nc_id = 0;
300 ncp_conn_unlock(ncp, td);
301 /*
302 * if signal is raised - how I do react ?
303 */
304 lockmgr(&ncp->nc_lock, LK_DRAIN, 0, td);
305 lockdestroy(&ncp->nc_lock);
306 while (ncp->nc_lwant) {
307 printf("lwant = %d\n", ncp->nc_lwant);
308 tsleep(&ncp->nc_lwant, PZERO,"ncpdr",2*hz);
309 }
310 ncp_conn_locklist(LK_EXCLUSIVE, td);
311 SLIST_REMOVE(&conn_list, ncp, ncp_conn, nc_next);
312 ncp_conn_cnt--;
313 ncp_conn_unlocklist(td);
314 if (ncp->li.user)
315 free(ncp->li.user, M_NCPDATA);
316 if (ncp->li.password)
317 free(ncp->li.password, M_NCPDATA);
318 crfree(ncp->nc_owner);
319 FREE(ncp, M_NCPDATA);
320 return (0);
321 }
322
323 int
324 ncp_conn_reconnect(struct ncp_conn *ncp)
325 {
326 int error;
327
328 /*
329 * Close opened sockets if any
330 */
331 ncp_sock_disconnect(ncp);
332 error = ncp_sock_connect(ncp);
333 if (error)
334 return error;
335 error = ncp_ncp_connect(ncp);
336 if (error)
337 return error;
338 error = ncp_renegotiate_connparam(ncp, NCP_DEFAULT_BUFSIZE, 0);
339 if (error == NWE_SIGNATURE_LEVEL_CONFLICT) {
340 printf("Unable to negotiate requested security level\n");
341 error = EOPNOTSUPP;
342 }
343 if (error) {
344 ncp_ncp_disconnect(ncp);
345 return error;
346 }
347 #ifdef NCPBURST
348 error = ncp_burst_connect(ncp);
349 if (error) {
350 ncp_ncp_disconnect(ncp);
351 return error;
352 }
353 #endif
354 return 0;
355 }
356
357 int
358 ncp_conn_login(struct ncp_conn *conn, struct thread *td, struct ucred *cred)
359 {
360 struct ncp_bindery_object user;
361 u_char ncp_key[8];
362 int error;
363
364 error = ncp_get_encryption_key(conn, ncp_key);
365 if (error) {
366 printf("%s: Warning: use unencrypted login\n", __func__);
367 error = ncp_login_unencrypted(conn, conn->li.objtype,
368 conn->li.user, conn->li.password, td, cred);
369 } else {
370 error = ncp_get_bindery_object_id(conn, conn->li.objtype,
371 conn->li.user, &user, td, cred);
372 if (error)
373 return error;
374 error = ncp_login_encrypted(conn, &user, ncp_key,
375 conn->li.password, td, cred);
376 }
377 if (!error)
378 conn->flags |= NCPFL_LOGGED | NCPFL_WASLOGGED;
379 return error;
380 }
381
382 /*
383 * Lookup connection by handle, return a locked conn descriptor
384 */
385 int
386 ncp_conn_getbyref(int ref, struct thread *td, struct ucred *cred, int mode,
387 struct ncp_conn **connpp)
388 {
389 struct ncp_conn *ncp;
390 int error = 0;
391
392 ncp_conn_locklist(LK_SHARED, td);
393 SLIST_FOREACH(ncp, &conn_list, nc_next)
394 if (ncp->nc_id == ref) break;
395 if (ncp == NULL) {
396 ncp_conn_unlocklist(td);
397 return(EBADF);
398 }
399 error = ncp_conn_lock2(ncp, td, cred, mode);
400 if (!error)
401 *connpp = ncp;
402 return (error);
403 }
404 /*
405 * find attached, but not logged in connection to specified server
406 */
407 int
408 ncp_conn_getattached(struct ncp_conn_args *li, struct thread *td,
409 struct ucred *cred, int mode, struct ncp_conn **connpp)
410 {
411 struct ncp_conn *ncp, *ncp2 = NULL;
412 int error = 0;
413
414 ncp_conn_locklist(LK_SHARED, td);
415 SLIST_FOREACH(ncp, &conn_list, nc_next) {
416 if ((ncp->flags & NCPFL_LOGGED) != 0 ||
417 strcmp(ncp->li.server,li->server) != 0 ||
418 ncp->li.saddr.sa_len != li->saddr.sa_len ||
419 bcmp(&ncp->li.saddr,&ncp->li.saddr,li->saddr.sa_len) != 0)
420 continue;
421 if (ncp_suser(cred) == 0 ||
422 cred->cr_uid == ncp->nc_owner->cr_uid)
423 break;
424 error = ncp_conn_access(ncp,cred,mode);
425 if (!error && ncp2 == NULL)
426 ncp2 = ncp;
427 }
428 if (ncp == NULL) ncp = ncp2;
429 if (ncp == NULL) {
430 ncp_conn_unlocklist(td);
431 return(EBADF);
432 }
433 error = ncp_conn_lock2(ncp, td, cred, mode);
434 if (!error)
435 *connpp=ncp;
436 return (error);
437 }
438
439 /*
440 * Lookup connection by server/user pair, return a locked conn descriptor.
441 * if li is NULL or server/user pair incomplete, try to select best connection
442 * based on owner.
443 * Connection selected in next order:
444 * 1. Try to search conn with ucred owner, if li is NULL also find a primary
445 * 2. If 1. fails try to get first suitable shared connection
446 * 3. If 2. fails then nothing can help to poor ucred owner
447 */
448
449 int
450 ncp_conn_getbyli(struct ncp_conn_args *li, struct thread *td,
451 struct ucred *cred, int mode, struct ncp_conn **connpp)
452 {
453 struct ncp_conn *ncp, *ncp2 = NULL;
454 int error = 0, partial, haveserv;
455
456 partial = (li == NULL || li->server[0] == 0 || li->user == NULL);
457 haveserv = (li && li->server[0]);
458 ncp_conn_locklist(LK_SHARED, td);
459 SLIST_FOREACH(ncp, &conn_list, nc_next) {
460 if (partial) {
461 if (cred->cr_uid == ncp->nc_owner->cr_uid) {
462 if (haveserv) {
463 if (strcmp(ncp->li.server,li->server) == 0)
464 break;
465 } else {
466 if (ncp->flags & NCPFL_PRIMARY)
467 break;
468 ncp2 = ncp;
469 }
470 continue;
471 }
472 } else {
473 if (strcmp(ncp->li.server,li->server) != 0 ||
474 ncp->li.user == NULL ||
475 strcmp(ncp->li.user,li->user) != 0)
476 continue;
477 if (cred->cr_uid == ncp->nc_owner->cr_uid)
478 break;
479 if (ncp_suser(cred) == 0)
480 ncp2 = ncp;
481 }
482 error = ncp_conn_access(ncp,cred,mode);
483 if (!error && ncp2 == NULL)
484 ncp2 = ncp;
485 }
486 if (ncp == NULL) ncp = ncp2;
487 if (ncp == NULL) {
488 ncp_conn_unlocklist(td);
489 return(EBADF);
490 }
491 error = ncp_conn_lock2(ncp, td, cred,mode);
492 if (!error)
493 *connpp=ncp;
494 return (error);
495 }
496
497 /*
498 * Set primary connection flag, since it have sence only for an owner,
499 * only owner can modify this flag.
500 * connection expected to be locked.
501 */
502 int
503 ncp_conn_setprimary(struct ncp_conn *conn, int on)
504 {
505 struct ncp_conn *ncp=NULL;
506
507 if (conn->ucred->cr_uid != conn->nc_owner->cr_uid)
508 return EACCES;
509 ncp_conn_locklist(LK_SHARED, conn->td);
510 SLIST_FOREACH(ncp, &conn_list, nc_next) {
511 if (conn->ucred->cr_uid == ncp->nc_owner->cr_uid)
512 ncp->flags &= ~NCPFL_PRIMARY;
513 }
514 ncp_conn_unlocklist(conn->td);
515 if (on)
516 conn->flags |= NCPFL_PRIMARY;
517 return 0;
518 }
519 /*
520 * Lease conn to given proc, returning unique handle
521 * problem: how locks should be applied ?
522 */
523 int
524 ncp_conn_gethandle(struct ncp_conn *conn, struct thread *td, struct ncp_handle **handle)
525 {
526 struct ncp_handle *refp;
527
528 lockmgr(&lhlock, LK_EXCLUSIVE, 0, td);
529 SLIST_FOREACH(refp, &lhlist, nh_next)
530 if (refp->nh_conn == conn && td == refp->nh_td) break;
531 if (refp) {
532 conn->ref_cnt++;
533 refp->nh_ref++;
534 *handle = refp;
535 lockmgr(&lhlock, LK_RELEASE, 0, td);
536 return 0;
537 }
538 MALLOC(refp,struct ncp_handle *,sizeof(struct ncp_handle),M_NCPDATA,
539 M_WAITOK | M_ZERO);
540 SLIST_INSERT_HEAD(&lhlist,refp,nh_next);
541 refp->nh_ref++;
542 refp->nh_td = td;
543 refp->nh_conn = conn;
544 refp->nh_id = ncp_next_handle++;
545 *handle = refp;
546 conn->ref_cnt++;
547 lockmgr(&lhlock, LK_RELEASE, 0, td);
548 return 0;
549 }
550 /*
551 * release reference, if force - ignore refcount
552 */
553 int
554 ncp_conn_puthandle(struct ncp_handle *handle, struct thread *td, int force)
555 {
556 struct ncp_handle *refp = handle;
557
558 lockmgr(&lhlock, LK_EXCLUSIVE, 0, td);
559 refp->nh_ref--;
560 refp->nh_conn->ref_cnt--;
561 if (force) {
562 refp->nh_conn->ref_cnt -= refp->nh_ref;
563 refp->nh_ref = 0;
564 }
565 if (refp->nh_ref == 0) {
566 SLIST_REMOVE(&lhlist, refp, ncp_handle, nh_next);
567 FREE(refp, M_NCPDATA);
568 }
569 lockmgr(&lhlock, LK_RELEASE, 0, td);
570 return 0;
571 }
572 /*
573 * find a connHandle
574 */
575 int
576 ncp_conn_findhandle(int connHandle, struct thread *td, struct ncp_handle **handle) {
577 struct ncp_handle *refp;
578
579 lockmgr(&lhlock, LK_SHARED, 0, td);
580 SLIST_FOREACH(refp, &lhlist, nh_next)
581 if (refp->nh_td == td && refp->nh_id == connHandle) break;
582 lockmgr(&lhlock, LK_RELEASE, 0, td);
583 if (refp == NULL) {
584 return EBADF;
585 }
586 *handle = refp;
587 return 0;
588 }
589 /*
590 * Clear handles associated with specified process
591 */
592 int
593 ncp_conn_putprochandles(struct thread *td)
594 {
595 struct ncp_handle *hp, *nhp;
596 int haveone = 0;
597
598 lockmgr(&lhlock, LK_EXCLUSIVE, 0, td);
599 for (hp = SLIST_FIRST(&lhlist); hp; hp = nhp) {
600 nhp = SLIST_NEXT(hp, nh_next);
601 if (hp->nh_td != td) continue;
602 haveone = 1;
603 hp->nh_conn->ref_cnt -= hp->nh_ref;
604 SLIST_REMOVE(&lhlist, hp, ncp_handle, nh_next);
605 FREE(hp, M_NCPDATA);
606 }
607 lockmgr(&lhlock, LK_RELEASE, 0, td);
608 return haveone;
609 }
610 /*
611 * remove references in all possible connections,
612 * XXX - possible problem is a locked list.
613 */
614 /*void
615 ncp_conn_list_rm_ref(pid_t pid) {
616 struct ncp_conn *ncp;
617
618 ncp_conn_locklist(LK_SHARED, NULL);
619 SLIST_FOREACH(ncp, &conn_list, nc_next) {
620 ncp_conn_rm_ref(ncp,pid,1);
621 }
622 ncp_conn_unlocklist(NULL);
623 return;
624 }
625 */
626 int
627 ncp_conn_getinfo(struct ncp_conn *ncp, struct ncp_conn_stat *ncs) {
628 bzero(ncs,sizeof(*ncs));
629 ncs->li = ncp->li;
630 ncs->li.user = ncs->user;
631 if (ncp->li.user)
632 strcpy(ncs->user, ncp->li.user);
633 ncs->li.password = NULL;
634 ncs->connRef = ncp->nc_id;
635 ncs->ref_cnt = ncp->ref_cnt;
636 ncs->connid = ncp->connid;
637 ncs->owner = ncp->nc_owner->cr_uid;
638 ncs->group = ncp->nc_group;
639 ncs->flags = ncp->flags;
640 ncs->buffer_size = ncp->buffer_size;
641 return 0;
642 }
643
644 static int
645 ncp_sysctl_connstat(SYSCTL_HANDLER_ARGS)
646 {
647 int error;
648 struct ncp_conn_stat ncs;
649 struct ncp_conn *ncp;
650 /* struct ucred *cred = req->td->td_ucred;*/
651
652 error = sysctl_wire_old_buffer(req, 0);
653 if (error != 0)
654 return (error);
655 ncp_conn_locklist(LK_SHARED, req->td);
656 error = SYSCTL_OUT(req, &ncp_conn_cnt, sizeof(ncp_conn_cnt));
657 SLIST_FOREACH(ncp, &conn_list, nc_next) {
658 if (error) break;
659 /* I can't do conn_lock while list is locked */
660 ncp->nc_lwant++;
661 ncp_conn_getinfo(ncp, &ncs);
662 ncp->nc_lwant--;
663 error = SYSCTL_OUT(req, &ncs, sizeof(ncs));
664 }
665 ncp_conn_unlocklist(req->td);
666 return(error);
667 }
Cache object: c030389c7d2f28378962087d2a8e817f
|