/* Copyright 1989, 1998 The Open Group Copyright 2000-2005 Oswald Buddenhagen Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the copyright holder. */ /* * xdm - display manager daemon * Author: Keith Packard, MIT X Consortium * * various utility routines */ #include "dm.h" #include "dm_auth.h" #include "dm_error.h" #include #include #include #include #include #if 0 /*def USG; this was hpux once upon a time */ # define NEED_UTSNAME #endif #ifdef NEED_UTSNAME # include #endif void * Calloc( size_t nmemb, size_t size ) { void *ret; if (!(ret = calloc( nmemb, size ))) LogOutOfMem(); return ret; } void * Malloc( size_t size ) { void *ret; if (!(ret = malloc( size ))) LogOutOfMem(); return ret; } void * Realloc( void *ptr, size_t size ) { void *ret; if (!(ret = realloc( ptr, size )) && size) LogOutOfMem(); return ret; } int StrCmp( const char *s1, const char *s2 ) { if (s1 == s2) return 0; if (!s1) return -1; if (!s2) return 1; return strcmp( s1, s2 ); } void WipeStr( char *str ) { if (str) { bzero( str, strlen( str ) ); free( str ); } } #ifndef HAVE_STRNLEN int StrNLen( const char *s, int max ) { unsigned l; for (l = 0; l < (unsigned)max && s[l]; l++); return l; } #endif /* duplicate src; wipe & free old dst string */ int ReStrN( char **dst, const char *src, int len ) { char *ndst = 0; if (src) { if (len < 0) len = strlen( src ); if (*dst && !memcmp( *dst, src, len ) && !(*dst)[len]) return 1; if (!(ndst = Malloc( len + 1 ))) { WipeStr( *dst ); *dst = 0; return 0; } memcpy( ndst, src, len ); ndst[len] = 0; } WipeStr( *dst ); /* make an option, if we should become heavily used */ *dst = ndst; return 2; } int ReStr( char **dst, const char *src ) { return ReStrN( dst, src, -1 ); } /* duplicate src */ int StrNDup( char **dst, const char *src, int len ) { if (src) { if (len < 0) len = strlen( src ); if (!(*dst = Malloc( len + 1 ))) return 0; memcpy( *dst, src, len ); (*dst)[len] = 0; } else *dst = 0; return 1; } int StrDup( char **dst, const char *src ) { return StrNDup( dst, src, -1 ); } /* append any number of strings to dst */ int StrApp( char **dst, ... ) { int len; char *bk, *pt, *dp; va_list va; len = 1; if (*dst) len += strlen( *dst ); va_start( va, dst ); for (;;) { pt = va_arg( va, char * ); if (!pt) break; len += strlen( pt ); } va_end( va ); if (!(bk = Malloc( len ))) { if (*dst) { free( *dst ); *dst = 0; } return 0; } dp = bk; if (*dst) { len = strlen( *dst ); memcpy( dp, *dst, len ); dp += len; free( *dst ); } va_start( va, dst ); for (;;) { pt = va_arg( va, char * ); if (!pt) break; len = strlen( pt ); memcpy( dp, pt, len ); dp += len; } va_end( va ); *dp = '\0'; *dst = bk; return 1; } char ** initStrArr( char **arr ) { if (!arr && (arr = Malloc( sizeof(char *) ))) arr[0] = 0; return arr; } int arrLen( char **arr ) { int nu = 0; if (arr) for (; arr[nu]; nu++); return nu; } static char ** extStrArr( char **arr, char ***strp ) { char **rarr; int nu; nu = arrLen( arr ); if ((rarr = Realloc( arr, sizeof(char *) * (nu + 2) ))) { rarr[nu + 1] = 0; *strp = rarr + nu; return rarr; } freeStrArr( arr ); return 0; } char ** addStrArr( char **arr, const char *str, int len ) { char **strp; if ((arr = extStrArr( arr, &strp ))) { if (StrNDup( strp, str, len )) return arr; freeStrArr( arr ); } return 0; } char ** xCopyStrArr( int rn, char **arr ) { char **rarr; int nu; nu = arrLen( arr ); if ((rarr = Calloc( sizeof(char *), nu + rn + 1 ))) memcpy( rarr + rn, arr, sizeof(char *) * nu ); return rarr; } void freeStrArr( char **arr ) { char **a; if (arr) { for (a = arr; *a; a++) free( *a ); free( arr ); } } char ** parseArgs( char **argv, const char *string ) { const char *word; char **strp, *str; int wlen; if (!(argv = initStrArr( argv ))) return 0; while (*string) { if (isspace( *string )) { string++; continue; } word = string; wlen = 0; do { if (*string == '\\') { if (!*++string) string--; wlen++; } else if (*string == '\'') { while (*++string != '\'' && *string) wlen++; } else if (*string == '"') { while (*++string != '"' && *string) { if (*string == '\\') { if (!*++string) string--; } wlen++; } } else wlen++; } while (*++string && !isspace( *string )); if (!(argv = extStrArr( argv, &strp ))) return 0; if (!(*strp = str = Malloc( wlen + 1 ))) { freeStrArr( argv ); return 0; } do { if (*word == '\\') { if (!*++word) word--; *str++ = *word; } else if (*word == '\'') { while (*++word != '\'' && *word) *str++ = *word; } else if (*word == '"') { while (*++word != '"' && *word) { if (*word == '\\') { if (!*++word) word--; } *str++ = *word; } } else *str++ = *word; } while (*++word && !isspace( *word )); *str = 0; } return argv; } const char * getEnv( char **e, const char *name ) { if (e) { int l = strlen( name ); for (; *e; e++) if (!memcmp( *e, name, l ) && (*e)[l] == '=') return (*e) + l + 1; } return 0; } char ** setEnv( char **e, const char *name, const char *value ) { char **new, **old; char *newe; int envsize; int l; #ifdef _AIX /* setpenv() depends on "SYSENVIRON:", not "SYSENVIRON:=" */ if (!value) { if (!StrDup( &newe, name )) return e; } else #endif { newe = 0; if (!StrApp( &newe, name, "=", value, (char *)0 )) return e; } envsize = 0; if (e) { l = strlen( name ); for (old = e; *old; old++) if (!memcmp( *old, name, l ) && ((*old)[l] == '=' || !(*old)[l])) { free( *old ); *old = newe; return e; } envsize = old - e; } if (!(new = (char **) Realloc( (char *)e, (unsigned)((envsize + 2) * sizeof(char *)) ))) { free( newe ); return e; } new[envsize] = newe; new[envsize + 1] = 0; return new; } char ** putEnv( const char *string, char **env ) { char *n; char *b; if (!(b = strchr( string, '=' ))) return NULL; if (!StrNDup( &n, string, b - string )) return NULL; env = setEnv( env, n, b + 1 ); free( n ); return env; } static int GetHostname( char *buf, int maxlen ) { int len; #ifdef NEED_UTSNAME /* * same host name crock as in server and xinit. */ struct utsname name; uname( &name ); len = strlen( name.nodename ); if (len >= maxlen) len = maxlen - 1; memcpy( buf, name.nodename, len ); buf[len] = '\0'; #else buf[0] = '\0'; (void)gethostname( buf, maxlen ); buf[maxlen - 1] = '\0'; len = strlen( buf ); #endif /* NEED_UTSNAME */ return len; } static char localHostbuf[256]; static int gotLocalHostname; const char * localHostname( void ) { if (!gotLocalHostname) { GetHostname( localHostbuf, sizeof(localHostbuf) - 1 ); gotLocalHostname = 1; } return localHostbuf; } static int AtomicIO( ssize_t (*f)( int, void *, size_t ), int fd, void *buf, int count ) { int ret, rlen; for (rlen = 0; rlen < count; ) { dord: ret = f( fd, (void *)((char *)buf + rlen), count - rlen ); if (ret < 0) { if (errno == EINTR) goto dord; if (errno == EAGAIN) break; return -1; } if (!ret) break; rlen += ret; } return rlen; } int Reader( int fd, void *buf, int count ) { return AtomicIO( read, fd, buf, count ); } int Writer( int fd, const void *buf, int count ) { return AtomicIO( (ssize_t(*)( int, void *, size_t ))write, fd, (void *)buf, count ); } int fGets( char *buf, int max, FILE *f ) { int len; if (!fgets( buf, max, f )) return -1; len = strlen( buf ); if (len && buf[len - 1] == '\n') buf[--len] = 0; return len; } time_t mTime( const char *fn ) { struct stat st; if (stat( fn, &st )) return -1; else return st.st_mtime; } void randomStr( char *s ) { static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; unsigned i, rn = secureRandom(); for (i = 0; i < 6; i++) { *s++ = letters[rn % 62]; rn /= 62; } *s = 0; } static int StrNChrCnt( const char *s, int slen, char c ) { int i, cnt; for (i = cnt = 0; i < slen && s[i]; i++) if (s[i] == c) cnt++; return cnt; } /* X -from ip6-addr does not work here, so i don't know whether this is needed. #define IP6_MAGIC */ void ListSessions( int flags, struct display *d, void *ctx, void (*emitXSess)( struct display *, struct display *, void * ), void (*emitTTYSess)( STRUCTUTMP *, struct display *, void * ) ) { struct display *di; #ifdef IP6_MAGIC int le, dot; #endif #ifdef BSD_UTMP int fd; struct utmp ut[1]; #else STRUCTUTMP *ut; #endif for (di = displays; di; di = di->next) { if (((flags & lstRemote) || (di->displayType & d_location) == dLocal) && (di->status == remoteLogin || ((flags & lstPassive) ? di->status == running : di->userSess >= 0))) { emitXSess(di, d, ctx); } } if (!(flags & lstTTY)) { return; } #ifdef BSD_UTMP if ((fd = open( UTMP_FILE, O_RDONLY )) < 0) { return; } while (Reader( fd, ut, sizeof(ut[0]) ) == sizeof(ut[0])) { if (*ut->ut_user) { /* no idea how to list passive TTYs on BSD */ #else SETUTENT(); while ((ut = GETUTENT())) { if (ut->ut_type == USER_PROCESS # if 0 /* list passive TTYs at all? not too sensible, i think. */ || ((flags & lstPassive) && ut->ut_type == LOGIN_PROCESS) # endif ) { #endif if (*ut->ut_host) { /* from remote or x */ if (!(flags & lstRemote)) { continue; } } else { /* hack around broken konsole which does not set ut_host. */ /* this check is probably linux-specific. */ /* alternatively we could open the device and try VT_OPENQRY. */ if (memcmp( ut->ut_line, "tty", 3 ) || !isdigit( ut->ut_line[3] )) { continue; } } if (StrNChrCnt( ut->ut_line, sizeof(ut->ut_line), ':' )) { continue; /* x login */ } switch (StrNChrCnt( ut->ut_host, sizeof(ut->ut_host), ':' )) { case 1: /* x terminal */ continue; default: #ifdef IP6_MAGIC /* unknown - IPv6 makes things complicated */ le = StrNLen( ut->ut_host, sizeof(ut->ut_host) ); /* cut off screen number */ for (dot = le; ut->ut_host[--dot] != ':'; ) if (ut->ut_host[dot] == '.') { le = dot; break; } for (di = displays; di; di = di->next) if (!memcmp( di->name, ut->ut_host, le ) && !di->name[le]) goto cont; /* x terminal */ break; cont: continue; case 0: /* no x terminal */ #endif break; } emitTTYSess( ut, d, ctx ); } } #ifdef BSD_UTMP close( fd ); #else ENDUTENT(); #endif }