17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5facf4a8dSllai1 * Common Development and Distribution License (the "License"). 6facf4a8dSllai1 * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*a204de77Scth * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate /* 297c478bd9Sstevel@tonic-gate * This file contains global data and code shared between master and slave parts 307c478bd9Sstevel@tonic-gate * of the pseudo-terminal driver. 317c478bd9Sstevel@tonic-gate * 327c478bd9Sstevel@tonic-gate * Pseudo terminals (or pt's for short) are allocated dynamically. 337c478bd9Sstevel@tonic-gate * pt's are put in the global ptms_slots array indexed by minor numbers. 347c478bd9Sstevel@tonic-gate * 357c478bd9Sstevel@tonic-gate * The slots array is initially small (of the size NPTY_MIN). When more pt's are 367c478bd9Sstevel@tonic-gate * needed than the slot array size, the larger slot array is allocated and all 377c478bd9Sstevel@tonic-gate * opened pt's move to the new one. 387c478bd9Sstevel@tonic-gate * 397c478bd9Sstevel@tonic-gate * Resource allocation: 407c478bd9Sstevel@tonic-gate * 417c478bd9Sstevel@tonic-gate * pt_ttys structures are allocated via pt_ttys_alloc, which uses 427c478bd9Sstevel@tonic-gate * kmem_cache_alloc(). 437c478bd9Sstevel@tonic-gate * Minor number space is allocated via vmem_alloc() interface. 447c478bd9Sstevel@tonic-gate * ptms_slots arrays are allocated via kmem_alloc(). 457c478bd9Sstevel@tonic-gate * 467c478bd9Sstevel@tonic-gate * Minors are started from 1 instead of 0 because vmem_alloc returns 0 in case 477c478bd9Sstevel@tonic-gate * of failure. Also, in anticipation of removing clone device interface to 487c478bd9Sstevel@tonic-gate * pseudo-terminal subsystem, minor 0 should not be used. (Potential future 497c478bd9Sstevel@tonic-gate * development). 507c478bd9Sstevel@tonic-gate * 517c478bd9Sstevel@tonic-gate * After the table slot size reaches pt_maxdelta, we stop 2^N extension 527c478bd9Sstevel@tonic-gate * algorithm and start extending the slot table size by pt_maxdelta. 537c478bd9Sstevel@tonic-gate * 54facf4a8dSllai1 * Device entries /dev/pts directory are created dynamically by the 55facf4a8dSllai1 * /dev filesystem. We no longer call ddi_create_minor_node() on 56facf4a8dSllai1 * behalf of the slave driver. The /dev filesystem creates /dev/pts 57facf4a8dSllai1 * nodes based on the pt_ttys array. 58facf4a8dSllai1 * 597c478bd9Sstevel@tonic-gate * Synchronization: 607c478bd9Sstevel@tonic-gate * 617c478bd9Sstevel@tonic-gate * All global data synchronization between ptm/pts is done via global 627c478bd9Sstevel@tonic-gate * ptms_lock mutex which is implicitly initialized by declaring it global. 637c478bd9Sstevel@tonic-gate * 647c478bd9Sstevel@tonic-gate * Individual fields of pt_ttys structure (except ptm_rdq, pts_rdq and 657c478bd9Sstevel@tonic-gate * pt_nullmsg) are protected by pt_ttys.pt_lock mutex. 667c478bd9Sstevel@tonic-gate * 677c478bd9Sstevel@tonic-gate * PT_ENTER_READ/PT_ENTER_WRITE are reference counter based read-write locks 687c478bd9Sstevel@tonic-gate * which allow reader locks to be reacquired by the same thread (usual 697c478bd9Sstevel@tonic-gate * reader/writer locks can't be used for that purpose since it is illegal for 707c478bd9Sstevel@tonic-gate * a thread to acquire a lock it already holds, even as a reader). The sole 717c478bd9Sstevel@tonic-gate * purpose of these macros is to guarantee that the peer queue will not 727c478bd9Sstevel@tonic-gate * disappear (due to closing peer) while it is used. It is safe to use 737c478bd9Sstevel@tonic-gate * PT_ENTER_READ/PT_EXIT_READ brackets across calls like putq/putnext (since 747c478bd9Sstevel@tonic-gate * they are not real locks but reference counts). 757c478bd9Sstevel@tonic-gate * 767c478bd9Sstevel@tonic-gate * PT_ENTER_WRITE/PT_EXIT_WRITE brackets are used ONLY in master/slave 777c478bd9Sstevel@tonic-gate * open/close paths to modify ptm_rdq and pts_rdq fields. These fields should 787c478bd9Sstevel@tonic-gate * be set to appropriate queues *after* qprocson() is called during open (to 797c478bd9Sstevel@tonic-gate * prevent peer from accessing the queue with incomplete plumbing) and set to 807c478bd9Sstevel@tonic-gate * NULL before qprocsoff() is called during close. Put and service procedures 817c478bd9Sstevel@tonic-gate * use PT_ENTER_READ/PT_EXIT_READ to prevent peer closes. 827c478bd9Sstevel@tonic-gate * 837c478bd9Sstevel@tonic-gate * The pt_nullmsg field is only used in open/close routines and is also 847c478bd9Sstevel@tonic-gate * protected by PT_ENTER_WRITE/PT_EXIT_WRITE brackets to avoid extra mutex 857c478bd9Sstevel@tonic-gate * holds. 867c478bd9Sstevel@tonic-gate * 877c478bd9Sstevel@tonic-gate * Lock Ordering: 887c478bd9Sstevel@tonic-gate * 897c478bd9Sstevel@tonic-gate * If both ptms_lock and per-pty lock should be held, ptms_lock should always 907c478bd9Sstevel@tonic-gate * be entered first, followed by per-pty lock. 917c478bd9Sstevel@tonic-gate * 927c478bd9Sstevel@tonic-gate * Global functions: 937c478bd9Sstevel@tonic-gate * 947c478bd9Sstevel@tonic-gate * void ptms_init(void); 957c478bd9Sstevel@tonic-gate * 967c478bd9Sstevel@tonic-gate * Called by pts/ptm _init entry points. It performes one-time 977c478bd9Sstevel@tonic-gate * initialization needed for both pts and ptm. This initialization is done 987c478bd9Sstevel@tonic-gate * here and not in ptms_initspace because all these data structures are not 997c478bd9Sstevel@tonic-gate * needed if pseudo-terminals are not used in the system. 1007c478bd9Sstevel@tonic-gate * 1017c478bd9Sstevel@tonic-gate * struct pt_ttys *pt_ttys_alloc(void); 1027c478bd9Sstevel@tonic-gate * 1037c478bd9Sstevel@tonic-gate * Allocate new minor number and pseudo-terminal entry. May sleep. 1047c478bd9Sstevel@tonic-gate * New minor number is recorded in pt_minor field of the entry returned. 1057c478bd9Sstevel@tonic-gate * This routine also initializes pt_minor and pt_state fields of the new 1067c478bd9Sstevel@tonic-gate * pseudo-terminal and puts a pointer to it into ptms_slots array. 1077c478bd9Sstevel@tonic-gate * 1087c478bd9Sstevel@tonic-gate * struct pt_ttys *ptms_minor2ptty(minor_t minor) 1097c478bd9Sstevel@tonic-gate * 1107c478bd9Sstevel@tonic-gate * Find pt_ttys structure by minor number. 1117c478bd9Sstevel@tonic-gate * Returns NULL when minor is out of range. 1127c478bd9Sstevel@tonic-gate * 113facf4a8dSllai1 * int ptms_minor_valid(minor_t minor, uid_t *ruid, gid_t *rgid) 114facf4a8dSllai1 * 115facf4a8dSllai1 * Check if minor refers to an allocated pty in the current zone. 116facf4a8dSllai1 * Returns 117facf4a8dSllai1 * 0 if not allocated or not for this zone. 118facf4a8dSllai1 * 1 if an allocated pty in the current zone. 119facf4a8dSllai1 * Also returns owner of pty. 120facf4a8dSllai1 * 121facf4a8dSllai1 * int ptms_minor_exists(minor_t minor) 122facf4a8dSllai1 * Check if minor refers to an allocated pty (in any zone) 123facf4a8dSllai1 * Returns 124facf4a8dSllai1 * 0 if not an allocated pty 125facf4a8dSllai1 * 1 if an allocated pty 126facf4a8dSllai1 * 127facf4a8dSllai1 * void ptms_set_owner(minor_t minor, uid_t ruid, gid_t rgid) 128facf4a8dSllai1 * 129facf4a8dSllai1 * Sets the owner associated with a pty. 130facf4a8dSllai1 * 1317c478bd9Sstevel@tonic-gate * void ptms_close(struct pt_ttys *pt, uint_t flags_to_clear); 1327c478bd9Sstevel@tonic-gate * 1337c478bd9Sstevel@tonic-gate * Clear flags_to_clear in pt and if no one owns it (PTMOPEN/PTSOPEN not 1347c478bd9Sstevel@tonic-gate * set) free pt entry and corresponding slot. 1357c478bd9Sstevel@tonic-gate * 1367c478bd9Sstevel@tonic-gate * Tuneables and configuration: 1377c478bd9Sstevel@tonic-gate * 1387c478bd9Sstevel@tonic-gate * pt_cnt: minimum number of pseudo-terminals in the system. The system 1397c478bd9Sstevel@tonic-gate * should provide at least this number of ptys (provided sufficient 1407c478bd9Sstevel@tonic-gate * memory is available). It is different from the older semantics 1417c478bd9Sstevel@tonic-gate * of pt_cnt meaning maximum number of ptys. 1427c478bd9Sstevel@tonic-gate * Set to 0 by default. 1437c478bd9Sstevel@tonic-gate * 1447c478bd9Sstevel@tonic-gate * pt_max_pty: Maximum number of pseudo-terminals in the system. The system 1457c478bd9Sstevel@tonic-gate * should not allocate more ptys than pt_max_pty (although, it may 1467c478bd9Sstevel@tonic-gate * impose stricter maximum). Zero value means no user-defined 1477c478bd9Sstevel@tonic-gate * maximum. This is intended to be used as "denial-of-service" 1487c478bd9Sstevel@tonic-gate * protection. 1497c478bd9Sstevel@tonic-gate * Set to 0 by default. 1507c478bd9Sstevel@tonic-gate * 1517c478bd9Sstevel@tonic-gate * Both pt_cnt and pt_max_pty may be modified during system lifetime 1527c478bd9Sstevel@tonic-gate * with their semantics preserved. 1537c478bd9Sstevel@tonic-gate * 1547c478bd9Sstevel@tonic-gate * pt_init_cnt: Initial size of ptms_slots array. Set to NPTY_INITIAL. 1557c478bd9Sstevel@tonic-gate * 1567c478bd9Sstevel@tonic-gate * pt_ptyofmem: Approximate percentage of system memory that may be 1577c478bd9Sstevel@tonic-gate * occupied by pty data structures. Initially set to NPTY_PERCENT. 1587c478bd9Sstevel@tonic-gate * This variable is used once during initialization to estimate 1597c478bd9Sstevel@tonic-gate * maximum number of ptys in the system. The actual maximum is 1607c478bd9Sstevel@tonic-gate * determined as minimum of pt_max_pty and calculated value. 1617c478bd9Sstevel@tonic-gate * 1627c478bd9Sstevel@tonic-gate * pt_maxdelta: Maximum extension chunk of the slot table. 1637c478bd9Sstevel@tonic-gate */ 1647c478bd9Sstevel@tonic-gate 1657c478bd9Sstevel@tonic-gate 1667c478bd9Sstevel@tonic-gate 1677c478bd9Sstevel@tonic-gate #include <sys/types.h> 1687c478bd9Sstevel@tonic-gate #include <sys/param.h> 1697c478bd9Sstevel@tonic-gate #include <sys/termios.h> 1707c478bd9Sstevel@tonic-gate #include <sys/stream.h> 1717c478bd9Sstevel@tonic-gate #include <sys/stropts.h> 1727c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 1737c478bd9Sstevel@tonic-gate #include <sys/ptms.h> 1747c478bd9Sstevel@tonic-gate #include <sys/stat.h> 1757c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 1767c478bd9Sstevel@tonic-gate #include <sys/ddi.h> 1777c478bd9Sstevel@tonic-gate #include <sys/bitmap.h> 1787c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 1797c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 1807c478bd9Sstevel@tonic-gate #include <sys/zone.h> 1817c478bd9Sstevel@tonic-gate #ifdef DEBUG 1827c478bd9Sstevel@tonic-gate #include <sys/strlog.h> 1837c478bd9Sstevel@tonic-gate #endif 1847c478bd9Sstevel@tonic-gate 1857c478bd9Sstevel@tonic-gate 1867c478bd9Sstevel@tonic-gate /* Initial number of ptms slots */ 1877c478bd9Sstevel@tonic-gate #define NPTY_INITIAL 16 1887c478bd9Sstevel@tonic-gate 1897c478bd9Sstevel@tonic-gate #define NPTY_PERCENT 5 1907c478bd9Sstevel@tonic-gate 1917c478bd9Sstevel@tonic-gate /* Maximum increment of the slot table size */ 1927c478bd9Sstevel@tonic-gate #define PTY_MAXDELTA 128 1937c478bd9Sstevel@tonic-gate 1947c478bd9Sstevel@tonic-gate /* 1957c478bd9Sstevel@tonic-gate * Tuneable variables. 1967c478bd9Sstevel@tonic-gate */ 1977c478bd9Sstevel@tonic-gate uint_t pt_cnt = 0; /* Minimum number of ptys */ 1987c478bd9Sstevel@tonic-gate size_t pt_max_pty = 0; /* Maximum number of ptys */ 1997c478bd9Sstevel@tonic-gate uint_t pt_init_cnt = NPTY_INITIAL; /* Initial number of ptms slots */ 2007c478bd9Sstevel@tonic-gate uint_t pt_pctofmem = NPTY_PERCENT; /* Percent of memory to use for ptys */ 2017c478bd9Sstevel@tonic-gate uint_t pt_maxdelta = PTY_MAXDELTA; /* Max increment for slot table size */ 2027c478bd9Sstevel@tonic-gate 2037c478bd9Sstevel@tonic-gate /* Other global variables */ 2047c478bd9Sstevel@tonic-gate 2057c478bd9Sstevel@tonic-gate kmutex_t ptms_lock; /* Global data access lock */ 2067c478bd9Sstevel@tonic-gate 2077c478bd9Sstevel@tonic-gate /* 2087c478bd9Sstevel@tonic-gate * Slot array and its management variables 2097c478bd9Sstevel@tonic-gate */ 2107c478bd9Sstevel@tonic-gate static struct pt_ttys **ptms_slots = NULL; /* Slots for actual pt structures */ 2117c478bd9Sstevel@tonic-gate static size_t ptms_nslots = 0; /* Size of slot array */ 2127c478bd9Sstevel@tonic-gate static size_t ptms_ptymax = 0; /* Maximum number of ptys */ 2137c478bd9Sstevel@tonic-gate static size_t ptms_inuse = 0; /* # of ptys currently allocated */ 2147c478bd9Sstevel@tonic-gate 215facf4a8dSllai1 dev_info_t *pts_dip = NULL; /* set if slave is attached */ 2167c478bd9Sstevel@tonic-gate 2177c478bd9Sstevel@tonic-gate static struct kmem_cache *ptms_cache = NULL; /* pty cache */ 2187c478bd9Sstevel@tonic-gate 2197c478bd9Sstevel@tonic-gate static vmem_t *ptms_minor_arena = NULL; /* Arena for device minors */ 2207c478bd9Sstevel@tonic-gate 2217c478bd9Sstevel@tonic-gate static uint_t ptms_roundup(uint_t); 2227c478bd9Sstevel@tonic-gate static int ptms_constructor(void *, void *, int); 2237c478bd9Sstevel@tonic-gate static void ptms_destructor(void *, void *); 2247c478bd9Sstevel@tonic-gate static minor_t ptms_grow(void); 2257c478bd9Sstevel@tonic-gate 2267c478bd9Sstevel@tonic-gate /* 2277c478bd9Sstevel@tonic-gate * Total size occupied by one pty. Each pty master/slave pair consumes one 2287c478bd9Sstevel@tonic-gate * pointer for ptms_slots array, one pt_ttys structure and one empty message 2297c478bd9Sstevel@tonic-gate * preallocated for pts close. 2307c478bd9Sstevel@tonic-gate */ 2317c478bd9Sstevel@tonic-gate 2327c478bd9Sstevel@tonic-gate #define PTY_SIZE (sizeof (struct pt_ttys) + \ 2337c478bd9Sstevel@tonic-gate sizeof (struct pt_ttys *) + \ 2347c478bd9Sstevel@tonic-gate sizeof (dblk_t)) 2357c478bd9Sstevel@tonic-gate 2367c478bd9Sstevel@tonic-gate #ifdef DEBUG 2377c478bd9Sstevel@tonic-gate int ptms_debug = 0; 2387c478bd9Sstevel@tonic-gate #define PTMOD_ID 5 2397c478bd9Sstevel@tonic-gate #endif 2407c478bd9Sstevel@tonic-gate 2417c478bd9Sstevel@tonic-gate /* 2427c478bd9Sstevel@tonic-gate * Clear all bits of x except the highest bit 2437c478bd9Sstevel@tonic-gate */ 2447c478bd9Sstevel@tonic-gate #define truncate(x) ((x) <= 2 ? (x) : (1 << (highbit(x) - 1))) 2457c478bd9Sstevel@tonic-gate 2467c478bd9Sstevel@tonic-gate /* 2477c478bd9Sstevel@tonic-gate * Roundup the number to the nearest power of 2 2487c478bd9Sstevel@tonic-gate */ 2497c478bd9Sstevel@tonic-gate static uint_t 2507c478bd9Sstevel@tonic-gate ptms_roundup(uint_t x) 2517c478bd9Sstevel@tonic-gate { 2527c478bd9Sstevel@tonic-gate uint_t p = truncate(x); /* x with non-high bits stripped */ 2537c478bd9Sstevel@tonic-gate 2547c478bd9Sstevel@tonic-gate /* 2557c478bd9Sstevel@tonic-gate * If x is a power of 2, return x, otherwise roundup. 2567c478bd9Sstevel@tonic-gate */ 2577c478bd9Sstevel@tonic-gate return (p == x ? p : (p * 2)); 2587c478bd9Sstevel@tonic-gate } 2597c478bd9Sstevel@tonic-gate 2607c478bd9Sstevel@tonic-gate /* 2617c478bd9Sstevel@tonic-gate * Allocate ptms_slots array and kmem cache for pt_ttys. This initialization is 2627c478bd9Sstevel@tonic-gate * only called once during system lifetime. Called from ptm or pts _init 2637c478bd9Sstevel@tonic-gate * routine. 2647c478bd9Sstevel@tonic-gate */ 2657c478bd9Sstevel@tonic-gate void 2667c478bd9Sstevel@tonic-gate ptms_init(void) 2677c478bd9Sstevel@tonic-gate { 2687c478bd9Sstevel@tonic-gate mutex_enter(&ptms_lock); 2697c478bd9Sstevel@tonic-gate 2707c478bd9Sstevel@tonic-gate if (ptms_slots == NULL) { 2717c478bd9Sstevel@tonic-gate ptms_slots = kmem_zalloc(pt_init_cnt * 2727c478bd9Sstevel@tonic-gate sizeof (struct pt_ttys *), KM_SLEEP); 2737c478bd9Sstevel@tonic-gate 2747c478bd9Sstevel@tonic-gate ptms_cache = kmem_cache_create("pty_map", 2757c478bd9Sstevel@tonic-gate sizeof (struct pt_ttys), 0, ptms_constructor, 2767c478bd9Sstevel@tonic-gate ptms_destructor, NULL, NULL, NULL, 0); 2777c478bd9Sstevel@tonic-gate 2787c478bd9Sstevel@tonic-gate ptms_nslots = pt_init_cnt; 2797c478bd9Sstevel@tonic-gate 2807c478bd9Sstevel@tonic-gate /* Allocate integer space for minor numbers */ 2817c478bd9Sstevel@tonic-gate ptms_minor_arena = vmem_create("ptms_minor", (void *)1, 2827c478bd9Sstevel@tonic-gate ptms_nslots, 1, NULL, NULL, NULL, 0, 2837c478bd9Sstevel@tonic-gate VM_SLEEP | VMC_IDENTIFIER); 2847c478bd9Sstevel@tonic-gate 2857c478bd9Sstevel@tonic-gate /* 2867c478bd9Sstevel@tonic-gate * Calculate available number of ptys - how many ptys can we 2877c478bd9Sstevel@tonic-gate * allocate in pt_pctofmem % of available memory. The value is 2887c478bd9Sstevel@tonic-gate * rounded up to the nearest power of 2. 2897c478bd9Sstevel@tonic-gate */ 2907c478bd9Sstevel@tonic-gate ptms_ptymax = ptms_roundup((pt_pctofmem * kmem_maxavail()) / 2917c478bd9Sstevel@tonic-gate (100 * PTY_SIZE)); 2927c478bd9Sstevel@tonic-gate } 2937c478bd9Sstevel@tonic-gate mutex_exit(&ptms_lock); 2947c478bd9Sstevel@tonic-gate } 2957c478bd9Sstevel@tonic-gate 2967c478bd9Sstevel@tonic-gate /* 297facf4a8dSllai1 * This routine attaches the pts dip. 2987c478bd9Sstevel@tonic-gate */ 2997c478bd9Sstevel@tonic-gate int 300facf4a8dSllai1 ptms_attach_slave(void) 3017c478bd9Sstevel@tonic-gate { 302facf4a8dSllai1 if (pts_dip == NULL && i_ddi_attach_pseudo_node("pts") == NULL) 303facf4a8dSllai1 return (-1); 3047c478bd9Sstevel@tonic-gate 305facf4a8dSllai1 ASSERT(pts_dip); 306facf4a8dSllai1 return (0); 3077c478bd9Sstevel@tonic-gate } 3087c478bd9Sstevel@tonic-gate 3097c478bd9Sstevel@tonic-gate /* 310facf4a8dSllai1 * Called from /dev fs. Checks if dip is attached, 311facf4a8dSllai1 * and if it is, returns its major number. 3127c478bd9Sstevel@tonic-gate */ 313facf4a8dSllai1 major_t 314facf4a8dSllai1 ptms_slave_attached(void) 3157c478bd9Sstevel@tonic-gate { 316*a204de77Scth major_t maj = DDI_MAJOR_T_NONE; 317facf4a8dSllai1 3187c478bd9Sstevel@tonic-gate mutex_enter(&ptms_lock); 319facf4a8dSllai1 if (pts_dip) 320facf4a8dSllai1 maj = ddi_driver_major(pts_dip); 3217c478bd9Sstevel@tonic-gate mutex_exit(&ptms_lock); 322facf4a8dSllai1 323facf4a8dSllai1 return (maj); 3247c478bd9Sstevel@tonic-gate } 3257c478bd9Sstevel@tonic-gate 3267c478bd9Sstevel@tonic-gate /* 3277c478bd9Sstevel@tonic-gate * Allocate new minor number and pseudo-terminal entry. Returns the new entry or 3287c478bd9Sstevel@tonic-gate * NULL if no memory or maximum number of entries reached. 3297c478bd9Sstevel@tonic-gate */ 3307c478bd9Sstevel@tonic-gate struct pt_ttys * 3317c478bd9Sstevel@tonic-gate pt_ttys_alloc(void) 3327c478bd9Sstevel@tonic-gate { 3337c478bd9Sstevel@tonic-gate minor_t dminor; 3347c478bd9Sstevel@tonic-gate struct pt_ttys *pt = NULL; 3357c478bd9Sstevel@tonic-gate 3367c478bd9Sstevel@tonic-gate mutex_enter(&ptms_lock); 3377c478bd9Sstevel@tonic-gate 3387c478bd9Sstevel@tonic-gate /* 3397c478bd9Sstevel@tonic-gate * Always try to allocate new pty when pt_cnt minimum limit is not 3407c478bd9Sstevel@tonic-gate * achieved. If it is achieved, the maximum is determined by either 3417c478bd9Sstevel@tonic-gate * user-specified value (if it is non-zero) or our memory estimations - 3427c478bd9Sstevel@tonic-gate * whatever is less. 3437c478bd9Sstevel@tonic-gate */ 3447c478bd9Sstevel@tonic-gate if (ptms_inuse >= pt_cnt) { 3457c478bd9Sstevel@tonic-gate /* 3467c478bd9Sstevel@tonic-gate * When system achieved required minimum of ptys, check for the 3477c478bd9Sstevel@tonic-gate * denial of service limits. 3487c478bd9Sstevel@tonic-gate * 3497c478bd9Sstevel@tonic-gate * Since pt_max_pty may be zero, the formula below is used to 3507c478bd9Sstevel@tonic-gate * avoid conditional expression. It will equal to pt_max_pty if 3517c478bd9Sstevel@tonic-gate * it is not zero and ptms_ptymax otherwise. 3527c478bd9Sstevel@tonic-gate */ 3537c478bd9Sstevel@tonic-gate size_t user_max = (pt_max_pty == 0 ? ptms_ptymax : pt_max_pty); 3547c478bd9Sstevel@tonic-gate 3557c478bd9Sstevel@tonic-gate /* Do not try to allocate more than allowed */ 3567c478bd9Sstevel@tonic-gate if (ptms_inuse >= min(ptms_ptymax, user_max)) { 3577c478bd9Sstevel@tonic-gate mutex_exit(&ptms_lock); 3587c478bd9Sstevel@tonic-gate return (NULL); 3597c478bd9Sstevel@tonic-gate } 3607c478bd9Sstevel@tonic-gate } 3617c478bd9Sstevel@tonic-gate ptms_inuse++; 3627c478bd9Sstevel@tonic-gate 3637c478bd9Sstevel@tonic-gate /* 3647c478bd9Sstevel@tonic-gate * Allocate new minor number. If this fails, all slots are busy and 3657c478bd9Sstevel@tonic-gate * we need to grow the hash. 3667c478bd9Sstevel@tonic-gate */ 3677c478bd9Sstevel@tonic-gate dminor = (minor_t)(uintptr_t) 3687c478bd9Sstevel@tonic-gate vmem_alloc(ptms_minor_arena, 1, VM_NOSLEEP); 3697c478bd9Sstevel@tonic-gate 3707c478bd9Sstevel@tonic-gate if (dminor == 0) { 3717c478bd9Sstevel@tonic-gate /* Grow the cache and retry allocation */ 3727c478bd9Sstevel@tonic-gate dminor = ptms_grow(); 3737c478bd9Sstevel@tonic-gate } 3747c478bd9Sstevel@tonic-gate 3757c478bd9Sstevel@tonic-gate if (dminor == 0) { 3767c478bd9Sstevel@tonic-gate /* Not enough memory now */ 3777c478bd9Sstevel@tonic-gate ptms_inuse--; 3787c478bd9Sstevel@tonic-gate mutex_exit(&ptms_lock); 3797c478bd9Sstevel@tonic-gate return (NULL); 3807c478bd9Sstevel@tonic-gate } 3817c478bd9Sstevel@tonic-gate 3827c478bd9Sstevel@tonic-gate pt = kmem_cache_alloc(ptms_cache, KM_NOSLEEP); 3837c478bd9Sstevel@tonic-gate if (pt == NULL) { 3847c478bd9Sstevel@tonic-gate /* Not enough memory - this entry can't be used now. */ 3857c478bd9Sstevel@tonic-gate vmem_free(ptms_minor_arena, (void *)(uintptr_t)dminor, 1); 3867c478bd9Sstevel@tonic-gate ptms_inuse--; 3877c478bd9Sstevel@tonic-gate } else { 3887c478bd9Sstevel@tonic-gate pt->pt_minor = dminor; 3897c478bd9Sstevel@tonic-gate pt->pt_pid = curproc->p_pid; /* For debugging */ 3907c478bd9Sstevel@tonic-gate pt->pt_state = (PTMOPEN | PTLOCK); 3917c478bd9Sstevel@tonic-gate pt->pt_zoneid = getzoneid(); 392facf4a8dSllai1 pt->pt_ruid = 0; /* we don't know uid/gid yet. Report as root */ 393facf4a8dSllai1 pt->pt_rgid = 0; 3947c478bd9Sstevel@tonic-gate ASSERT(ptms_slots[dminor - 1] == NULL); 3957c478bd9Sstevel@tonic-gate ptms_slots[dminor - 1] = pt; 3967c478bd9Sstevel@tonic-gate } 3977c478bd9Sstevel@tonic-gate 3987c478bd9Sstevel@tonic-gate mutex_exit(&ptms_lock); 3997c478bd9Sstevel@tonic-gate return (pt); 4007c478bd9Sstevel@tonic-gate } 4017c478bd9Sstevel@tonic-gate 4027c478bd9Sstevel@tonic-gate /* 4037c478bd9Sstevel@tonic-gate * Get pt_ttys structure by minor number. 4047c478bd9Sstevel@tonic-gate * Returns NULL when minor is out of range. 4057c478bd9Sstevel@tonic-gate */ 4067c478bd9Sstevel@tonic-gate struct pt_ttys * 4077c478bd9Sstevel@tonic-gate ptms_minor2ptty(minor_t dminor) 4087c478bd9Sstevel@tonic-gate { 4097c478bd9Sstevel@tonic-gate struct pt_ttys *pt = NULL; 4107c478bd9Sstevel@tonic-gate 4117c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&ptms_lock)); 4127c478bd9Sstevel@tonic-gate if ((dminor >= 1) && (dminor <= ptms_nslots) && ptms_slots != NULL) 4137c478bd9Sstevel@tonic-gate pt = ptms_slots[dminor - 1]; 4147c478bd9Sstevel@tonic-gate 4157c478bd9Sstevel@tonic-gate return (pt); 4167c478bd9Sstevel@tonic-gate } 4177c478bd9Sstevel@tonic-gate 4187c478bd9Sstevel@tonic-gate /* 419facf4a8dSllai1 * Invoked in response to chown on /dev/pts nodes to change the 420facf4a8dSllai1 * permission on a pty 421facf4a8dSllai1 */ 422facf4a8dSllai1 void 423facf4a8dSllai1 ptms_set_owner(minor_t dminor, uid_t ruid, gid_t rgid) 424facf4a8dSllai1 { 425facf4a8dSllai1 struct pt_ttys *pt; 426facf4a8dSllai1 427facf4a8dSllai1 ASSERT(ruid >= 0); 428facf4a8dSllai1 ASSERT(rgid >= 0); 429facf4a8dSllai1 430facf4a8dSllai1 if (ruid < 0 || rgid < 0) 431facf4a8dSllai1 return; 432facf4a8dSllai1 433facf4a8dSllai1 /* 434facf4a8dSllai1 * /dev/pts/0 is not used, but some applications may check it. There 435facf4a8dSllai1 * is no pty backing it - so we have nothing to do. 436facf4a8dSllai1 */ 437facf4a8dSllai1 if (dminor == 0) 438facf4a8dSllai1 return; 439facf4a8dSllai1 440facf4a8dSllai1 mutex_enter(&ptms_lock); 441facf4a8dSllai1 pt = ptms_minor2ptty(dminor); 442facf4a8dSllai1 if (pt != NULL && pt->pt_zoneid == getzoneid()) { 443facf4a8dSllai1 pt->pt_ruid = ruid; 444facf4a8dSllai1 pt->pt_rgid = rgid; 445facf4a8dSllai1 } 446facf4a8dSllai1 mutex_exit(&ptms_lock); 447facf4a8dSllai1 } 448facf4a8dSllai1 449facf4a8dSllai1 /* 450facf4a8dSllai1 * Given a ptm/pts minor number 451facf4a8dSllai1 * returns: 452facf4a8dSllai1 * 1 if the pty is allocated to the current zone. 453facf4a8dSllai1 * 0 otherwise 454facf4a8dSllai1 * 455facf4a8dSllai1 * If the pty is allocated to the current zone, it also returns the owner. 456facf4a8dSllai1 */ 457facf4a8dSllai1 int 458facf4a8dSllai1 ptms_minor_valid(minor_t dminor, uid_t *ruid, gid_t *rgid) 459facf4a8dSllai1 { 460facf4a8dSllai1 struct pt_ttys *pt; 461facf4a8dSllai1 int ret; 462facf4a8dSllai1 463facf4a8dSllai1 ASSERT(ruid); 464facf4a8dSllai1 ASSERT(rgid); 465facf4a8dSllai1 466f48205beScasper *ruid = (uid_t)-1; 467f48205beScasper *rgid = (gid_t)-1; 468facf4a8dSllai1 469facf4a8dSllai1 /* 470facf4a8dSllai1 * /dev/pts/0 is not used, but some applications may check it, so create 471facf4a8dSllai1 * it also. Report the owner as root. It belongs to all zones. 472facf4a8dSllai1 */ 473facf4a8dSllai1 if (dminor == 0) { 474facf4a8dSllai1 *ruid = 0; 475facf4a8dSllai1 *rgid = 0; 476facf4a8dSllai1 return (1); 477facf4a8dSllai1 } 478facf4a8dSllai1 479facf4a8dSllai1 ret = 0; 480facf4a8dSllai1 mutex_enter(&ptms_lock); 481facf4a8dSllai1 pt = ptms_minor2ptty(dminor); 482facf4a8dSllai1 if (pt != NULL) { 483facf4a8dSllai1 ASSERT(pt->pt_ruid >= 0); 484facf4a8dSllai1 ASSERT(pt->pt_rgid >= 0); 485facf4a8dSllai1 if (pt->pt_zoneid == getzoneid()) { 486facf4a8dSllai1 ret = 1; 487facf4a8dSllai1 *ruid = pt->pt_ruid; 488facf4a8dSllai1 *rgid = pt->pt_rgid; 489facf4a8dSllai1 } 490facf4a8dSllai1 } 491facf4a8dSllai1 mutex_exit(&ptms_lock); 492facf4a8dSllai1 493facf4a8dSllai1 return (ret); 494facf4a8dSllai1 } 495facf4a8dSllai1 496facf4a8dSllai1 /* 497facf4a8dSllai1 * Given a ptm/pts minor number 498facf4a8dSllai1 * returns: 499facf4a8dSllai1 * 0 if the pty is not allocated 500facf4a8dSllai1 * 1 if the pty is allocated 501facf4a8dSllai1 */ 502facf4a8dSllai1 int 503facf4a8dSllai1 ptms_minor_exists(minor_t dminor) 504facf4a8dSllai1 { 505facf4a8dSllai1 int ret; 506facf4a8dSllai1 507facf4a8dSllai1 mutex_enter(&ptms_lock); 508facf4a8dSllai1 ret = ptms_minor2ptty(dminor) ? 1 : 0; 509facf4a8dSllai1 mutex_exit(&ptms_lock); 510facf4a8dSllai1 511facf4a8dSllai1 return (ret); 512facf4a8dSllai1 } 513facf4a8dSllai1 514facf4a8dSllai1 /* 5157c478bd9Sstevel@tonic-gate * Close the pt and clear flags_to_clear. 5167c478bd9Sstevel@tonic-gate * If pt device is not opened by someone else, free it and clear its slot. 5177c478bd9Sstevel@tonic-gate */ 5187c478bd9Sstevel@tonic-gate void 5197c478bd9Sstevel@tonic-gate ptms_close(struct pt_ttys *pt, uint_t flags_to_clear) 5207c478bd9Sstevel@tonic-gate { 5217c478bd9Sstevel@tonic-gate uint_t flags; 5227c478bd9Sstevel@tonic-gate 5237c478bd9Sstevel@tonic-gate ASSERT(MUTEX_NOT_HELD(&ptms_lock)); 5247c478bd9Sstevel@tonic-gate ASSERT(pt != NULL); 5257c478bd9Sstevel@tonic-gate 5267c478bd9Sstevel@tonic-gate mutex_enter(&ptms_lock); 5277c478bd9Sstevel@tonic-gate 5287c478bd9Sstevel@tonic-gate mutex_enter(&pt->pt_lock); 5297c478bd9Sstevel@tonic-gate pt->pt_state &= ~flags_to_clear; 5307c478bd9Sstevel@tonic-gate flags = pt->pt_state; 5317c478bd9Sstevel@tonic-gate mutex_exit(&pt->pt_lock); 5327c478bd9Sstevel@tonic-gate 5337c478bd9Sstevel@tonic-gate if (! (flags & (PTMOPEN | PTSOPEN))) { 5347c478bd9Sstevel@tonic-gate /* No one owns the entry - free it */ 5357c478bd9Sstevel@tonic-gate 5367c478bd9Sstevel@tonic-gate ASSERT(pt->ptm_rdq == NULL); 5377c478bd9Sstevel@tonic-gate ASSERT(pt->pts_rdq == NULL); 5387c478bd9Sstevel@tonic-gate ASSERT(pt->pt_nullmsg == NULL); 5397c478bd9Sstevel@tonic-gate ASSERT(pt->pt_refcnt == 0); 5407c478bd9Sstevel@tonic-gate ASSERT(pt->pt_minor <= ptms_nslots); 5417c478bd9Sstevel@tonic-gate ASSERT(ptms_slots[pt->pt_minor - 1] == pt); 5427c478bd9Sstevel@tonic-gate ASSERT(ptms_inuse > 0); 5437c478bd9Sstevel@tonic-gate 5447c478bd9Sstevel@tonic-gate ptms_inuse--; 5457c478bd9Sstevel@tonic-gate 5467c478bd9Sstevel@tonic-gate pt->pt_pid = 0; 5477c478bd9Sstevel@tonic-gate 5487c478bd9Sstevel@tonic-gate ptms_slots[pt->pt_minor - 1] = NULL; 5497c478bd9Sstevel@tonic-gate /* Return minor number to the pool of minors */ 5507c478bd9Sstevel@tonic-gate vmem_free(ptms_minor_arena, (void *)(uintptr_t)pt->pt_minor, 1); 5517c478bd9Sstevel@tonic-gate /* Return pt to the cache */ 5527c478bd9Sstevel@tonic-gate kmem_cache_free(ptms_cache, pt); 5537c478bd9Sstevel@tonic-gate } 5547c478bd9Sstevel@tonic-gate mutex_exit(&ptms_lock); 5557c478bd9Sstevel@tonic-gate } 5567c478bd9Sstevel@tonic-gate 5577c478bd9Sstevel@tonic-gate /* 5587c478bd9Sstevel@tonic-gate * Allocate another slot table twice as large as the original one (limited to 5597c478bd9Sstevel@tonic-gate * global maximum). Migrate all pt to the new slot table and free the original 5607c478bd9Sstevel@tonic-gate * one. Create more /devices entries for new devices. 5617c478bd9Sstevel@tonic-gate */ 5627c478bd9Sstevel@tonic-gate static minor_t 5637c478bd9Sstevel@tonic-gate ptms_grow() 5647c478bd9Sstevel@tonic-gate { 5657c478bd9Sstevel@tonic-gate minor_t old_size = ptms_nslots; 5667c478bd9Sstevel@tonic-gate minor_t delta = MIN(pt_maxdelta, old_size); 5677c478bd9Sstevel@tonic-gate minor_t new_size = old_size + delta; 5687c478bd9Sstevel@tonic-gate struct pt_ttys **ptms_old = ptms_slots; 5697c478bd9Sstevel@tonic-gate struct pt_ttys **ptms_new; 5707c478bd9Sstevel@tonic-gate void *vaddr; /* vmem_add return value */ 5717c478bd9Sstevel@tonic-gate 5727c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&ptms_lock)); 5737c478bd9Sstevel@tonic-gate 5747c478bd9Sstevel@tonic-gate DDBG("ptmopen(%d): need to grow\n", (int)ptms_inuse); 5757c478bd9Sstevel@tonic-gate 5767c478bd9Sstevel@tonic-gate /* Allocate new ptms array */ 5777c478bd9Sstevel@tonic-gate ptms_new = kmem_zalloc(new_size * sizeof (struct pt_ttys *), 5787c478bd9Sstevel@tonic-gate KM_NOSLEEP); 5797c478bd9Sstevel@tonic-gate if (ptms_new == NULL) 5807c478bd9Sstevel@tonic-gate return ((minor_t)0); 5817c478bd9Sstevel@tonic-gate 5827c478bd9Sstevel@tonic-gate /* Increase clone index space */ 5837c478bd9Sstevel@tonic-gate vaddr = vmem_add(ptms_minor_arena, (void *)(uintptr_t)(old_size + 1), 5847c478bd9Sstevel@tonic-gate new_size - old_size, VM_NOSLEEP); 5857c478bd9Sstevel@tonic-gate 5867c478bd9Sstevel@tonic-gate if (vaddr == NULL) { 5877c478bd9Sstevel@tonic-gate kmem_free(ptms_new, new_size * sizeof (struct pt_ttys *)); 5887c478bd9Sstevel@tonic-gate return ((minor_t)0); 5897c478bd9Sstevel@tonic-gate } 5907c478bd9Sstevel@tonic-gate 5917c478bd9Sstevel@tonic-gate /* Migrate pt entries to a new location */ 5927c478bd9Sstevel@tonic-gate ptms_nslots = new_size; 5937c478bd9Sstevel@tonic-gate bcopy(ptms_old, ptms_new, old_size * sizeof (struct pt_ttys *)); 5947c478bd9Sstevel@tonic-gate ptms_slots = ptms_new; 5957c478bd9Sstevel@tonic-gate kmem_free(ptms_old, old_size * sizeof (struct pt_ttys *)); 5967c478bd9Sstevel@tonic-gate 5977c478bd9Sstevel@tonic-gate /* Allocate minor number and return it */ 5987c478bd9Sstevel@tonic-gate return ((minor_t)(uintptr_t) 5997c478bd9Sstevel@tonic-gate vmem_alloc(ptms_minor_arena, 1, VM_NOSLEEP)); 6007c478bd9Sstevel@tonic-gate } 6017c478bd9Sstevel@tonic-gate 6027c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 6037c478bd9Sstevel@tonic-gate static int 6047c478bd9Sstevel@tonic-gate ptms_constructor(void *maddr, void *arg, int kmflags) 6057c478bd9Sstevel@tonic-gate { 6067c478bd9Sstevel@tonic-gate struct pt_ttys *pt = maddr; 6077c478bd9Sstevel@tonic-gate 6087c478bd9Sstevel@tonic-gate pt->pts_rdq = NULL; 6097c478bd9Sstevel@tonic-gate pt->ptm_rdq = NULL; 6107c478bd9Sstevel@tonic-gate pt->pt_nullmsg = NULL; 6117c478bd9Sstevel@tonic-gate pt->pt_pid = NULL; 6127c478bd9Sstevel@tonic-gate pt->pt_minor = NULL; 6137c478bd9Sstevel@tonic-gate pt->pt_refcnt = 0; 6147c478bd9Sstevel@tonic-gate pt->pt_state = 0; 6157c478bd9Sstevel@tonic-gate pt->pt_zoneid = GLOBAL_ZONEID; 6167c478bd9Sstevel@tonic-gate 6177c478bd9Sstevel@tonic-gate cv_init(&pt->pt_cv, NULL, CV_DEFAULT, NULL); 6187c478bd9Sstevel@tonic-gate mutex_init(&pt->pt_lock, NULL, MUTEX_DEFAULT, NULL); 6197c478bd9Sstevel@tonic-gate return (0); 6207c478bd9Sstevel@tonic-gate } 6217c478bd9Sstevel@tonic-gate 6227c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 6237c478bd9Sstevel@tonic-gate static void 6247c478bd9Sstevel@tonic-gate ptms_destructor(void *maddr, void *arg) 6257c478bd9Sstevel@tonic-gate { 6267c478bd9Sstevel@tonic-gate struct pt_ttys *pt = maddr; 6277c478bd9Sstevel@tonic-gate 6287c478bd9Sstevel@tonic-gate ASSERT(pt->pt_refcnt == 0); 6297c478bd9Sstevel@tonic-gate ASSERT(pt->pt_state == 0); 6307c478bd9Sstevel@tonic-gate ASSERT(pt->ptm_rdq == NULL); 6317c478bd9Sstevel@tonic-gate ASSERT(pt->pts_rdq == NULL); 6327c478bd9Sstevel@tonic-gate 6337c478bd9Sstevel@tonic-gate mutex_destroy(&pt->pt_lock); 6347c478bd9Sstevel@tonic-gate cv_destroy(&pt->pt_cv); 6357c478bd9Sstevel@tonic-gate } 6367c478bd9Sstevel@tonic-gate 6377c478bd9Sstevel@tonic-gate #ifdef DEBUG 6387c478bd9Sstevel@tonic-gate void 6397c478bd9Sstevel@tonic-gate ptms_log(char *str, uint_t arg) 6407c478bd9Sstevel@tonic-gate { 6417c478bd9Sstevel@tonic-gate if (ptms_debug) { 6427c478bd9Sstevel@tonic-gate if (ptms_debug & 2) 6437c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, str, arg); 6447c478bd9Sstevel@tonic-gate if (ptms_debug & 4) 6457c478bd9Sstevel@tonic-gate (void) strlog(PTMOD_ID, -1, 0, SL_TRACE | SL_ERROR, 6467c478bd9Sstevel@tonic-gate str, arg); 6477c478bd9Sstevel@tonic-gate else 6487c478bd9Sstevel@tonic-gate (void) strlog(PTMOD_ID, -1, 0, SL_TRACE, str, arg); 6497c478bd9Sstevel@tonic-gate } 6507c478bd9Sstevel@tonic-gate } 6517c478bd9Sstevel@tonic-gate 6527c478bd9Sstevel@tonic-gate void 6537c478bd9Sstevel@tonic-gate ptms_logp(char *str, uintptr_t arg) 6547c478bd9Sstevel@tonic-gate { 6557c478bd9Sstevel@tonic-gate if (ptms_debug) { 6567c478bd9Sstevel@tonic-gate if (ptms_debug & 2) 6577c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, str, arg); 6587c478bd9Sstevel@tonic-gate if (ptms_debug & 4) 6597c478bd9Sstevel@tonic-gate (void) strlog(PTMOD_ID, -1, 0, SL_TRACE | SL_ERROR, 6607c478bd9Sstevel@tonic-gate str, arg); 6617c478bd9Sstevel@tonic-gate else 6627c478bd9Sstevel@tonic-gate (void) strlog(PTMOD_ID, -1, 0, SL_TRACE, str, arg); 6637c478bd9Sstevel@tonic-gate } 6647c478bd9Sstevel@tonic-gate } 6657c478bd9Sstevel@tonic-gate #endif 666