diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 4aed2c8219774f5d797760606b8489a92ddc5163 (patch) | |
tree | 3f8c130f7d269626bf6a9447407ef6c35954426a /kdm | |
download | tdebase-4aed2c8219774f5d797760606b8489a92ddc5163.tar.gz tdebase-4aed2c8219774f5d797760606b8489a92ddc5163.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdebase@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kdm')
151 files changed, 39832 insertions, 0 deletions
diff --git a/kdm/ChangeLog b/kdm/ChangeLog new file mode 100644 index 000000000..0189ab24a --- /dev/null +++ b/kdm/ChangeLog @@ -0,0 +1,540 @@ +This change log contains only changes relevant to the KDM configuration, +startup and packaging. Bug fixes are not listed, and feature changes only +if they affect the configuration. + +2005-08-21 Oswald Buddenhagen <[email protected]> + + * Added timed login. Option AutoLoginDelay. + * Added persistent auto-login. Option AutoLoginAgain. + +2005-02-01 Oswald Buddenhagen <[email protected]> + + * Made the word splitter more sh-like. Affects HaltCmd, RebootCmd, + Setup, Startup, Reset, Session and Xrdb. + * Replaced option Xservers with StaticServers, ReserveServers, + ServerCmd, ServerArgsLocal, ServerArgsRemote, ServerVT and ServerTTY. + +2005-01-31 Oswald Buddenhagen <[email protected]> + + * Added console mode that is suitable for systems with VTs (Linux). + Option ConsoleTTYs. The @tty spec in Xservers is irrelevant on + those systems now. + +2005-01-23 Oswald Buddenhagen <[email protected]> + + * Added Grub support to boot options. Option UseLilo replaced + with BootManager { None, Grub, Lilo }. Removed options LiloCmd + and LiloMap. + +2005-01-09 Oswald Buddenhagen <[email protected]> + + * Merged sessreg into kdm. Option UseSessReg. + +2004-08-14 Oswald Buddenhagen <[email protected]> + + * Replaced dysfunct InteractiveSd with working + ScheduledSd { Never, Optional, Always } + +2004-07-28 Oswald Buddenhagen <[email protected]> + + * Added control sockets. Control FiFos are now obsolete. + Added control socket command line client 'kdmctl'. + * The Setup program is now run even for automatic logins. + Setup, Startup and Reset have the arg "auto" for automatic logins. + +2004-07-23 Oswald Buddenhagen <[email protected]> + + * Dynamic VT allocation added; option ServerVTs; no need to specify + vtX in Xservers any more. + +2004-07-10 Oswald Buddenhagen <[email protected]> + + * GreeterPosX, GreeterPosY and GreeterPosFixed replaced with + single GreeterPos which is a pair of relative coordinates + +2004-07-01 Oswald Buddenhagen <[email protected]> + + * The greeter can now run a "session preloader"; option Preloader + +2004-04-15 Oswald Buddenhagen <[email protected]> + + * Merge from XDM: + - IPv6 support + - LISTEN keyword in Xaccess + - Changes to Enable and Port in [Xdmcp] now take effect when HUPed + - Support for EGD/PRNGD; options PrngdPort and PrngdSocket + +2004-04-14 Oswald Buddenhagen <[email protected]> + + * -debug now groks additional bits for stracing and valgrinding + helper processes and disabling syslog usage. + +2004-04-09 Oswald Buddenhagen <[email protected]> + + * NoPassUsers now accepts an asterisk ("*") meaning all users + +2004-04-08 Oswald Buddenhagen <[email protected]> + + * Changes to FifoDir and FifoGroup now take effect on the global + command FiFo when HUPed. + +2004-03-16 Oswald Buddenhagen <[email protected]> + + * Changed AllowClose default to true; only the default kdmrc + disables it for local display now. + +2004-03-11 Chris Cheney <[email protected]> + + * Sanitized UserPath and SystemPath defaults. + +2004-03-07 Oswald Buddenhagen <[email protected]> + + * Add user name autocompletion to greeter; option UserCompletion. + As a side effect, "None" is gone from ShowUsers and UserList + appeared; ShowUsers & SortUsers affect both the user list and + the completion list. + +2004-02-28 Oswald Buddenhagen <[email protected]> + + * The default Xsession no longer tries to merge kdm's PATH into the + one set up by the shell startup scripts. Instead, kde.desktop + contains a full pathname and startkde fixes up PATH if necessary. + * The default Xsession will emulate the startup behaviour of more + shells, in particular bash, zsh and csh/tcsh. + * Setting up the session log is now done by kdm, not the Session + script; option ClientLogFile. + +2004-01-25 Oswald Buddenhagen <[email protected]> + + * Add support for specifying groups in SelectedUsers, HiddenUsers and + NoPassUsers. Prefix group names with a @. + +2003-12-17 Oswald Buddenhagen <[email protected]> + + * XDMCP initiated local displays are now treated as remote displays + (on localhost) by the config reader. + +2003-11-09 Oswald Buddenhagen <[email protected]> + + * Sanitized display restart behaviour; option StartInterval is gone. + +2003-11-04 Oswald Buddenhagen <[email protected]> + + * Conversation plugins can be configured now; option PluginOptions. + * The "Restart X Server"/"Close Connection" action can be configured + away; option AllowClose. + * The "Console Login" action can be configured away without touching + Xservers; option AllowConsole. + +2003-10-27 Oswald Buddenhagen <[email protected]> + + * KDM now complies with the input model of PAM. The greeter has an + interface for conversation plugins, so alternative authentication + mechanisms can be handled; options PluginsLogin & PluginsShutdown. + * Password-less and automatic logins now use a separate PAM service + (${KDM_PAM_SERVICE}-np). + +2003-10-17 Oswald Buddenhagen <[email protected]> + + * Add --with-kdm-xconsole configure switch. No need to patch + Makefile.am to enable the built-in xconsole anymore. + +2003-09-23 Oswald Buddenhagen <[email protected]> + + * Session types are now defined with .desktop files; consequently the + SessionTypes option is gone and we got SessionsDirs instead. + * The default Xsession now hard-wires the session types + - "default" to startkde + - "custom" to ~/.xsession + * The previous session type choice is now saved in a different file + (~/.dmrc) in a different format (ini-file); the SessSaveFile option + is gone. Optionally KDM can be configured to store all .dmrc files + in a common directory (option DmrcDir); this can be useful for AFS + based installations. + * The location of the administratively set user faces is now specified + by the FaceDir option and the pictures have a .face.icon extension + (or .face for "natural" images, possibly photos). + + The spec for the above changes is shared with GDM, so packagers should + choose common directories. + + * The kdmsts file moved to /var/lib/kdm by default; option DataDir. + * Nuked the AutoLoginSession option; i don't think it was useful at all + and it can be emulated anyway. + +2003-09-03 Oswald Buddenhagen <[email protected]> + + * Add option RandomDevice to override the OS specific default + entropy source. + +2003-08-26 Oswald Buddenhagen <[email protected]> + + * Add random seed to forged "previous" session type calculation; + option ForgingSeed. + +2003-07-15 Malte Starostik <[email protected]> + + * ColorScheme is now interpreted as the base name of the .kcsrc file, + not the contents of its Name field. + +2003-05-11 Oswald Buddenhagen <[email protected]> + + * GUIStyle & ColorScheme now accept an empty string, meaning + "built-in default". Made defaults empty, consequently. + +2002-12-01 Oswald Buddenhagen <[email protected]> + + * Integrated chooser into greeter; external 'chooser' executable + and the Chooser option are gone. + * The chooser can be started locally (without an XDMCP query); + options LoginMode and ChooserHosts. + * Added built-in xconsole to greeter; options ShowLog and LogSource. + This code is not built by default; uncomment the first three lines + in kfrontend/Makefile.am to enable it. + * The DaemonMode option is gone. The command line switches -daemon and + -nodaemon still exist, but are mostly unnecessary, as KDM can decide + what to do based on the parent process ID. + * The AutoLogin option and the -autolog/-noautolog switches are gone. + * The AutoLogin1st option is gone. + * The position of the -debug and -logfile command line options is + irrelevant again. The -xrm option is back, but is ignored by the + KDE frontend. + +2002-08-28 Oswald Buddenhagen <[email protected]> + + * Made it possible to specify the color scheme for the greeter; + option ColorScheme + +2002-08-10 Oswald Buddenhagen <[email protected]> + + * Renamed kdmdesktop to krootimage, moved it back into the KDM source + tree, and changed its command line + * krootimage will be automatically invoked by the greeter by default; + option UseBackground + * Chucked out the [Desktop0] section from kdmrc. Instead, the + location of the config file containing such a section is specified + with the BackgroundRc option. + * User images can now be optionally fetched from the users's home + directories; option FaceSource + * The default of the KeyFile option is now empty again + * GreeterScreen now groks -2, meaning upper-right screen + +2002-08-06 Oswald Buddenhagen <[email protected]> + + * Automatically don't daemonize if started by init. + +2002-03-19 Oswald Buddenhagen <[email protected]> + + * The default Xsession will emulate the startup behaviour of sh/ksh + by sourcing /etc/profile and ~/.profile. + +2002-03-10 Oswald Buddenhagen <[email protected]> + + * Added InteractiveSd option. This is not really implemented yet, + so enabling it just makes KDM deny the existence of the shutdown + condition/timing options. + * The Setup script is now executed synchronously. Long-lasting + commands should be put in the background explicitly. + +2002-02-28 George Staikos <[email protected]> + + * GreeterScreen now groks -1, meaning upper-left screen. + +2002-01-14 Oswald Buddenhagen <[email protected]> + + * Added option NumLock {On,Off,Keep} to preset the NumLock modifier + state for the greeter + +2001-12-11 Oswald Buddenhagen <[email protected]> + + * Added AntiAliasing option to disable antialiasing in the greeter + +2001-11-30 Oswald Buddenhagen <[email protected]> + + * Added GreeterScreen option to put the greeter on a particular + screen in multi-headed setups. + * Changed the default of Language from "C" to "en_US" + +2001-11-22 Oswald Buddenhagen <[email protected]> + + * The defaults of the options Xservers, Session, Setup, Startup, + Reset and PidFile are now back to the saner XDM defaults, + so KDM works even without config files. + * Changed option ShowUsers: All -> NotHidden + * Renamed the option Users to SelectedUsers and NoUsers to HiddenUsers. + * The GUIStyle option now groks all installed widget styles. + Note that Motif+ and KDE are now called MotifPlus resp. Default. + +2001-11-02 Oswald Buddenhagen <[email protected]> + + * Added conditional/scheduled shutdown modes; options + DefaultSdMode and AllowSdForceNow; moved AllowShutdown from + [X-<dpy>-Greeter] to [X-<dpy>-Core]. + * Added reserve display support; extension to Xservers. + * Added command FiFo support (see README); options FifoDir, + FifoGroup, [ShutDown]/AllowFifo, and [ShutDown]/AllowFifoNow. + FiFo location and capabilities are exported in $XDM_MANAGED. + +2001-10-04 Oswald Buddenhagen <[email protected]> + + * Xauth files are now created in AuthDir, not AuthDir/authdir. + Changed AuthDir default to /var/run/xauth. + +2001-07-12 Oswald Buddenhagen <[email protected]> + + * Renamed the option Xwilling to Willing + * The RandomFile option is not recognized on Linux and OpenBSD any + longer, as they have better entropy sources + +2001-07-10 Oswald Buddenhagen <[email protected]> + + * Added the tool 'genkdmconf'. It's supposed to create a suitable + configuration for KDM during 'make install' by merging new defaults + with a previous XDM/KDM config (if any is found). + +2001-07-03 Oswald Buddenhagen <[email protected]> + + * Added counterpart to the MinShowUID option: MaxShowUID + +2001-06-23 Oswald Buddenhagen <[email protected]> + + * Xauth files are now created in AuthDir/authfiles, not + AuthDir/authdir/authfiles + +2001-06-16 Oswald Buddenhagen <[email protected]> + + * Optionally put the cursor right in the "Password" field when + a user is preselected in the "Login" field; option FocusPasswd + +2001-06-15 Oswald Buddenhagen <[email protected]> + + * Replaced the ShutdownButton + ShutdownNeedsRoot option pair with + the AllowShutdown {None,Root,All} option + +2001-06-10 Oswald Buddenhagen <[email protected]> + + * The source directory structure changed entirely + * The argument to the -debug command line option is now a bit field; + the DebugLevel resource is gone + * The ErrorLogFile resource is gone + * The greeter module libKdmGreet.so has been converted to an + executable named kdm_greet; the external config parser is now + named kdm_config. The resource GreeterLib and the command line + option -getcfg (and -cfg2get) are gone, as the locations of the + config parser and greeter are derived from the location of the + kdm executable + * The config files are all located in ${kde_confdir}/kdm now; the + defaults for Setup & Session were adapted to this. + * The keys in kdmrc were reorganized: + - [KDM]/ShutdownButton -> [X-<dpy>-Greeter]/(ShutdownButton & + ShutdownNeedsRoot) + - [KDM]/Shutdown -> [Shutdown]/HaltCmd + - [KDM]/Restart -> [Shutdown]/RebootCmd + - [KDM]/LogoArea -> [X-*-Greeter]/ {None,Logo,Clock} + - remaining keys from [KDM] -> [X-*-Greeter]/ + - [Lilo]/Lilo -> [Shutdown]/UseLilo + - [Lilo]/LiloCommand -> [Shutdown]/LiloCmd + - [Lilo]/LiloMap -> [Shutdown]/ + - [Locale]/Language -> [X-*-Greeter]/ (Country key dropped) + * KDM will no longer use kdm-config; most of its resources were + absorbed into kdmrc: + - Servers -> [General]/Xservers + - RequestPort -> [Xdmcp]/(Port & Enable) + - DaemonMode -> [General]/ + - PidFile -> [General]/ + - LockPidFile -> [General]/ + - AuthDir -> [General]/ + - AutoRescan -> [General]/ + - RemoveDomainname -> [Xdmcp]/ + - KeyFile -> [Xdmcp]/ + - AccessFile -> [Xdmcp]/Xaccess/ + - ExportList -> [General]/ + - RandomFile -> [General]/ + - ChoiceTimeout -> [Xdmcp]/ + - SourceAddress -> [Xdmcp]/ + - Willing -> [Xdmcp]/Xwilling + - AutoLogin -> [General]/ + - GrabServer -> [X-<dpy>-Greeter]/ + - GrabTimeout -> [X-<dpy>-Greeter]/ + - AuthComplain -> [X-<dpy>-Greeter]/ + - AuthName -> [X-<dpy>-Core]/AuthNames + - NoPassUsers -> [X-<dpy>-Core]/ & [X-<dpy>-Core]/NoPassEnable + - AutoUser -> [X-<dpy>-Core]/(AutoLoginUser & AutoLoginEnable) + - AutoPass -> [X-<dpy>-Core]/AutoLoginPass + - AutoString -> [X-<dpy>-Core]/AutoLoginSession + - remaining server & session resources -> [X-<dpy>-Core]/ + * In GreetString the HOSTNAME substitution was replaced with the + %%, %d, %h, %n, %s, %r & %m expandos + * EchoMode does not understand "NoStars" any more. Use "NoEcho". + * Defaults changed: AuthDir to /var/lib/kdm, KeyFile to + $kdm_confdir/kdmkeys, Xservers to $kdm_confdir/Xservers, Xaccess to + $kdm_confdir/Xaccess, Startup to $kdm_confdir/Xstartup, Reset to + $kdm_confdir/Xreset, removed kde2 from SessionTypes + * The previous user is now saved in $kdm_confdir/kdmsts, not kdmrc + * Added option SessSaveFile, defaulting to .wmrc + * Command line option changes: + - -server, -udpPort, -resources, -session and -xrm are gone + - -error is aliased to -logfile + - -debug and -error/-logfile must be specified first + - options are now accepted with both one and two leading dashes + * The default Xsession will now + - source ~/.xprofile if present + - try harder to determine what executable $1 corresponds to + - interpret "default" as ~/.xsession + +2001-03-19 Oswald Buddenhagen <[email protected]> + + * %DMNAME% and %DMPATH% are expanded in string resources; + changed the defaults for PidFile and ConfigFile accordingly - + the latter resulting in KDM now using kdm-config, NOT xdm-config + * Use external config parser to merge platform-specific (that is, + KDE-like) configuration data into the XDM resources; command line + options -getcfg (default %DMPATH%_getcfg) and -cfg2get (no default, + meaning kdm_getcfg will use $kde_confdir/kdmrc). + * Changed "kde" to "kde2" in the default SessionTypes + * Stolen idea for console mode handling from dtlogin; options + ConsoleMode and AllowConsoleMode are gone; extension to Xservers + +2001-01-19 Oswald Buddenhagen <[email protected]> + + * Added resources AllowRootLogin and AllowNullPasswd + +2001-01-15 Oswald Buddenhagen <[email protected]> + + * Renamed UserIDLow option to MinShowUID + * The LogoArea option now accepts the value "None" + +2001-01-13 Oswald Buddenhagen <[email protected]> + + * The GUIStyle option now works again and groks all of Qt's + built-in widget styles and the "KDE" style + +2001-01-11 Oswald Buddenhagen <[email protected]> + + * Added placing of the greeter at fixed coordinates; options + GreeterPosFixed, GreeterPosX, and GreeterPosY. + * Added "default" to the default SessionTypes + +2000-01-06 Oswald Buddenhagen <[email protected]> + + * Added option AllowConsoleMode + +2000-12-09 Oswald Buddenhagen <[email protected]> + + * Added auto-login; options AutoLoginEnable, AutoLoginUser & + AutoLogin1st; resources AutoUser, AutoPass, AutoString & AutoLogin1st + * Added password-less login; options NoPassEnable & NoPassUsers; + resource NoPassUsers + * Added auto-re-login on XServer crash; resource & option AutoReLogin + + The kdmrc options and xdm-config resources are "ORed", i.e., if + either is enabled, the function is enabled. + The command line options -autolog/-noautolog and the resource AutoLogin + can be used to disable auto-login and password-less login at once. + + * Added displaying the previously logged in user in the "Login" + field; option ShowPrevious. The previous user is saved in kdmrc, + section [Previous]. + +2000-12-07 Oswald Buddenhagen <[email protected]> + + * New XDM port from XFree86 4.0.1 + - new resources SourceAddress & Willing + - /authdir/authfiles is now automatically appended to AuthDir + * Default for PidFile and Setup changed back to empty + * Displays restarting too fast are disabled; resource StartInterval + * Option UserView, and NoUsers dependency on Users being empty + replaced with explicit option ShowUsers {All,Selected,None} + * Made the greeter dynamically loadable (libKdmGreet.so) + * Moved chooser and greeter to separate directories, + same for unused stuff (misc/) + + * Added half-baked support for command FiFos; resources + FifoCreate, FifoGroup, FifoMode. Replaced on 2001-11-02 + +2000-10-10 Steffen Hansen <[email protected]> + + * Made kdmdesktop read the [Desktop0] section from kdmrc instead + of kdmdesktoprc. + +2000-09-07 Waldo Bastian <[email protected]> + + * Make password echo mode configurable; + option EchoMode {OneStar,ThreeStars,NoEcho} + +2000-08-07 Christopher Molnar <[email protected]> + + * The minimal user ID to show in the user view can be specified now; + option UserIDLow. + +2000-06-04 Espen Sand <[email protected]> + + * The logo area can now display either a clock or a pixmap; + option LogoArea {KdmClock,KdmLogo} + +1999-12-12 Jaromir Dolecek <[email protected]> + + * Use OS-specific defaults for Shutdown & Restart + * Make PidFile, UserPath & SystemPath defaults on NetBSD match FreeBSD + +1999-11-17 Harald Hoyer <[email protected]> + + * Made kchooser + +1999-11-15 Matthias Hoelzer-Kluepfel <[email protected]> + + * kdmdesktop replaced with ../kdesktop/kdmdesktop. Uses new config + file (kdmdesktoprc) with new options (all in section [Desktop0]): + +1999-07-01 Steffen Hansen <[email protected]> + + * Xaccess now accepts NOBROADCAST + +1999-06-07 Matthias Hoelzer-Kluepfel <[email protected]> + + * Added next boot OS selection via LiLo; options [Lilo] Lilo, + LiloCommand & LiloMap + * Added button to switch to console mode; option [KDM] ConsoleMode + +1999-03-01 Stephan Kulow <[email protected]> + + * Option GUIStyle temporarily removed + +1998-10-08 Thomas Tanghus <[email protected]> + + * [KDMDESKTOP] option changes: + - BackgroundPictureTile, BackgroundPictureCenter, FancyBackground -> + BackgroundPictureMode {None,Tile,Center,Scale, + TopLeft,TopRight,BottomLeft,BottomRight,Fancy} + - add BackGroundColorMode {Plain,Horizontal,Vertical} + - BackgroundColor -> BackGroundColor1, BackGroundColor2 + +1998-09-20 Hans Petter Bieker <[email protected]> + + * Change defaults: + - Setup: "" -> XDMDIR/Xsetup + - PidFile: "" -> FreeBSD: /var/run/kdm.pid, others: XDMDIR/kdm-pid + - Session: "XBINDIR/xterm -ls" -> XDMDIR/Xsession + - UserPath & SystemPath: no /usr/ucb for Linux & FreeBSD + +1998-09-11 Hans Petter Bieker <[email protected]> + + * Replace hard-coded paths with XBINDIR/XDMDIR in various defaults + +1998-09-06 Hans Petter Bieker <[email protected]> + + * Default Xsession now searches $1 in PATH and execs it + +1998-03-26 Stephan Kulow <[email protected]> + + * Nuke -kdedir cmdline option and Kdedir resource + +1997-09-09 Steffen Hansen <[email protected]> + + * Change defaults: + - AuthDir: XDMDIR/authDir -> XDMDIR/authdir + +1997-09-04 kdecvs + + * Change defaults: + - AuthDir: XDMDIR -> XDMDIR/authDir diff --git a/kdm/Makefile.am b/kdm/Makefile.am new file mode 100644 index 000000000..106e47862 --- /dev/null +++ b/kdm/Makefile.am @@ -0,0 +1,20 @@ +SUBDIRS = . backend kfrontend + +PAM = @KDM_PAM_SERVICE@ + +noinst_DATA=config.ci + +kdmdocdir = $(datadir)/doc/kdm +kdmdoc_DATA = README + +install-data-local: + -@test -n "$(DESTDIR)" || test -z "$(PAM)" || { $(top_srcdir)/mkpamserv $(PAM) && $(top_srcdir)/mkpamserv -P $(PAM)-np; } + +config.ci: $(srcdir)/config.def $(srcdir)/confproc.pl + $(PERL) -w $(srcdir)/confproc.pl $(srcdir)/config.def $@ + +CLEANFILES = config.ci + +### this is *only* for the kdm home page maintainer! ### +hp: + scp README ChangeLog TODO devel-home:files/kdm diff --git a/kdm/README b/kdm/README new file mode 100644 index 000000000..bdf34814e --- /dev/null +++ b/kdm/README @@ -0,0 +1,454 @@ +This is the K Display Manager (KDM) for KDE 3.4, +the KDE replacement for the X Display Manager (XDM). + +Semi-official home page: http://devel-home.kde.org/~ossi/sw/kdm.html + + +configure options that affect KDM +--------------------------------- + +--with-pam[=service] + Compile KDM (and other parts of kdebase) with PAM support. The default + service is "kde". PAM is automatically used if found. + +--with-kdm-pam=service + Override the PAM service used specifically by KDM. Depends on --with-pam. + +--with-shadow + Compile KDM (and other parts of kdebase) with shadow password support. + Shadow passwords are automatically used if found. This affects KDM only + if PAM is not used. + +--with-krb4[=path] + Compile KDM (and the LDAP KIO slave) with KTH Kerberos 4 support. Note + that this does not work with the Kerberos 4 compatibility layer found in + MIT Kerberos 5. This affects KDM only if PAM is not used. + +--with-afs + Compile KDM with AFS support. Depends on --with-krb4. + +--with-krb5auth[=path] +--with-rpcauth + Compile KDM with Kerberos 5 resp. secure RPC support for X authorization + cookies. It's pretty pointless to enable this if you don't use an X server + that supports it. + + If you want user authentication against a Kerberos realm, compile KDM with + PAM support and use the appropriate module. + +--without-xdmcp + Compile KDM without XDMCP support. + +--with-kdm-xconsole + Compile KDM with a builtin "xconsole" replacement in the greeter. I don't + consider this too useful, but SuSE wanted it, so it's there. ;) + + +KDM's file system layout +------------------------ + +${kde_confdir} is usually ${prefix}/share/config +${kde_datadir} is usually ${prefix}/share/apps +The indented locations are envisioned for a configuration shared with GDM. + +${kde_confdir}/kdm/{kdmrc,Xservers,Xaccess,Xwilling,...} +${kde_datadir}/kdm/sessions/*.desktop + /etc/X11/sessions/,/usr/share/xsessions/ +${kde_datadir}/kdm/pics/users/ +${kde_datadir}/kdm/pics/ +${kde_datadir}/kdm/faces/*.face{,.icon} + /usr/share/faces/ +/var/run/xauth/A* +/var/run/xdmctl/xdmctl* +/var/run/kdm.pid +/var/lib/kdm/kdmsts +<site-specific>/*.dmrc +$HOME/.face{,.icon} +$HOME/.dmrc + + +How to setup KDM +---------------- + +KDM's config files are all located in ${kde_confdir}/kdm. +"make install" will create a probably working configuration, either by +deriving it from an already present KDM/XDM installation or by using +defaults if no previous installation is found. + +You can change the configuration from the KDE Control Center. You will +find the "Login Manager" module in the "System Administration" group. + +Have a look at README.pam in the kdebase top level directory if your +system uses PAM. + + +Configuring session types +------------------------- + +The way session types are configured changed drastically in KDE 3.2. +Session types are now represented by .desktop files in appropriate locations. +The format of the .desktop files is (not yet) defined in the FreeDesktop.org +desktop entry spec. Differences to "standard" .desktop files are: +- the Type is fixed to XSession and can be omitted +- the Encoding is fixed to UTF-8 and can be omitted +- the Exec field will be passed to "eval exec" in a bourne shell; no macro + expansion is performed on it. "default", "custom" and "failsafe" are magic + constants that cause special actions. +- Name, Comment, TryExec and Hidden are supported +- the remaining keys have no meaning currently +Session types are internally identified by filename (without extension); +that's what will be saved to ~/.dmrc and what DESKTOP_SESSION will be set to. +For every magic Exec constant a session type of the same name exists. + +Unless your system is configured differently already, you should create a +directory ${kde_confdir}/kdm/sessions and add this to kdmrc: + +[X-*-Core] +SessionsDirs=${kde_confdir}/kdm/sessions,${kde_datadir}/kdm/sessions + +(Note that you must use actual paths instead of variables, see the section +about KDM's file system layout.) +Do any changes only in the config directory - any changes in the data +directory will be lost after the next KDE update. + +To override a session type, copy the .desktop file from the data dir to the +config dir and edit it at will. Removing the shipped session types can be +accomplished by "shadowing" them with .desktop files containing Hidden=true. +For the magic session types no .desktop files exist by default, but KDM +pretends they would, so you can override them like any other type. +I guess you already know how to add a new session type by now. ;-) + + +Running KDM from init +--------------------- + +NOTE, that this description applies to RedHat 5.x and must be adapted for +other distributions/systems. Generally I'd advise _against_ starting KDM +directly from init - better use a proper init script, possibly by slightly +modifying the XDM init script shipped by your distribution. + + Edit (as root) /etc/inittab. + + Look for the line: + + x:5:respawn:/usr/X11/bin/xdm -nodaemon + + Replace it with: + + x:5:respawn:/opt/kde/bin/kdm + + This tells init(8) to respawn KDM, the KDE display manager, when + the system is in run level 5. + Note that KDM does not need the -nodaemon option. + + To start KDM, either run (as root) /sbin/telinit 5 (to switch to + run level 5), or (this is risky! don't do it until you _know_ you + want the system to boot into this every time!) edit /etc/inittab + and change the line: + + id:3:initdefault: + + to + + id:5:initdefault: + + If you do the latter step, then every time your system boots + successfully it will go into run level 5 and run KDM, + presenting you the exceedingly cute KDE login screen. + + +The command sockets +------------------- + +This is a feature you can use to remote-control KDM. It's mostly intended +for use by ksmserver and kdesktop from a running session, but other +applications are possible as well. + +The sockets are UNIX domain sockets which live in subdirectories of the +directory specified by FifoDir=. The subdir is the key to addressing and +security; the sockets all have the file name "socket" and file permissions +rw-rw-rw- (0666). This is because some systems don't care for the file +permissions of the socket files. +There are two types of sockets: the global one (dmctl) and the per-display +ones (dmctl-<display>). +The global one's subdir is owned by root, the subdirs of the per-display +ones' are owned by the user currently owning the session (root or the +logged in user). Group ownership of the subdirs can be set via FifoGroup=, +otherwise it's root. The file permissions of the subdirs are rwxr-x--- (0750). + +The fields of a command are separated by tabs (\t), the fields of a list +are separated by spaces, literal spaces in list fields are denoted by "\s". +The command is terminated by a newline (\n). +The same applies to replies. The reply on success is "ok", possibly followed +by the requested information. The reply on error is an errno-style word (e.g., +"perm", "noent", etc.) followed by a longer explanation. + +Global commands: + +"login" display ("now"|"schedule") user password [session_arguments] + - login user at specified display. if "now" is specified, a possibly + running session is killed, otherwise the login is done after the + session exits. + session_arguments are printf-like escaped contents for .dmrc. Unlisted + keys will default to previously saved values. + +Per-display commands: + +"lock" + - The display is marked as locked. If the X-Server crashes in this state, + no auto-relogin will be performed even if the option is on. + +"unlock" + - Reverse the effect of "lock": re-enable auto-relogin. + +"suicide" + - The currently running session is forcibly terminated. No auto-relogin is + attempted, but a scheduled "login" command will be executed. + +Commands for all sockets: + +"caps" + - Returns a list this socket's capabilities: + "kdm" - identifies kdm, in case some other DM implements this protocol, too. + "list", "activate", "lock", "suicide", "login" - the respective command + is supported. + "bootoptions" - the "listbootoptions" command and the "=" option to + "shutdown" are supported. + "shutdown <list>" - "shutdown" is supported and allowed to the listed users + (comma-separated). "*" means all authenticated users. + "shutdown" - "shutdown" is supported and allowed to everybody. + "nuke <list>" - forced shutdown is allowed to the listed users. + "nuke" - forced shutdown is allowed to everybody. + "reserve <number>" - reserve displays are configured and <number> are + available at this time. + +"list" ["all"|"alllocal"] + - Return a list of running sessions. By default all active sessions are + listed. If "all" is specified, passive sessions are listed as well. If + "alllocal" is specified, passive sessions are listed as well, but all + incoming remote sessions are skipped. + Each session entry is a comma-separated tuple of: + - Display or TTY name + - VT name for local sessions + - Logged in user's name, empty for passive sessions and outgoing remote + sessions (local chooser mode) + - Session type or remote host for outgoing remote sessions, empty for + passive sessions + - A flag field: + - "t" for tty sessions + - "*" for the display belonging to the requesting socket + - "!" for sessions that cannot be killed by the requesting socket + - New flags might be added later + - New fields might be added later + +"reserve" [timeout in seconds] + - Start a reserve login screen. If nobody logs in within the specified amount + of time (one minute by default), the display is removed again. When the + session on the display exits, the display is removed, too. + - Permitted only on sockets of local displays and the global socket. + +"activate" (vt|display) + - Switch to a particular VT (virtual terminal). The VT may be specified + either directly (e.g., vt3) or by a display using it (e.g., :2). + - Permitted only on sockets of local displays and the global socket. + +"listbootoptions" + - List available boot options. + => "ok" list default current + default and current are indices into the list and are -1 if unset or + undeterminable. + +"shutdown" ("reboot"|"halt") ["="bootchoice] \ + ("ask"|"trynow"|"forcenow"|"schedule"|\ + start ("-1"|end ("force"|"forcemy"|"cancel"))) + - Request a system shutdown, either a reboot or a halt/poweroff. + - An OS choice for the next boot may be specified from the list returned by + "listbootoptions". + - Shutdowns requested from per-display sockets are executed when the current + session on that display exits. Such a request may pop up a dialog asking + for confirmation and/or authentication. + - start is the time for which the shutdown is scheduled. If it starts with + a plus-sign, the current time is added. Zero means immediately. + - end is the latest time at which the shutdown should be performed if active + sessions are still running. If it starts with a plus-sign, the start time + is added. Minus one means wait infinitely. If end is through and active + sessions are still running, KDM can do one of the following: + * "cancel" - give up the shutdown. + * "force" - shut down nonetheless. + * "forcemy" - shut down nonetheless if all active sessions belong to the + requesting user. Only for per-display sockets. + - start and end are specified in seconds since the UNIX epoch. + - "trynow" is a synonym for "0 0 cancel", "forcenow" for "0 0 force" and + "schedule" for "0 -1". + - "ask" attempts an immediate shutdown and interacts with the user if active + sessions are still running. Only for per-display sockets. + +"shutdown" "cancel" ["local"|"global"] + - Cancel a scheduled shutdown. The global socket always cancels the currently + pending shutdown, while per-display sockets default to cancelling their + queued request. + +"shutdown" "status" + - Return a list with information about shutdowns. + The entries are comma-separated tuples of: + - ("global"|"local") - pending vs. queued shutdown. A local entry can be + returned only by a per-display socket. + - ("halt"|"reboot") + - start + - end + - ("ask"|"force"|"forcemy"|"cancel") + - Numeric user ID of the requesting user, -1 for the global socket. + - The next boot OS choice or "-" for none. + - New fields might be added later. + +There are two ways of using the sockets: +- Connecting them directly. FifoDir is exported as $DM_CONTROL; the name + of per-display sockets can be derived from $DISPLAY. +- By using the kdmctl command (e.g., from within a shell script). + Try "kdmctl -h" to find out more. + +Here is an example bash script "reboot into FreeBSD": + +if kdmctl | grep -q shutdown; then + IFS=$'\t' + set -- `kdmctl listbootoptions` + if [ "$1" = ok ]; then + fbsd=$(echo "$2" | tr ' ' '\n' | sed -ne 's,\\s, ,g;/freebsd/I{p;q}') + if [ -n "$fbsd" ]; then + kdmctl shutdown reboot "=$fbsd" ask > /dev/null + else + echo "FreeBSD boot unavailable." + fi + else + echo "Boot options unavailable." + fi +else + echo "Cannot reboot system." +fi + + +"It doesn't work!!" +------------------- + +More input! ;-) + +KDM accepts two command line options related to logging: + + -debug <n> + <n> is a decimal or hexadecimal (prefix 0x) number. + The number is a bitfield, i.e., it is formed by summing up the + required values from this table: + 1 (0x1) - core debugging. Probably the most useful one. + 2 (0x2) - config reader debugging. + 4 (0x4) - greeter debugging. + 8 (0x8) - IPC debugging. This logs _all_ communication between the + core, the config reader and the greeter - including the + passwords you type, so edit the log before showing it to + somebody. + This attempts to synchronize the processes to interleave the + log messages optimally, but will probably fail unless you use + -debug 0x80 as well. + 16 (0x10) - wait after forking session sub-daemon. + 32 (0x20) - wait after starting config reader. + 64 (0x40) - wait after starting greeter. + The wait options are only useful if you need to attach a debugger + to a process, but it crashes before you are able to do so without + the delay. See below. + 128 (0x80) - don't use syslog for internally generated messages. + 256 (0x100) - core Xauth debugging. + 1024 (0x400) - run config reader and greeter through valgrind. + 2048 (0x800) - run config reader and greeter through strace. + + Logs from "-debug 7" are usually a good start. + + -logfile <file> + <file> is the file to log various messages to. The default log file is + /var/log/kdm.log. For internal reasons there is no option in kdmrc to + permanently specify the log file location. If you redirect KDM's + standard error output to a file, KDM will log there. + If KDM is configured to use syslog (and it _very_ probably is on any + modern system), all internally generated messages are logged to the + "daemon" facility. The log usually can be found in /var/log/debug.log + and /var/log/daemon.log; make sure that daemon.* is logged (look at + /etc/syslog.conf). + If you have problems logging in and your system uses PAM (also quite + probable on modern systems), the "auth" and "authpriv" syslog facilities + are interesting, too. + +Send me all the logs together with a detailed description of what you did +and what happened. If your problem is related to a specific configuration, +you should also attach a tar.gz archive of your KDM config directory. + +If I request a backtrace from you and KDM didn't create one yet via the +usual drkonqi procedure, you'll have to do that yourself. The keyphrase +is "attaching gdb". How exactly this is done depends on the part that +crashes: +- master daemon. Actually you should never need to attach to it, as + you can start it within the debugger already: + # gdb --args kdm -nodaemon -debug 7 + (gdb) run +- display subdaemon. Find (using ps) the process with a name like + "-:0" (where :0 is actually the display this process is for). This + process' PPID is the master daemon. Attach to it this way: + # gdb kdm <the PID you found> + (gdb) cont + If the subdaemon crashes before you can attach, add 16 to the debug flags + when you start KDM. +- config reader. You will have to add 32 to the debug flags almost certainly. + The PPID will be the master daemon as well. + # gdb kdm_config $(pidof kdm_config) + (gdb) cont +- greeter. If it's too fast, add 64 to -debug. The PPID will be the subdaemon. + # gdb kdm_greet $(pidof kdm_greet) + (gdb) cont + The simplification with "pidof" works only if you have only one display, + otherwise you have to find the PID manually (by using ps -fx). +Once you got gdb attached to the offending process, do whatever is needed +to make it crash (probably nothing, if you had to use a delay parameter). +Once it crashed, gdb will tell you a signal name, like SIGSEGV - that's the +first interesting part for me. Then you have to create the actual backtrace: + (gdb) bt +The output of this command is interesting for me. +I might request a backtrace even if nothing crashes, but instead hangs. In +this case don't use "cont" after attaching, but use "bt" right away. If the +process is already running, interrupt it with ctrl-c. +For obvious reasons you have to run gdb on a different virtual terminal than +the X server. To get there, press alt-ctrl-f1 and log in as root. To +switch to the X server's vt, press alt-ctrl-f7 (the exact function key may +be different on your system). You may also use a remote login from a +second machine. In any case it is advantageous to have mouse support on the +debugging console for copying the backtrace. +Note that a backtrace is usually _much_ more useful if the binary contains +debugging info, so you should install from source with the --enable-debug +configure flag if at all possible. + + +Random rambings and license information +--------------------------------------- + +Version 0.1 of KDM is copyright + Matthias Ettrich <[email protected]> +All later versions copyright: + (C) 1997-2000 Steffen Hansen <[email protected]> +Since version 0.90 (KDE 2.1) copyright: + (C) 2000-2003 Oswald Buddenhagen <[email protected]> + +The files in the backend directory are licensed under the X licence +(see http://www.x.org/Downloads_terms.html for more info). +The files in the kfrontend directory are licensed under the GNU GPL. + +Thanks to (in no particular order): +Michael Bach Jensen and Torsten Rahn for drawing icons. +Duncan Haldane for investigation of PAM issues. +Stephan Kulow for helping with the autoconf stuff. +Martin Baehr for intensive testing and writing the sample Xsession scripts. +Harald Hoyer <[email protected]> for the (now obsoleted) chooser. +SuSE for employing me (ossi) for three months to work on kdm. +BasysKom for sponsoring my (ossi's) work on the conversation plugin stuff. +... and _many_ others ... + + +-- +Have fun with it (and feel free to comment), + + Oswald Buddenhagen <[email protected]> diff --git a/kdm/TODO b/kdm/TODO new file mode 100644 index 000000000..12a1ec545 --- /dev/null +++ b/kdm/TODO @@ -0,0 +1,243 @@ +theming (#37349): +- maybe add a Themable plugin flag. if not set and no talker, abort. +- minor: show QWidgets only when the layout is ready and the theme was painted. + but one can't hide the widgets in a QLayout, as they have no size then. +- add attribute inheritance. apply attributes extracted from particular + elements of the (hidden) talker. +- make plugin return a QDom instead of embedding a QLayoutItem (QLabels look + just awful in the themed greeter). big problem: there is no KdmGrid ... try + to (ab)use QLayout. +- extract background from theme. use explicit node-id "background", i think. +- automatic talker node detection/creation. same for background, possibly. + +- remote login can have the chosen host as the sessName +- popup menu grabs keyboard. that means it is ungrabbed afterwards ... +- error label uses fixed colors. red might be ok, but not black. + +- message after switching to text mode + +- handle non-linux VTs: + on systems without VT_GETSTATE, try activating all consoles in turn to + find free ones. wow, this sucks so much. + - BSD: 1st: pcvt, /dev/ttyC[0] (OpenBSD), /dev/ttyv[0] (other), + also emulated by wscons on /dev/ttyE. + 2nd: syscons, /dev/ttyv[0], fallback /dev/vga + - Lynx, /dev/atc[0] + - Solaris, /dev/vt[00] + - SVR4, /dev/vc[00] (ESIX), /dev/vt[00] (other) + - SCO, /dev/tty[00], query current with CONS_GETINFO, counts 0-based + ref: xorg/programs/Xserver/hw/xfree86/os-support/xf86_OSlib.h +- act on BSD_INIT +- before nuking X server on other vt, save current vt and restore it before + disallocating server vt. or just make the xserver not switch wildly. + +- possibly parse Xserver log to find failure cause. this is very hacky. + +- try harder to get rid of processes, see X servers failure cleanup path + +- make auto-re-login a per-user option; save in .dmrc. + +- add Xserver option set selection (#56329) +- add support for XRandR (#48602) +save these options to .dmrc? + +- per-display sections in .dmrc. read-only, as far as kdm is concerned, as + otherwise the GUI would become insanely complex. + +- make config position independent +- parse /etc/kderc? +- merge multiple kdmrcs in the style of kconfig. how to set section priorities? +- genkdmconf: treat backgroundrc as an ini file, not as a text blob +- add proper quoting and dequoting to genkdmconf ini parser & writer + +- write generic conversation plugin +- write modern conv plugin. or maybe this should be a parallel vs. serial + setting of the classic plugin? + +- actually implement the libpam_client support + +- check if pam works before trying to authenticate +- test whether nis, kerberos4 & kerberos5 work +- sync BSD_AUTH from xdm, sync osfc2 from kcheckpass + +- swap pam_setcred and pam_open_session order. +- check how the system specific functions like setpcred (AIX) and + setusercontext (BSD) combine with pam_setcred. + +- Move clock from greeter dialog to desktop +- add more clock types (#18178) +- add icons to action menu. icon theme selection! + +- Add XDMCP _client_ to core (for remote login like in dtlogin). + Currently this is done by simply restarting the x-server with -query. + +- add login restrictions for reserve displays (#59353) + +- possibly do the authentication for the reserve display on the display it + is launched from (relates #59353) + +- remote-accessible command sockets for remote shutdown, etc. + or maybe implement it as an xdmcp extension? +- LoginMode=DirectQuery + +- "XDMCP over FiFo" - or at least a "manage <dpy> [<xauth>]" command +- the per-display sockets are in fact nonsense; gdm's approach is better + +- add bgset to XDM_MANAGED + add FiFo command "background\t{inprogress,aborted,done}" + +- lilo boot option <default>, i.e., -R with no argument +- support lilo -A mode + +- support sleep/suspend in the shutdown menu. should this be really treated + like a shutdown? (#33839) + +- add language selection (export as LC_*). kde should respect this until the + language is explicitly configured. and later? option "use system setting"? + integrate with $KDE_LANG somehow. (#55379, #63804) +- add keymap selection (via xkb) (#51245, #64642) +for both, one would preset a list of available options and make one entry +the greeter's own setting. explicitly setting it sets it for both the greeter +and the session. .dmrc later affects only the session, not the greeter. + +- handle failsafe internally, take care of focus. see #32973 + +- TryExec for "custom" session type. always show the entry, but disable it + if it is unavailable for the selected user. + +- cursor theming support via Xcursor (#66829) + +- add screensaver (#41941) +- support DPMS (#18597) + +- add a minimalistic window manager to the greeter (#17716, #51039) + +- write a separate configurator application, as kcontrol does not scale well + enough to cover all of kdm's options. + +- Different logos for each session type (see #74500) +- User pictures in logo field +- display user's .plan/.project (or .person? .userinfo?) in the greeter? + text area/label would suck -> tooltip? + +- allow disabling full names or login names in userview (#54110) +- user list loading in the background (after first few to get a reasonable + width estimate) + +- faking session parameters (type, language, etc.) of nonexistent users based + on statistical analysis of actual users ... severe overkill!? + +- export password to the startup/session scripts. somehow ... (#35396) + +- maybe reset CapsLock in the greeter. there is some CapsLock vs. ShiftLock + confusion, though. + +- maybe add kiosk mode: the user and his options are preset and locked in + the greeter. i doubt it's usefulness, though. + +- make builtin xconsole hideable; it should free the device when invisible. + possibly auto-hide it on vt switch - see kdesktop_lock for the x event + handling. + +- ssh-agent/gpg-agent integration (#44177, #65709) + +- lbxproxy integration (tell ghakko) + +- in kcm_kdm, detach backgroundrc change status from kdmrc change status. + +- when a shutdown is scheduled, don't remove all login possibilities. + instead, display a warning in the greeter. use SIGUSR1 to notify already + running greeters about changes. +- user notification about scheduled shutdown (and cancelled forced shutdown): + - wall + - greeter popup + - d-bus message. this would be best, particularly because screen savers + would need no special handling then. +- maybe bomb DefaultSdMode, save in state file instead. compare with ksmserver. + +- gdm changelog indicates that PAM sometimes + - continues despite PAM_CONV_ERR + - asks user name twice +- gdm avoids the PAM_MESSAGE message box vs. prompt problem by displaying + everything in one "error area". all messages are simply appended; an empty + message clears the area. +- gdm stops cursor blinking on not used (remote) displays after 20 secs to + save bandwidth. + +internal stuff: +- improve signal handling in the subdaemon, it's incredibly racy (GOpen/GClose). + depends on proper main loop. + alternative extreme measure: launch greeter from master daemon? +- the process reaping from GClose should be in sync with the main loop. +- kill warning on AIX - see bug #13628 (really present?) +- implement auto-re-login by keeping the display subdaemon alive instead + of starting a new one and feeding it the old auth data. +- options for running the greeter and the core unprivileged. problem: xauth. +- rethink the coupling of the kdm components, particularily the config reader. + options: + - keep things basically as-is, make the Xaccess interface even more flexible, + add capability flags. + - as previous, but don't use #defines, but textual constants. even more + flexible, but slower, bigger, no compile-time checking, and the typing + system would have to be more core-based. keys in the rc are considered + invalid if they were not queried. + - completely opposite: no explicit queries, but hard-code everything. that + kills the idea of having one backend binary for multiple frontends, but + that's a BlueSkyDream anyway. + following that path, the config reader could be nuked at all. + +ralf says: +- put the kmenu sidebar image on the left of the greeter +- enable the clock by default + +thoughts (not really todo): +- PAM sucks. big time. + historically, it is completely incapable of operating in event-driven contexts + when it comes to non-console authentication schemes. the module just hangs in + pam_sm_authenticate() (pam_authenticate() to the outside), waiting for input + from its device. + then came linux-pam 0.58, introducing PAM_BINARY_{MSG,PROMPT} to the + conversation function interface. no conversation function could handle the + binary prompts generically, of course. so came linux-pam 0.63 with a client + library that would add another layer of indirection, so the conversation + function could simply call into it and it would do whatever was configured + by the admin. and everbody was happy, right? wrong! i've yet to see a single + module (except for the demo module in linux-pam, of course) that actually + uses this feature. not to mention the non-existing portability (you don't + seriously expect TOG to extend the PAM standard within the next decade, do + you?). so we're right where we started from. + this imposes problems in two use cases: + - cancelling authentication alltogether. this happens when the user changes + the authentication method or when the greeter exits for some reason. if + the process waits in the conversation function, it can simply return + PAM_CONV_ABORT. if the module hangs, we're screwed. + - suspending authentication. this is needed for shutdowns that need auth. + if the module hangs, we're screwed, of course. if we're waiting in the + conversation function, we have three options: 1) just abort the auth + cycle and start a new one. this is what is done currently. 2) just open + a second pam handle and authenticate with it, all from within the "outer" + pam_authenticate(). if we're lucky, no involved modules use static variables + and things work out. 3) linux-pam 0.65 introduced the following: the + conversation function can return PAM_CONV_AGAIN. this in turn makes the + module and consequently libpam return PAM_INCOMPLETE, requesting the + application to call the resp. libpam function again. in theory this + guarantees that authentication with a second pam handle is safe. of course, + PAM_INCOMPLETE is just as popular and thus useful as PAM_BINARY_PROMPT. + we could just longjmp() out of hanging modules from a signal handler. + however, this might lead to resource leaks and even leave us with an unstable + libpam. killing the hanging process seems like the most viable solution. + however, for this we first need to make the greeter a child of the master + daemon. also, the display sub-daemon (which happens to do the main auth.) + is responsible for keeping the initial X connection open. killing it would + terminate the session according to the XDMCP spec. other issues are probable. +- multiple conv. plugins could be used in a row, each serving a pam module. + the plugins would have to detect that it's their turn by filtering messages + and prompts. +- consider making the menu an actions-only menu again and put an "options >>" + button somewhere. relates #63401, #61492 +- pipe .xsession-errors through the daemon and put a size limit on it. + remove old logs in disk-full situation. +- set LC_ALL in the backend for i18n-capable PAM libs - does one exist? + + +last sync with XFree86 HEAD: 2004-04-02 diff --git a/kdm/backend/Imakefile b/kdm/backend/Imakefile new file mode 100644 index 000000000..75efcb45a --- /dev/null +++ b/kdm/backend/Imakefile @@ -0,0 +1,203 @@ +/* well, we have no subdirs ... +#define PassCDebugFlags 'CDEBUGFLAGS=$(CDEBUGFLAGS)' +*/ + +#ifdef DEBUG +CDEBUGFLAGS := $(CDEBUGFLAGS) -g +#endif + +#ifndef BuildBoth +#define BuildBoth (defined(LinuxArchitecture) && !UseElfFormat) +#endif + +#ifndef LinuxShadowSuite +#define LinuxShadowSuite NO +#endif + +#if FSUseSyslog +LOG_DEFINES = -DUSE_SYSLOG +#endif + +#ifdef NoXDMCP +XDMCPLIB = +#else +XDMCP_DEFINES = -DXDMCP +#endif + +#if HasXdmAuth +XDMAUTH_DEFINES = -DHASXDMAUTH +XDMAUTHOBJS = xdmauth.o +XDMAUTHSRCS = xdmauth.c +#endif + +#if HasSecureRPC +RPC_DEFINES = -DSECURE_RPC +RPCOBJS = rpcauth.o +RPCSRCS = rpcauth.c +RPCLIB = -lrpcsvc +#endif + +#if HasKrbIV +#if NOAFS +KRBIV_DEFINES = KrbIVDefines -DNO_AFS +#else +KRBIV_DEFINES = KrbIVDefines +#endif +KRBIV_INCLUDES = KrbIVIncludes +KRBIVLIB = KrbIVLibraries +#endif + +#if HasKrb5 +KRB5_DEFINES = Krb5Defines +KRB5_INCLUDE = Krb5Includes +KRB5OBJS = krb5auth.o +KRB5SRCS = krb5auth.c +#endif + +/* This is correct for Linux and FreeBSD */ +#if HasPam +PAM_LIBRARIES = PamLibraries +PAM_DEFINES = -DUSE_PAM +#endif + +#if HasPam +#undef HasShadowPasswd +#define HasShadowPasswd NO +#undef HasLibCrypt +#define HasLibCrypt NO +#endif + +/* +#if HasBSDAuth +BSDAUTH_DEFINES = -DUSE_BSDAUTH +#endif +*/ + +#if SystemV4 || HasShadowPasswd + +#if !LinuxShadowSuite +PWD_DEFINES = -DUSESHADOW +#else +PWD_DEFINES = -DUSESHADOW -DSHADOWSUITE +#endif + +#if !defined(i386IscArchitecture) && !defined(i386ScoArchitecture) && !defined(LinuxArchitecture) && !defined(NTOArchitecture) && !defined(SGIArchitecture) +SYS_LIBRARIES3 = -lresolv +#endif +#if SystemV || defined(SequentArchitecture) +SYS_LIBRARIES1 = -lsec +#endif +#if defined(LinuxArchitecture) && (!UseElfFormat || LinuxShadowSuite) +SYS_LIBRARIES1 = -lshadow +#endif + +#endif + +#if defined(UltrixArchitecture) +SYS_LIBRARIES1 = -lauth +#endif + +#if (defined(AIXArchitecture) && (OSMajorVersion >= 3)) +SYS_LIBRARIES1 = -ls +#endif + +#if HasLibCrypt +#ifdef SpecialLibCrypt +CRYPT_LIBRARIES = SpecialLibCrypt +#else +CRYPT_LIBRARIES = -lcrypt +#if defined(LynxOSArchitecture) +CRYPT_DEFINES = -DHAS_CRYPT +#endif +#endif +#endif + +#if HasBSD44Sockets +SOCK_DEFINES = -DBSD44SOCKETS +#endif + +#if defined(i386Architecture) || defined(AmigaArchitecture) +FRAGILE_DEFINES = -DFRAGILE_DEV_MEM +#endif + +#ifdef RandomDefines +RANDOM_DEFINES = RandomDefines +#elif defined(OpenBSDArchitecture) +RANDOM_DEFINES = -DARC4_RANDOM +#elif defined(LinuxArchitecture) +RANDOM_DEFINES = -DDEV_RANDOM=\"/dev/urandom\" +#elif defined(NetBSDArchitecture) && \ + ((OSMajorVersion > 1) || \ + (OSMajorVersion == 1 && OSMinorVersion > 3)) +RANDOM_DEFINES = -DDEV_RANDOM=\"/dev/urandom\" +#endif + + +#if HasSetUserContext +USER_CONTEXT_DEFINES = -DHAS_SETUSERCONTEXT +# XXX - only FreeBSD has this in libutil +SYS_LIBRARIES1 = -lutil +#endif + +#if HasSetProcTitle +PROCTITLE_DEFINES = -DHAS_SETPROCTITLE +#endif + + SYS_LIBRARIES = $(SYS_LIBRARIES1) $(SYS_LIBRARIES2) $(SYS_LIBRARIES3) + + INCLUDES = $(KRB5_INCLUDE) + DEPLIBS = $(DEPXLIB) $(DEPXAUTHLIB) $(DEPXDMCPLIB) + LOCAL_LIBRARIES = $(XLIB) $(XAUTHLIB) \ + $(XDMCPLIB) $(RPCLIB) $(PAM_LIBRARIES) \ + $(CRYPT_LIBRARIES) $(KRBIVLIB) + + COMMSRCS = auth.c daemon.c server.c dpylist.c dm.c error.c \ + netaddr.c reset.c resource.c protodpy.c policy.c \ + session.c socket.c streams.c util.c xdmcp.c \ + process.c mitauth.c \ + genauth.c access.c choose.c \ + $(XDMAUTHSRCS) $(RPCSRCS) $(KRB5SRCS) + COMMOBJS = auth.o daemon.o server.o dpylist.o dm.o error.o \ + netaddr.o reset.o resource.o protodpy.o policy.o \ + session.o socket.o streams.o util.o xdmcp.o \ + process.o mitauth.o \ + genauth.o access.o choose.o \ + $(XDMAUTHOBJS) $(RPCOBJS) $(KRB5OBJS) + + SRCS1 = $(COMMSRCS) client.c + OBJS1 = $(COMMOBJS) client.o + +#if BuildBoth + SRCS2 = $(COMMSRCS) clientsh.c + OBJS2 = $(COMMOBJS) clientsh.o + + XDM_SHADOW = xdm-shadow +#endif + + PROGRAMS = xdm $(XDM_SHADOW) + + + OSMAJORVERSION = OSMajorVersion + OSMINORVERSION = OSMinorVersion + CONN_DEFINES = $(CONNECTION_FLAGS) + DEFINES = $(SIGNAL_DEFINES) $(LOG_DEFINES) \ + $(CRYPT_DEFINES)$(PWD_DEFINES) \ + $(BSDAUTH_DEFINES) $(PAM_DEFINES) $(USER_CONTEXT_DEFINES) \ + $(XDMAUTH_DEFINES) $(RPC_DEFINES) $(KRB5_DEFINES) \ + $(XDMCP_DEFINES) $(SOCK_DEFINES) $(CONN_DEFINES) \ + $(FRAGILE_DEFINES) $(RANDOM_DEFINES) $(PROCTITLE_DEFINES) \ + -DOSMAJORVERSION=$(OSMAJORVERSION) -DOSMINORVERSION=$(OSMINORVERSION) \ + -Dconst= + +ComplexProgramTarget_1(xdm,$(LOCAL_LIBRARIES),NullParameter) +#if BuildBoth +NormalProgramTarget(xdm-shadow,$(OBJS2),$(DEPLIBS),$(LOCAL_LIBRARIES),-lshadow) +InstallProgram(xdm-shadow,$(BINDIR)) +ObjectFromSpecialSource(clientsh,client,-DUSESHADOW) +#endif + +#if defined(FreeBSDArchitecture) && (OSMajorVersion < 2) +XCOMM only for daemon.c? it's used in some other places, too. +SpecialCObjectRule(daemon,$(ICONFIGFILES),-UCSRG_BASED) +#endif + diff --git a/kdm/backend/Makefile.am b/kdm/backend/Makefile.am new file mode 100644 index 000000000..81dc2702f --- /dev/null +++ b/kdm/backend/Makefile.am @@ -0,0 +1,46 @@ +# forcibly remove thread-related defines & flags +AUTOMAKE_OPTIONS = foreign +CPPFLAGS = $(USER_INCLUDES) $(X_INCLUDES) $(KRB4_INCS) $(KRB5_INCS) -I.. -I../.. +LDFLAGS = $(USER_LDFLAGS) $(X_LDFLAGS) $(X_RPATH) $(KRB4_RPATH) $(KRB5_RPATH) +LDADD = $(LIB_X11) -lXau $(LIBXDMCP) $(PASSWDLIBS) $(LIBSHADOW) $(LIBGEN) \ + $(LIB_LIBS) $(KRB4_LIBS) $(KRB5_LIBS) $(LIBSOCKET) $(LIBRESOLV) \ + $(LIBUCB) $(LIBUTIL) $(LIBPOSIX4) + +bin_PROGRAMS = kdm +kdm_SOURCES = \ + access.c \ + auth.c \ + bootman.c \ + choose.c \ + client.c \ + ctrl.c \ + daemon.c \ + dm.c \ + dpylist.c \ + error.c \ + genauth.c \ + inifile.c \ + krb5auth.c \ + mitauth.c \ + netaddr.c \ + policy.c \ + process.c \ + protodpy.c \ + reset.c \ + resource.c \ + rpcauth.c \ + server.c \ + session.c \ + sessreg.c \ + socket.c \ + streams.c \ + util.c \ + xdmauth.c \ + xdmcp.c + +EXTRA_DIST = printf.c + +noinst_HEADERS = dm.h dm_socket.h dm_error.h dm_auth.h greet.h + +# for unsermake (automake is handled by SUBDIRS in ../) +kdm_COMPILE_FIRST = ../config.ci diff --git a/kdm/backend/access.c b/kdm/backend/access.c new file mode 100644 index 000000000..82736be57 --- /dev/null +++ b/kdm/backend/access.c @@ -0,0 +1,468 @@ +/* + +Copyright 1990, 1998 The Open Group +Copyright 2001,2004 Oswald Buddenhagen <[email protected]> +Copyright 2002 Sun Microsystems, Inc. All rights reserved. + +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 + * + * Access control for XDMCP - keep a database of allowable display addresses + * and (potentially) a list of hosts to send ForwardQuery packets to + */ + +#include <config.h> + +#ifdef XDMCP + +#include "dm.h" +#include "dm_error.h" +#include "dm_socket.h" + +#include <stdio.h> +#include <ctype.h> + +#include <netdb.h> +#if defined(IPv6) && defined(AF_INET6) +# include <arpa/inet.h> +#endif + +typedef struct { + short int type; + union { + char *aliasPattern; + char *hostPattern; + struct _displayAddress { + CARD16 connectionType; + ARRAY8 hostAddress; + } displayAddress; + } entry; +} HostEntry; + +typedef struct { + short int iface; + short int mcasts; + short int nmcasts; +} ListenEntry; + +typedef struct { + char *name; + short int hosts; + short int nhosts; +} AliasEntry; + +typedef struct { + short int entries; + short int nentries; + short int hosts; + short int nhosts; + short int flags; +} AclEntry; + +typedef struct { + HostEntry *hostList; + ListenEntry *listenList; + AliasEntry *aliasList; + AclEntry *acList; + short int nHosts, nListens, nAliases, nAcls; + CfgDep dep; +} AccArr; + +static AccArr accData[1]; + + +static ARRAY8 localAddress; + +ARRAY8Ptr +getLocalAddress( void ) +{ + static int haveLocalAddress; + + if (!haveLocalAddress) { +#if defined(IPv6) && defined(AF_INET6) + struct addrinfo *ai; + + if (getaddrinfo( localHostname(), NULL, NULL, &ai )) { + XdmcpAllocARRAY8( &localAddress, 4 ); + localAddress.data[0] = 127; + localAddress.data[1] = 0; + localAddress.data[2] = 0; + localAddress.data[3] = 1; + } else { + if (ai->ai_family == AF_INET) { + XdmcpAllocARRAY8( &localAddress, sizeof(struct in_addr) ); + memcpy( localAddress.data, + &((struct sockaddr_in *)ai->ai_addr)->sin_addr, + sizeof(struct in_addr) ); + } else /* if (ai->ai_family == AF_INET6) */ { + XdmcpAllocARRAY8( &localAddress, sizeof(struct in6_addr) ); + memcpy( localAddress.data, + &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr, + sizeof(struct in6_addr) ); + } + freeaddrinfo( ai ); +#else + struct hostent *hostent; + + if ((hostent = gethostbyname( localHostname() ))) { + XdmcpAllocARRAY8( &localAddress, hostent->h_length ); + memmove( localAddress.data, hostent->h_addr, hostent->h_length ); +#endif + haveLocalAddress = 1; + } + } + return &localAddress; +} + + +void +ScanAccessDatabase( int force ) +{ + struct _displayAddress *da; + char *cptr; + int nChars, i; + + Debug( "ScanAccessDatabase\n" ); + if (Setjmp( cnftalk.errjmp )) + return; /* may memleak */ + if (startConfig( GC_gXaccess, &accData->dep, force ) <= 0) + return; + if (accData->hostList) + free( accData->hostList ); + accData->nHosts = GRecvInt(); + accData->nListens = GRecvInt(); + accData->nAliases = GRecvInt(); + accData->nAcls = GRecvInt(); + nChars = GRecvInt(); + if (!(accData->hostList = (HostEntry *) + Malloc( accData->nHosts * sizeof(HostEntry) + + accData->nListens * sizeof(ListenEntry) + + accData->nAliases * sizeof(AliasEntry) + + accData->nAcls * sizeof(AclEntry) + + nChars ))) + { + CloseGetter(); + return; + } + accData->listenList = (ListenEntry *)(accData->hostList + accData->nHosts); + accData->aliasList = (AliasEntry *)(accData->listenList + accData->nListens); + accData->acList = (AclEntry *)(accData->aliasList + accData->nAliases); + cptr = (char *)(accData->acList + accData->nAcls); + for (i = 0; i < accData->nHosts; i++) { + switch ((accData->hostList[i].type = GRecvInt())) { + case HOST_ALIAS: + accData->hostList[i].entry.aliasPattern = cptr; + cptr += GRecvStrBuf( cptr ); + break; + case HOST_PATTERN: + accData->hostList[i].entry.hostPattern = cptr; + cptr += GRecvStrBuf( cptr ); + break; + case HOST_ADDRESS: + da = &accData->hostList[i].entry.displayAddress; + da->hostAddress.data = (unsigned char *)cptr; + cptr += (da->hostAddress.length = GRecvArrBuf( cptr )); + switch (GRecvInt()) + { +#ifdef AF_INET + case AF_INET: + da->connectionType = FamilyInternet; + break; +#endif +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: + da->connectionType = FamilyInternet6; + break; +#endif +#ifdef AF_DECnet + case AF_DECnet: + da->connectionType = FamilyDECnet; + break; +#endif +/*#ifdef AF_UNIX + case AF_UNIX: +#endif*/ + default: + da->connectionType = FamilyLocal; + break; + } + break; + case HOST_BROADCAST: + break; + default: + LogError( "Received unknown host type %d from config reader\n", accData->hostList[i].type ); + return; + } + } + for (i = 0; i < accData->nListens; i++) { + accData->listenList[i].iface = GRecvInt(); + accData->listenList[i].mcasts = GRecvInt(); + accData->listenList[i].nmcasts = GRecvInt(); + } + for (i = 0; i < accData->nAliases; i++) { + accData->aliasList[i].name = cptr; + cptr += GRecvStrBuf( cptr ); + accData->aliasList[i].hosts = GRecvInt(); + accData->aliasList[i].nhosts = GRecvInt(); + } + for (i = 0; i < accData->nAcls; i++) { + accData->acList[i].entries = GRecvInt(); + accData->acList[i].nentries = GRecvInt(); + accData->acList[i].hosts = GRecvInt(); + accData->acList[i].nhosts = GRecvInt(); + accData->acList[i].flags = GRecvInt(); + } +} + + +/* Returns non-0 if string is matched by pattern. Does case folding. + */ +static int +patternMatch( const char *string, const char *pattern ) +{ + int p, s; + + if (!string) + string = ""; + + for (;;) { + s = *string++; + switch (p = *pattern++) { + case '*': + if (!*pattern) + return 1; + for (string--; *string; string++) + if (patternMatch( string, pattern )) + return 1; + return 0; + case '?': + if (s == '\0') + return 0; + break; + case '\0': + return s == '\0'; + case '\\': + p = *pattern++; + /* fall through */ + default: + if (tolower( p ) != tolower( s )) + return 0; + } + } +} + + +#define MAX_DEPTH 32 + +static void +scanHostlist( int fh, int nh, + ARRAY8Ptr clientAddress, CARD16 connectionType, + ChooserFunc function, char *closure, + int broadcast, int *haveLocalhost ) +{ + HostEntry *h; + AliasEntry *a; + int na; + + for (h = accData->hostList + fh; nh; nh--, h++) { + switch (h->type) { + case HOST_ALIAS: + for (a = accData->aliasList, na = accData->nAliases; na; na--, a++) + if (patternMatch( a->name, h->entry.aliasPattern )) /* XXX originally swapped, no wildcards in alias name matching */ + scanHostlist( a->hosts, a->nhosts, + clientAddress, connectionType, + function, closure, broadcast, + haveLocalhost ); + break; + case HOST_ADDRESS: + if (XdmcpARRAY8Equal( getLocalAddress(), &h->entry.displayAddress.hostAddress )) + *haveLocalhost = 1; + else if (function) + (*function)( connectionType, &h->entry.displayAddress.hostAddress, closure ); + break; + case HOST_BROADCAST: + if (broadcast && function) + (*function)( FamilyBroadcast, 0, closure ); + break; + default: + break; + } + } +} + +static int +scanEntrylist( int fh, int nh, + ARRAY8Ptr clientAddress, CARD16 connectionType, + char **clientName ) +{ + HostEntry *h; + AliasEntry *a; + int na; + + for (h = accData->hostList + fh; nh; nh--, h++) { + switch (h->type) { + case HOST_ALIAS: + for (a = accData->aliasList, na = accData->nAliases; na; na--, a++) + if (patternMatch( a->name, h->entry.aliasPattern )) + if (scanEntrylist( a->hosts, a->nhosts, + clientAddress, connectionType, + clientName )) + return 1; + break; + case HOST_PATTERN: + if (!*clientName) + *clientName = NetworkAddressToHostname( connectionType, + clientAddress ); + if (patternMatch( *clientName, h->entry.hostPattern )) + return 1; + break; + case HOST_ADDRESS: + if (h->entry.displayAddress.connectionType == connectionType && + XdmcpARRAY8Equal( &h->entry.displayAddress.hostAddress, + clientAddress )) + return 1; + break; + default: + break; + } + } + return 0; +} + +static AclEntry * +matchAclEntry( ARRAY8Ptr clientAddress, CARD16 connectionType, int direct ) +{ + AclEntry *e, *re; + char *clientName = 0; + int ne; + + for (e = accData->acList, ne = accData->nAcls, re = 0; ne; ne--, e++) + if (!e->nhosts == direct) + if (scanEntrylist( e->entries, e->nentries, + clientAddress, connectionType, + &clientName )) + { + re = e; + break; + } + if (clientName) + free( clientName ); + return re; +} + +/* + * calls the given function for each valid indirect entry. Returns TRUE if + * the local host exists on any of the lists, else FALSE + */ +int +ForEachMatchingIndirectHost( ARRAY8Ptr clientAddress, + CARD16 connectionType, + ChooserFunc function, char *closure ) +{ + AclEntry *e; + int haveLocalhost = 0; + + e = matchAclEntry( clientAddress, connectionType, 0 ); + if (e && !(e->flags & a_notAllowed)) { + if (e->flags & a_useChooser) { + ARRAY8Ptr choice; + + choice = IndirectChoice( clientAddress, connectionType ); + if (!choice || XdmcpARRAY8Equal( getLocalAddress(), choice )) + haveLocalhost = 1; + else + (*function)( connectionType, choice, closure ); + } else + scanHostlist( e->hosts, e->nhosts, clientAddress, connectionType, + function, closure, FALSE, &haveLocalhost ); + } + return haveLocalhost; +} + +int +UseChooser( ARRAY8Ptr clientAddress, CARD16 connectionType ) +{ + AclEntry *e; + + e = matchAclEntry( clientAddress, connectionType, 0 ); + return e && !(e->flags & a_notAllowed) && (e->flags & a_useChooser) && + !IndirectChoice( clientAddress, connectionType ); +} + +void +ForEachChooserHost( ARRAY8Ptr clientAddress, CARD16 connectionType, + ChooserFunc function, char *closure ) +{ + AclEntry *e; + int haveLocalhost = 0; + + e = matchAclEntry( clientAddress, connectionType, 0 ); + if (e && !(e->flags & a_notAllowed) && (e->flags & a_useChooser)) + scanHostlist( e->hosts, e->nhosts, clientAddress, connectionType, + function, closure, TRUE, &haveLocalhost ); + if (haveLocalhost) + (*function)( connectionType, getLocalAddress(), closure ); +} + +/* + * returns TRUE if the given client is acceptable to the local host. The + * given display client is acceptable if it occurs without a host list. + */ +int +AcceptableDisplayAddress( ARRAY8Ptr clientAddress, CARD16 connectionType, + xdmOpCode type ) +{ + AclEntry *e; + + if (type == INDIRECT_QUERY) + return 1; + + e = matchAclEntry( clientAddress, connectionType, 1 ); + return e && !(e->flags & a_notAllowed) && + (type != BROADCAST_QUERY || !(e->flags & a_notBroadcast)); +} + +void +ForEachListenAddr( ListenFunc listenfunction, ListenFunc mcastfunction, + void **closure ) +{ + int i, j, ifc, mc, nmc; + + for (i = 0; i < accData->nListens; i++) { + ifc = accData->listenList[i].iface; + (*listenfunction)( ifc < 0 ? 0 : + &accData->hostList[ifc].entry.displayAddress.hostAddress, + closure ); + mc = accData->listenList[i].mcasts; + nmc = accData->listenList[i].nmcasts; + for (j = 0; j < nmc; j++, mc++) + (*mcastfunction)( &accData->hostList[mc].entry.displayAddress.hostAddress, + closure ); + } +} +#endif /* XDMCP */ diff --git a/kdm/backend/auth.c b/kdm/backend/auth.c new file mode 100644 index 000000000..fa7afac01 --- /dev/null +++ b/kdm/backend/auth.c @@ -0,0 +1,1247 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2000-2004 Oswald Buddenhagen <[email protected]> + +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 + * + * maintain the authorization generation daemon + */ + +#include "dm.h" +#include "dm_auth.h" +#include "dm_error.h" + +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> + +#include <sys/ioctl.h> + +#include "dm_socket.h" +#ifdef DNETCONN +# include <netdnet/dnetdb.h> +#endif + +#if (defined(_POSIX_SOURCE) && !defined(_AIX) && !defined(__QNX__)) || defined(__hpux) || defined(__svr4__) /* XXX */ +# define NEED_UTSNAME +# include <sys/utsname.h> +#endif + +#ifdef __GNU__ +# include <netdb.h> +# undef SIOCGIFCONF +#else /* __GNU__ */ +# include <net/if.h> +# ifdef __svr4__ +# include <netdb.h> +# include <sys/sockio.h> +# include <sys/stropts.h> +# endif +# ifdef __EMX__ +# define link rename +# define chown(a,b,c) +# include <io.h> +# endif +#endif /* __GNU__ */ + +struct AuthProtocol { + unsigned short name_length; + const char *name; + void (*InitAuth)( unsigned short len, const char *name ); + Xauth *(*GetAuth)( unsigned short len, const char *name ); +#ifdef XDMCP + void (*GetXdmcpAuth)( struct protoDisplay *pdpy, + unsigned short authorizationNameLen, + const char *authorizationName ); +#endif + int inited; +}; + +#ifdef XDMCP +# define xdmcpauth(arg) , arg +#else +# define xdmcpauth(arg) +#endif + +static struct AuthProtocol AuthProtocols[] = { +{ (unsigned short)18, "MIT-MAGIC-COOKIE-1", + MitInitAuth, MitGetAuth xdmcpauth(NULL), 0 +}, +#ifdef HASXDMAUTH +{ (unsigned short)19, "XDM-AUTHORIZATION-1", + XdmInitAuth, XdmGetAuth xdmcpauth(XdmGetXdmcpAuth), 0 +}, +#endif +#ifdef SECURE_RPC +{ (unsigned short)9, "SUN-DES-1", + SecureRPCInitAuth, SecureRPCGetAuth xdmcpauth(NULL), 0 +}, +#endif +#ifdef K5AUTH +{ (unsigned short)14, "MIT-KERBEROS-5", + Krb5InitAuth, Krb5GetAuth xdmcpauth(NULL), 0 +}, +#endif +}; + +static struct AuthProtocol * +findProtocol( unsigned short name_length, const char *name ) +{ + unsigned i; + + for (i = 0; i < as(AuthProtocols); i++) + if (AuthProtocols[i].name_length == name_length && + memcmp( AuthProtocols[i].name, name, name_length ) == 0) + { + return &AuthProtocols[i]; + } + return (struct AuthProtocol *)0; +} + +int +ValidAuthorization( unsigned short name_length, const char *name ) +{ + if (findProtocol( name_length, name )) + return TRUE; + return FALSE; +} + +static Xauth * +GenerateAuthorization( unsigned short name_length, const char *name ) +{ + struct AuthProtocol *a; + Xauth *auth = 0; + + Debug( "GenerateAuthorization %s\n", name ); + if ((a = findProtocol( name_length, name ))) { + if (!a->inited) { + (*a->InitAuth)( name_length, name ); + a->inited = TRUE; + } + auth = (*a->GetAuth)( name_length, name ); + if (auth) { + Debug( "got %p (%d %.*s) %02[*hhx\n", auth, + auth->name_length, auth->name_length, auth->name, + auth->data_length, auth->data ); + } else + Debug( "got (null)\n" ); + } else + Debug( "unknown authorization %s\n", name ); + return auth; +} + +#ifdef XDMCP + +void +SetProtoDisplayAuthorization( struct protoDisplay *pdpy, + unsigned short authorizationNameLen, + const char *authorizationName ) +{ + struct AuthProtocol *a; + Xauth *auth; + + a = findProtocol( authorizationNameLen, authorizationName ); + pdpy->xdmcpAuthorization = pdpy->fileAuthorization = 0; + if (a) { + if (!a->inited) { + (*a->InitAuth)( authorizationNameLen, authorizationName ); + a->inited = TRUE; + } + if (a->GetXdmcpAuth) { + (*a->GetXdmcpAuth)( pdpy, authorizationNameLen, authorizationName ); + auth = pdpy->xdmcpAuthorization; + } else { + auth = (*a->GetAuth)( authorizationNameLen, authorizationName ); + pdpy->fileAuthorization = auth; + pdpy->xdmcpAuthorization = 0; + } + if (auth) + Debug( "got %p (%d %.*s)\n", auth, + auth->name_length, auth->name_length, auth->name ); + else + Debug( "got (null)\n" ); + } +} + +#endif /* XDMCP */ + +void +CleanUpFileName( const char *src, char *dst, int len ) +{ + while (*src) { + if (--len <= 0) + break; + switch (*src & 0x7f) { + case '/': + *dst++ = '_'; + break; + case '-': + *dst++ = '.'; + break; + default: + *dst++ = (*src & 0x7f); + } + ++src; + } + *dst = '\0'; +} + + +static FILE * +fdOpenW( int fd ) +{ + FILE *f; + + if (fd >= 0) { + if ((f = fdopen( fd, "w" ))) + return f; + close( fd ); + } + return 0; +} + + +#define NAMELEN 255 + +static FILE * +MakeServerAuthFile( struct display *d ) +{ + FILE *f; +#ifndef HAVE_MKSTEMP + int r; +#endif + char cleanname[NAMELEN], nambuf[NAMELEN+128]; + + /* + * Some paranoid, but still not sufficient (DoS was still possible) + * checks used to be here. I removed all this stuff because + * a) authDir is supposed to be /var/run/xauth (=safe) or similar and + * b) even if it's not (say, /tmp), we create files safely (hopefully). + */ + if (mkdir( authDir, 0755 ) < 0 && errno != EEXIST) + return 0; + CleanUpFileName( d->name, cleanname, NAMELEN - 8 ); +#ifdef HAVE_MKSTEMP + sprintf( nambuf, "%s/A%s-XXXXXX", authDir, cleanname ); + if ((f = fdOpenW( mkstemp( nambuf ) ))) { + StrDup( &d->authFile, nambuf ); + return f; + } +#else + for (r = 0; r < 100; r++) { + sprintf( nambuf, "%s/A%s-XXXXXX", authDir, cleanname ); + (void)mktemp( nambuf ); + if ((f = fdOpenW( open( nambuf, O_WRONLY | O_CREAT | O_EXCL, 0600 ) ))) { + StrDup( &d->authFile, nambuf ); + return f; + } + } +#endif + return 0; +} + +int +SaveServerAuthorizations( struct display *d, Xauth **auths, int count ) +{ + FILE *auth_file; + int i; + + if (!d->authFile && d->clientAuthFile && *d->clientAuthFile) + StrDup( &d->authFile, d->clientAuthFile ); + if (d->authFile) { + if (!(auth_file = fdOpenW( creat( d->authFile, 0600 ) ))) { + LogError( "Cannot open X server authorization file %s\n", d->authFile ); + free( d->authFile ); + d->authFile = NULL; + return FALSE; + } + } else { + if (!(auth_file = MakeServerAuthFile( d ))) { + LogError( "Cannot create X server authorization file\n" ); + return FALSE; + } + } + Debug( "file: %s auth: %p\n", d->authFile, auths ); + for (i = 0; i < count; i++) { + /* + * User-based auths may not have data until + * a user logs in. In which case don't write + * to the auth file so xrdb and setup programs don't fail. + */ + if (auths[i]->data_length > 0) + if (!XauWriteAuth( auth_file, auths[i] ) || + fflush( auth_file ) == EOF) + { + fclose( auth_file ); + LogError( "Cannot write X server authorization file %s\n", + d->authFile ); + free( d->authFile ); + d->authFile = NULL; + return FALSE; + } + } + fclose( auth_file ); + return TRUE; +} + +void +SetLocalAuthorization( struct display *d ) +{ + Xauth *auth, **auths; + int i, j; + + if (d->authorizations) + { + for (i = 0; i < d->authNum; i++) + XauDisposeAuth( d->authorizations[i] ); + free( (char *)d->authorizations ); + d->authorizations = (Xauth **)NULL; + d->authNum = 0; + } + Debug( "SetLocalAuthorization %s, auths %[s\n", d->name, d->authNames ); + if (!d->authNames) + return; + for (i = 0; d->authNames[i]; i++) + ; + d->authNameNum = i; + if (d->authNameLens) + free( (char *)d->authNameLens ); + d->authNameLens = (unsigned short *)Malloc( d->authNameNum * sizeof(unsigned short) ); + if (!d->authNameLens) + return; + for (i = 0; i < d->authNameNum; i++) + d->authNameLens[i] = strlen( d->authNames[i] ); + auths = (Xauth **)Malloc( d->authNameNum * sizeof(Xauth *) ); + if (!auths) + return; + j = 0; + for (i = 0; i < d->authNameNum; i++) { + auth = GenerateAuthorization( d->authNameLens[i], d->authNames[i] ); + if (auth) + auths[j++] = auth; + } + if (SaveServerAuthorizations( d, auths, j )) { + d->authorizations = auths; + d->authNum = j; + } else { + for (i = 0; i < j; i++) + XauDisposeAuth( auths[i] ); + free( (char *)auths ); + } +} + +/* + * Set the authorization to use for xdm's initial connection + * to the X server. Cannot use user-based authorizations + * because no one has logged in yet, so we don't have any + * user credentials. + * Well, actually we could use SUN-DES-1 because we tell the server + * to allow root in. This is bogus and should be fixed. + */ +void +SetAuthorization( struct display *d ) +{ + register Xauth **auth = d->authorizations; + int i; + + for (i = 0; i < d->authNum; i++) { + if (auth[i]->name_length == 9 && + memcmp( auth[i]->name, "SUN-DES-1", 9 ) == 0) + continue; + if (auth[i]->name_length == 14 && + memcmp( auth[i]->name, "MIT-KERBEROS-5", 14 ) == 0) + continue; + XSetAuthorization( auth[i]->name, (int)auth[i]->name_length, + auth[i]->data, (int)auth[i]->data_length ); + } +} + +static int +openFiles( const char *name, char *new_name, FILE **oldp, FILE **newp ) +{ + strcat( strcpy( new_name, name ), "-n" ); + if (!(*newp = + fdOpenW( creat( new_name, 0600 ) ))) { + Debug( "can't open new file %s\n", new_name ); + return 0; + } + *oldp = fopen( name, "r" ); + Debug( "opens succeeded %s %s\n", name, new_name ); + return 1; +} + +struct addrList { + struct addrList *next; + unsigned short family, address_length, number_length; + char data[1]; +}; + +static struct addrList *addrs; + +static void +initAddrs( void ) +{ + addrs = 0; +} + +static void +doneAddrs( void ) +{ + struct addrList *a, *n; + for (a = addrs; a; a = n) { + n = a->next; + free( a ); + } +} + +static void +saveEntry( Xauth *auth ) +{ + struct addrList *new; + + if (!(new = Malloc( offsetof(struct addrList, data) + + auth->address_length + auth->number_length ))) + return; + new->address_length = auth->address_length; + new->number_length = auth->number_length; + memcpy( new->data, auth->address, (int)auth->address_length ); + memcpy( new->data + (int)auth->address_length, + auth->number, (int)auth->number_length ); + new->family = auth->family; + new->next = addrs; + addrs = new; +} + +static int +checkEntry( Xauth *auth ) +{ + struct addrList *a; + + for (a = addrs; a; a = a->next) + if (a->family == auth->family && + a->address_length == auth->address_length && + !memcmp( a->data, auth->address, auth->address_length ) && + a->number_length == auth->number_length && + !memcmp( a->data + a->address_length, + auth->number, auth->number_length )) + return 1; + return 0; +} + +static void +writeAuth( FILE *file, Xauth *auth, int *ok ) +{ + if (debugLevel & DEBUG_AUTH) /* normally too verbose */ + Debug( "writeAuth: doWrite = %d\n" + "family: %d\n" + "addr: %02[*:hhx\n" + "number: %02[*:hhx\n" + "name: %02[*:hhx\n" + "data: %02[*:hhx\n", + ok != 0, auth->family, + auth->address_length, auth->address, + auth->number_length, auth->number, + auth->name_length, auth->name, + auth->data_length, auth->data ); + if (ok && !XauWriteAuth( file, auth )) + *ok = FALSE; +} + +static void +writeAddr( int family, int addr_length, char *addr, + FILE *file, Xauth *auth, int *ok ) +{ + auth->family = (unsigned short)family; + auth->address_length = addr_length; + auth->address = addr; + Debug( "writeAddr: writing and saving an entry\n" ); + writeAuth( file, auth, ok ); + if (!checkEntry( auth )) + saveEntry( auth ); +} + +static void +DefineLocal( FILE *file, Xauth *auth, int *ok ) +{ +#if !defined(NEED_UTSNAME) || defined(__hpux) + char displayname[100]; +#endif +#ifdef NEED_UTSNAME + struct utsname name; +#endif + + /* stolen from xinit.c */ + +/* Make sure this produces the same string as _XGetHostname in lib/X/XlibInt.c. + * Otherwise, Xau will not be able to find your cookies in the Xauthority file. + * + * Note: POSIX says that the ``nodename'' member of utsname does _not_ have + * to have sufficient information for interfacing to the network, + * and so, you may be better off using gethostname (if it exists). + */ + +#ifdef NEED_UTSNAME + + /* hpux: + * Why not use gethostname()? Well, at least on my system, I've had to + * make an ugly kernel patch to get a name longer than 8 characters, and + * uname() lets me access to the whole string (it smashes release, you + * see), whereas gethostname() kindly truncates it for me. + */ + uname( &name ); + writeAddr( FamilyLocal, strlen( name.nodename ), name.nodename, + file, auth, ok ); +#endif + +#if !defined(NEED_UTSNAME) || defined(__hpux) + /* _AIX: + * In _AIX, _POSIX_SOURCE is defined, but uname gives only first + * field of hostname. Thus, we use gethostname instead. + */ + + /* + * For HP-UX, HP's Xlib expects a fully-qualified domain name, which + * is achieved by using gethostname(). For compatability, we must + * also still create the entry using uname() above. + */ + displayname[0] = 0; + if (!gethostname( displayname, sizeof(displayname) )) + displayname[sizeof(displayname) - 1] = 0; + +# ifdef NEED_UTSNAME + /* + * If gethostname and uname both returned the same name, + * do not write a duplicate entry. + */ + if (strcmp( displayname, name.nodename )) +# endif + writeAddr( FamilyLocal, strlen( displayname ), displayname, + file, auth, ok ); +#endif +} + +#ifdef SYSV_SIOCGIFCONF + +/* Deal with different SIOCGIFCONF ioctl semantics on SYSV, SVR4 */ + +int +ifioctl (int fd, int cmd, char *arg) +{ + struct strioctl ioc; + int ret; + + bzero( (char *)&ioc, sizeof(ioc) ); + ioc.ic_cmd = cmd; + ioc.ic_timout = 0; + if (cmd == SIOCGIFCONF) { + ioc.ic_len = ((struct ifconf *)arg)->ifc_len; + ioc.ic_dp = ((struct ifconf *)arg)->ifc_buf; + } else { + ioc.ic_len = sizeof(struct ifreq); + ioc.ic_dp = arg; + } + ret = ioctl( fd, I_STR, (char *)&ioc ); + if (ret >= 0 && cmd == SIOCGIFCONF) + ((struct ifconf *)arg)->ifc_len = ioc.ic_len; + return (ret); +} + +#endif /* SYSV_SIOCGIFCONF */ + +#ifdef HAVE_GETIFADDRS +# include <ifaddrs.h> + +static void +DefineSelf( FILE *file, Xauth *auth, int *ok ) +{ + struct ifaddrs *ifap, *ifr; + char *addr; + int family, len; + + if (getifaddrs( &ifap ) < 0) + return; + for (ifr = ifap; ifr; ifr = ifr->ifa_next) { + if (!ifr->ifa_addr) + continue; + family = ConvertAddr( (char *)(ifr->ifa_addr), &len, &addr ); + if (family == -1 || family == FamilyLocal) + continue; + /* + * don't write out 'localhost' entries, as + * they may conflict with other local entries. + * DefineLocal will always be called to add + * the local entry anyway, so this one can + * be tossed. + */ + if (family == FamilyInternet && + addr[0] == 127 && addr[1] == 0 && addr[2] == 0 && addr[3] == 1) + { + Debug( "Skipping localhost address\n" ); + continue; + } +# if defined(IPv6) && defined(AF_INET6) + if (family == FamilyInternet6) { + if (IN6_IS_ADDR_LOOPBACK( ((struct in6_addr *)addr) )) { + Debug( "Skipping IPv6 localhost address\n" ); + continue; + } + /* Also skip XDM-AUTHORIZATION-1 */ + if (auth->name_length == 19 && + !memcmp( auth->name, "XDM-AUTHORIZATION-1", 19 )) { + Debug( "Skipping IPv6 XDM-AUTHORIZATION-1\n" ); + continue; + } + } +# endif + writeAddr( family, len, addr, file, auth, ok ); + } + freeifaddrs( ifap ); +} +#else /* GETIFADDRS */ + +#if defined(STREAMSCONN) && !defined(SYSV_SIOCGIFCONF) && !defined(WINTCP) + +#include <tiuser.h> + +/* Define this host for access control. Find all the hosts the OS knows about + * for this fd and add them to the selfhosts list. + * TLI version, written without sufficient documentation. + */ +static void +DefineSelf( int fd, FILE *file, Xauth *auth, int *ok ) +{ + struct netbuf netb; + char addrret[1024]; /* easier than t_alloc */ + + netb.maxlen = sizeof(addrret); + netb.buf = addrret; + if (t_getname( fd, &netb, LOCALNAME ) == -1) + t_error( "t_getname" ); + /* what a kludge */ + writeAddr( FamilyInternet, 4, netb.buf+4, file, auth, ok ); +} + +#else + +#ifdef WINTCP /* NCR with Wollongong TCP */ + +#include <stropts.h> +#include <tiuser.h> + +#include <sys/stream.h> +#include <net/if.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#include <netinet/in.h> +#include <netinet/in_var.h> + +static void +DefineSelf( int fd, FILE *file, Xauth *auth, int *ok ) +{ + /* + * The Wollongong drivers used by NCR SVR4/MP-RAS don't understand the + * socket IO calls that most other drivers seem to like. Because of + * this, this routine must be special cased for NCR. Eventually, + * this will be cleared up. + */ + + struct ipb ifnet; + struct in_ifaddr ifaddr; + struct strioctl str; + unsigned char *addr; + int len, ipfd; + + if ((ipfd = open( "/dev/ip", O_RDWR, 0 )) < 0) { + LogError( "Trouble getting interface configuration\n" ); + return; + } + + /* Indicate that we want to start at the begining */ + ifnet.ib_next = (struct ipb *)1; + + while (ifnet.ib_next) { + str.ic_cmd = IPIOC_GETIPB; + str.ic_timout = 0; + str.ic_len = sizeof(struct ipb); + str.ic_dp = (char *)&ifnet; + + if (ioctl( ipfd, (int)I_STR, (char *)&str ) < 0) { + close( ipfd ); + LogError( "Trouble getting interface configuration\n" ); + return; + } + + ifaddr.ia_next = (struct in_ifaddr *)ifnet.if_addrlist; + str.ic_cmd = IPIOC_GETINADDR; + str.ic_timout = 0; + str.ic_len = sizeof(struct in_ifaddr); + str.ic_dp = (char *)&ifaddr; + + if (ioctl( ipfd, (int)I_STR, (char *)&str ) < 0) { + close( ipfd ); + LogError( "Trouble getting interface configuration\n" ); + return; + } + + /* + * Ignore the 127.0.0.1 entry. + */ + if (IA_SIN( &ifaddr )->sin_addr.s_addr == htonl( 0x7f000001 ) ) + continue; + + writeAddr( FamilyInternet, 4, (char *)&(IA_SIN( &ifaddr )->sin_addr), + file, auth, ok ); + + } + close( ipfd ); +} + +#else /* WINTCP */ + +/* Solaris provides an extended interface SIOCGLIFCONF. Other systems + * may have this as well, but the code has only been tested on Solaris + * so far, so we only enable it there. Other platforms may be added as + * needed. + * + * Test for Solaris commented out -- TSI @ UQV 2003.06.13 + */ +#ifdef SIOCGLIFCONF +/* #if defined(sun) */ +# define USE_SIOCGLIFCONF +/* #endif */ +#endif + +#if defined(SIOCGIFCONF) || defined (USE_SIOCGLIFCONF) + +#if !defined(SYSV_SIOCGIFCONF) || defined(USE_SIOCGLIFCONF) +# define ifioctl ioctl +#endif + +#ifdef USE_SIOCGLIFCONF +# define ifr_type struct lifreq +#else +# define ifr_type struct ifreq +#endif + +/* Handle variable length ifreq in BNR2 and later */ +#ifdef VARIABLE_IFREQ +# define ifr_size(p) (sizeof(struct ifreq) + \ + (p->ifr_addr.sa_len > sizeof(p->ifr_addr) ? \ + p->ifr_addr.sa_len - sizeof(p->ifr_addr) : 0)) +#else +# define ifr_size(p) (sizeof(ifr_type)) +#endif + +#ifdef USE_SIOCGLIFCONF +# define IFC_IOCTL_REQ SIOCGLIFCONF +# define IFC_REQ(ifc) ifc.lifc_req +# define IFC_LEN(ifc) ifc.lifc_len +# define IFR_ADDR(ifr) ifr->lifr_addr +# define IFR_NAME(ifr) ifr->lifr_name +#else +# define IFC_IOCTL_REQ SIOCGIFCONF +# define IFC_REQ(ifc) ifc.ifc_req +# define IFC_LEN(ifc) ifc.ifc_len +# define IFR_ADDR(ifr) ifr->ifr_addr +# define IFR_NAME(ifr) ifr->ifr_name +#endif + +/* Define this host for access control. Find all the hosts the OS knows about + * for this fd and add them to the selfhosts list. + */ +static void +DefineSelf( int fd, FILE *file, Xauth *auth, int *ok ) +{ + char buf[2048], *cp, *cplim; + int len; + char *addr; + int family; + ifr_type *ifr; +#ifdef USE_SIOCGLIFCONF + int n; + void * bufptr = buf; + size_t buflen = sizeof(buf); + struct lifconf ifc; +# ifdef SIOCGLIFNUM + struct lifnum ifn; +# endif +#else + struct ifconf ifc; +#endif + +#if defined(SIOCGLIFNUM) && defined(SIOCGLIFCONF) + ifn.lifn_family = AF_UNSPEC; + ifn.lifn_flags = 0; + if (ioctl( fd, (int)SIOCGLIFNUM, (char *)&ifn ) < 0) + LogError( "Failed getting interface count\n" ); + if (buflen < (ifn.lifn_count * sizeof(struct lifreq))) { + buflen = ifn.lifn_count * sizeof(struct lifreq); + bufptr = Malloc( buflen ); + } +#endif + +#ifdef USE_SIOCGLIFCONF + ifc.lifc_family = AF_UNSPEC; + ifc.lifc_flags = 0; + ifc.lifc_len = buflen; + ifc.lifc_buf = bufptr; +#else + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; +#endif + if (ifioctl (fd, IFC_IOCTL_REQ, (char *)&ifc) < 0) { + LogError( "Trouble getting network interface configuration\n" ); +#if defined(SIOCGLIFNUM) && defined(SIOCGLIFCONF) + if (bufptr != buf) + free( bufptr ); +#endif + return; + } + + cplim = (char *)IFC_REQ( ifc ) + IFC_LEN( ifc ); + + for (cp = (char *)IFC_REQ( ifc ); cp < cplim; cp += ifr_size (ifr)) { + ifr = (ifr_type *) cp; +#ifdef DNETCONN + /* + * this is ugly but SIOCGIFCONF returns decnet addresses in + * a different form from other decnet calls + */ + if (IFR_ADDR( ifr ).sa_family == AF_DECnet) { + len = sizeof(struct dn_naddr); + addr = (char *)IFR_ADDR( ifr ).sa_data; + family = FamilyDECnet; + } else +#endif + { + family = ConvertAddr( (char *)&IFR_ADDR( ifr ), &len, &addr ); + if (family < 0) + continue; + + if (len == 0) { + Debug( "skipping zero length address\n" ); + continue; + } + /* + * don't write out 'localhost' entries, as + * they may conflict with other local entries. + * DefineLocal will always be called to add + * the local entry anyway, so this one can + * be tossed. + */ + if (family == FamilyInternet && + addr[0] == 127 && addr[1] == 0 && + addr[2] == 0 && addr[3] == 1) + { + Debug( "skipping localhost address\n" ); + continue; + } +#if defined(IPv6) && defined(AF_INET6) + if (family == FamilyInternet6) { + if (IN6_IS_ADDR_LOOPBACK( ((struct in6_addr *)addr) )) { + Debug( "Skipping IPv6 localhost address\n" ); + continue; + } + /* Also skip XDM-AUTHORIZATION-1 */ + if (auth->name_length == 19 && + !memcmp( auth->name, "XDM-AUTHORIZATION-1", 19 )) { + Debug( "Skipping IPv6 XDM-AUTHORIZATION-1\n" ); + continue; + } + } +#endif + } + Debug( "DefineSelf: write network address, length %d\n", len ); + writeAddr( family, len, addr, file, auth, ok ); + } +#if defined(SIOCGLIFNUM) && defined(SIOCGLIFCONF) + if (bufptr != buf) + free( bufptr ); +#endif +} + +#else /* SIOCGIFCONF */ + +/* Define this host for access control. Find all the hosts the OS knows about + * for this fd and add them to the selfhosts list. + */ +static void +DefineSelf( int fd, int file, int auth, int *ok ) +{ + int len; + caddr_t addr; + int family; + + struct utsname name; + register struct hostent *hp; + + union { + struct sockaddr sa; + struct sockaddr_in in; + } saddr; + + struct sockaddr_in *inetaddr; + + /* hpux: + * Why not use gethostname()? Well, at least on my system, I've had to + * make an ugly kernel patch to get a name longer than 8 characters, and + * uname() lets me access to the whole string (it smashes release, you + * see), whereas gethostname() kindly truncates it for me. + */ + uname( &name ); + if ((hp = gethostbyname( name.nodename ))) { + saddr.sa.sa_family = hp->h_addrtype; + inetaddr = (struct sockaddr_in *)(&(saddr.sa)); + memmove( (char *)&(inetaddr->sin_addr), (char *)hp->h_addr, + (int)hp->h_length ); + if ( (family = ConvertAddr( &(saddr.sa), &len, &addr )) >= 0) + writeAddr( FamilyInternet, sizeof(inetaddr->sin_addr), + (char *)(&inetaddr->sin_addr), file, auth, ok ); + } +} + +#endif /* SIOCGIFCONF else */ +#endif /* WINTCP else */ +#endif /* STREAMSCONN && !SYSV_SIOCGIFCONF else */ +#endif /* HAVE_GETIFADDRS */ + +static void +setAuthNumber( Xauth *auth, const char *name ) +{ + char *colon; + char *dot; + + Debug( "setAuthNumber %s\n", name ); + colon = strrchr( name, ':' ); + if (colon) { + ++colon; + dot = strchr( colon, '.' ); + if (dot) + auth->number_length = dot - colon; + else + auth->number_length = strlen( colon ); + if (!StrNDup( &auth->number, colon, auth->number_length )) + auth->number_length = 0; + Debug( "setAuthNumber: %s\n", auth->number ); + } +} + +static void +writeLocalAuth( FILE *file, Xauth *auth, const char *name, int *ok ) +{ +#if defined(STREAMSCONN) || !defined(HAVE_GETIFADDRS) + int fd; +#endif + + Debug( "writeLocalAuth: %s %.*s\n", name, auth->name_length, auth->name ); + setAuthNumber( auth, name ); +# ifdef STREAMSCONN + fd = t_open( "/dev/tcp", O_RDWR, 0 ); + t_bind( fd, NULL, NULL ); + DefineSelf( fd, file, auth, ok ); + t_unbind( fd ); + t_close( fd ); +# elif defined(HAVE_GETIFADDRS) + DefineSelf( file, auth, ok ); +# else +# ifdef TCPCONN +# if defined(IPv6) && defined(AF_INET6) + fd = socket( AF_INET6, SOCK_STREAM, 0 ); + if (fd < 0) +# endif + fd = socket( AF_INET, SOCK_STREAM, 0 ); + DefineSelf( fd, file, auth, ok ); + close( fd ); +# endif +# ifdef DNETCONN + fd = socket( AF_DECnet, SOCK_STREAM, 0 ); + DefineSelf( fd, file, auth, ok ); + close( fd ); +# endif +# endif /* HAVE_GETIFADDRS */ + DefineLocal( file, auth, ok ); +} + +#ifdef XDMCP + +/* + * Call ConvertAddr(), and if it returns an IPv4 localhost, convert it + * to a local display name. Meets the _XTransConvertAddress's localhost + * hack. + */ + +static int +ConvertAuthAddr( char *saddr, int *len, char **addr ) +{ + int ret = ConvertAddr( saddr, len, addr ); + if (ret == FamilyInternet && + ((struct in_addr *)*addr)->s_addr == htonl( 0x7F000001L )) + ret = FamilyLocal; + return ret; +} + +static void +writeRemoteAuth( FILE *file, Xauth *auth, XdmcpNetaddr peer, int peerlen, + const char *name, int *ok ) +{ + int family = FamilyLocal; + char *addr; + + Debug( "writeRemoteAuth: %s %.*s\n", name, auth->name_length, auth->name ); + if (!peer || peerlen < 2) + return; + setAuthNumber( auth, name ); + family = ConvertAuthAddr( peer, &peerlen, &addr ); + Debug( "writeRemoteAuth: family %d\n", family ); + if (family != FamilyLocal) { + Debug( "writeRemoteAuth: %d, %02[*:hhx\n", + family, peerlen, addr ); + writeAddr( family, peerlen, addr, file, auth, ok ); + } else + writeLocalAuth( file, auth, name, ok ); +} + +#endif /* XDMCP */ + +#define NBSIZE 1024 + +static void +startUserAuth( char *buf, char *nbuf, FILE **old, FILE **new ) +{ + const char *home; + int lockStatus; + + initAddrs(); + *new = 0; + if ((home = getEnv( userEnviron, "HOME" )) && strlen( home ) < NBSIZE - 12) { + sprintf( buf, "%s/.Xauthority", home ); + Debug( "XauLockAuth %s\n", buf ); + lockStatus = XauLockAuth( buf, 1, 2, 10 ); + Debug( "lock is %d\n", lockStatus ); + if (lockStatus == LOCK_SUCCESS) + if (!openFiles( buf, nbuf, old, new )) + XauUnlockAuth( buf ); + } + if (!*new) + LogWarn( "Can't update authorization file in home dir %s\n", home ); +} + +static int +endUserAuth( FILE *old, FILE *new, const char *nname, int ok ) +{ + Xauth *entry; + struct stat statb; + + if (old) { + if (fstat( fileno( old ), &statb ) != -1) + chmod( nname, (int)(statb.st_mode & 0777) ); + /*SUPPRESS 560*/ + while ((entry = XauReadAuth( old ))) { + if (!checkEntry( entry )) { + Debug( "writing an entry\n" ); + writeAuth( new, entry, &ok ); + } + XauDisposeAuth( entry ); + } + fclose( old ); + } + if (fclose( new ) == EOF) + ok = FALSE; + doneAddrs(); + return ok; +} + +static void +undoUserAuth( const char *name, const char *new_name ) +{ + LogWarn( "Can't save user authorization in home dir\n" ); + unlink( new_name ); + XauUnlockAuth( name ); +} + +static char * +moveUserAuth( const char *name, char *new_name, char *envname ) +{ + if (unlink( name )) + Debug( "unlink %s failed\n", name ); + if (link( new_name, name )) { + Debug( "link failed %s %s\n", new_name, name ); + LogError( "Can't move user authorization into place\n" ); + envname = new_name; + } else { + Debug( "new authorization moved into place\n" ); + unlink( new_name ); + } + XauUnlockAuth( name ); + return envname; +} + +void +SetUserAuthorization( struct display *d ) +{ + FILE *old, *new; + char *name; + char *envname; + Xauth **auths; + int i, ok; + int magicCookie; + int data_len; + char name_buf[NBSIZE], new_name[NBSIZE + 2]; + + Debug( "SetUserAuthorization\n" ); + auths = d->authorizations; + if (auths) { + startUserAuth( name_buf, new_name, &old, &new ); + if (new) { + envname = 0; + name = name_buf; + } else { + fallback: + if (strlen( d->userAuthDir ) >= NBSIZE - 13) + return; + /* + * Note, that we don't lock the auth file here, as it's + * temporary - we can assume, that we are the only ones + * knowing about this file anyway. + */ +#ifdef HAVE_MKSTEMP + sprintf( name_buf, "%s/.XauthXXXXXX", d->userAuthDir ); + new = fdOpenW( mkstemp( name_buf ) ); +#else + for (i = 0; i < 100; i++) { + sprintf( name_buf, "%s/.XauthXXXXXX", d->userAuthDir ); + (void)mktemp( name_buf ); + if ((new = + fdOpenW( open( name_buf, O_WRONLY | O_CREAT | O_EXCL, + 0600 ) ))) + break; + } +#endif + if (!new) { + LogError( "Can't create authorization file in %s\n", + d->userAuthDir ); + return; + } + name = 0; + envname = name_buf; + old = 0; + } + ok = TRUE; + Debug( "%d authorization protocols for %s\n", d->authNum, d->name ); + /* + * Write MIT-MAGIC-COOKIE-1 authorization first, so that + * R4 clients which only knew that, and used the first + * matching entry will continue to function + */ + magicCookie = -1; + for (i = 0; i < d->authNum; i++) { + if (auths[i]->name_length == 18 && + !memcmp( auths[i]->name, "MIT-MAGIC-COOKIE-1", 18 )) + { + magicCookie = i; + if ((d->displayType & d_location) == dLocal) + writeLocalAuth( new, auths[i], d->name, &ok ); +#ifdef XDMCP + else + writeRemoteAuth( new, auths[i], (XdmcpNetaddr)d->peer.data, + d->peer.length, d->name, &ok ); +#endif + break; + } + } + /* now write other authorizations */ + for (i = 0; i < d->authNum; i++) { + if (i != magicCookie) { + data_len = auths[i]->data_length; + /* client will just use default Kerberos cache, so don't + * even write cache info into the authority file. + */ + if (auths[i]->name_length == 14 && + !strncmp( auths[i]->name, "MIT-KERBEROS-5", 14 )) + auths[i]->data_length = 0; + if ((d->displayType & d_location) == dLocal) + writeLocalAuth( new, auths[i], d->name, &ok ); +#ifdef XDMCP + else + writeRemoteAuth( new, auths[i], (XdmcpNetaddr)d->peer.data, + d->peer.length, d->name, &ok ); +#endif + auths[i]->data_length = data_len; + } + } + if (!endUserAuth( old, new, new_name, ok )) { + if (!name) { + LogError( "Can't save user authorization\n" ); + return; + } + undoUserAuth( name, new_name ); + initAddrs(); + goto fallback; + } + if (name) + envname = moveUserAuth( name, new_name, envname ); + if (envname) { + userEnviron = setEnv( userEnviron, "XAUTHORITY", envname ); + systemEnviron = setEnv( systemEnviron, "XAUTHORITY", envname ); + } + /* a chown() used to be here, but this code runs as user anyway */ + } + Debug( "done SetUserAuthorization\n" ); +} + +void +RemoveUserAuthorization( struct display *d ) +{ + Xauth **auths; + FILE *old, *new; + int i; + char name[NBSIZE], new_name[NBSIZE + 2]; + + if (!(auths = d->authorizations)) + return; + Debug( "RemoveUserAuthorization\n" ); + startUserAuth( name, new_name, &old, &new ); + if (new) { + for (i = 0; i < d->authNum; i++) { + if ((d->displayType & d_location) == dLocal) + writeLocalAuth( new, auths[i], d->name, 0 ); +#ifdef XDMCP + else + writeRemoteAuth( new, auths[i], (XdmcpNetaddr)d->peer.data, + d->peer.length, d->name, 0 ); +#endif + } + if (endUserAuth( old, new, new_name, TRUE )) + (void)moveUserAuth( name, new_name, 0 ); + else + undoUserAuth( name, new_name ); + } + Debug( "done RemoveUserAuthorization\n" ); +} diff --git a/kdm/backend/bootman.c b/kdm/backend/bootman.c new file mode 100644 index 000000000..c6ac968a2 --- /dev/null +++ b/kdm/backend/bootman.c @@ -0,0 +1,282 @@ +/* + +Copyright 2005 Stephan Kulow <[email protected]> +Copyright 2005 Oswald Buddenhagen <[email protected]> + +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 + * + * Boot options + */ + +#include "dm.h" +#include "dm_error.h" + +#include <string.h> +#include <unistd.h> +#include <stdio.h> +#include <ctype.h> + +extern char **environ; + +static int +getNull( char ***opts ATTR_UNUSED, int *def ATTR_UNUSED, int *cur ATTR_UNUSED ) +{ + return BO_NOMAN; +} + +static int +setNull( const char *opt ATTR_UNUSED, SdRec *sdr ATTR_UNUSED ) +{ + return BO_NOMAN; +} + +static char * +match( char *obuf, int *blen, const char *key, int klen ) +{ + char *buf = obuf; + if (memcmp( buf, key, klen ) || !isspace( buf[klen] )) + return 0; + buf += klen + 1; + for (; isspace( *buf ); buf++); + if (!*buf) + return 0; + *blen -= buf - obuf; + return buf; +} + +#define GRUB_MENU "/boot/grub/menu.lst" + +static char *grub; + +static int +getGrub( char ***opts, int *def, int *cur ) +{ + FILE *f; + char *ptr, *linp; + int len; + char line[1000]; + + if (!grub && !(grub = locate( "grub" ))) + return BO_NOMAN; + + *def = 0; + *cur = -1; + *opts = initStrArr( 0 ); + + if (!(f = fopen( GRUB_MENU, "r" ))) + return errno == ENOENT ? BO_NOMAN : BO_IO; + while ((len = fGets( line, sizeof(line), f )) != -1) { + for (linp = line; isspace(*linp); linp++, len--); + if ((ptr = match( linp, &len, "default", 7 ))) + *def = atoi( ptr ); + else if ((ptr = match( linp, &len, "title", 5 ))) { + for (; isspace( ptr[len - 1] ); len--); + *opts = addStrArr( *opts, ptr, len ); + } + } + fclose( f ); + + return BO_OK; +} + +static int +setGrub( const char *opt, SdRec *sdr ) +{ + FILE *f; + char *ptr; + int len, i; + char line[1000]; + + if (!(f = fopen( GRUB_MENU, "r" ))) + return errno == ENOENT ? BO_NOMAN : BO_IO; + for (i = 0; (len = fGets( line, sizeof(line), f )) != -1; ) + if ((ptr = match( line, &len, "title", 5 ))) { + if (!strcmp( ptr, opt )) { + fclose( f ); + sdr->osindex = i; + sdr->bmstamp = mTime( GRUB_MENU ); + return BO_OK; + } + i++; + } + fclose( f ); + return BO_NOENT; +} + +static void +commitGrub( void ) +{ + FILE *f; + int pid; + static const char *args[] = { 0, "--batch", "--no-floppy", 0 }; + + if (sdRec.bmstamp != mTime( GRUB_MENU ) && + setGrub( sdRec.osname, &sdRec ) != BO_OK) + return; + + args[0] = grub; + if ((f = pOpen( (char **)args, 'w', &pid ))) { + fprintf( f, "savedefault --default=%d --once\n", sdRec.osindex ); + pClose( f, pid ); + } +} + +static char *lilo; + +static int +getLilo( char ***opts, int *def, int *cur ) +{ + FILE *f; + int cdef, pid, len, ret = BO_OK; + static const char *args[5] = { 0, "-w", "-v", "-q", 0 }; + char buf[256], next[256]; + + if (!lilo && !(lilo = locate( "lilo" ))) + return BO_NOMAN; + + args[0] = lilo; + if (!(f = pOpen( (char **)args, 'r', &pid ))) + return BO_IO; + *opts = 0; + next[0] = 0; + for (;;) { + if ((len = fGets( buf, sizeof(buf), f)) == -1) { + ret = BO_NOMAN; + goto out; + } + if (!memcmp( buf, "Images:", 7 )) + break; +#define Ldeflin " Default boot command line:" + if (!memcmp( buf, Ldeflin, strlen(Ldeflin) )) { + memcpy( next, buf + strlen(Ldeflin) + 2, len - strlen(Ldeflin) - 3 ); + next[len - strlen(Ldeflin) - 3] = 0; + } + } + cdef = *def = 0; + *cur = -1; + *opts = initStrArr( 0 ); + while ((len = fGets( buf, sizeof(buf), f)) != -1) + if (buf[0] == ' ' && buf[1] == ' ' && buf[2] != ' ') { + if (buf[len - 1] == '*') { + *def = cdef; + len--; + } + for (; buf[len - 1] == ' '; len--); + *opts = addStrArr( *opts, buf + 2, len - 2 ); + if (!strcmp( (*opts)[cdef], next )) + *cur = cdef; + cdef++; + } + out: + if (pClose( f, pid )) { + if (*opts) + freeStrArr( *opts ); + return BO_IO; + } + return ret; +} + +static int +setLilo( const char *opt, SdRec *sdr ATTR_UNUSED ) +{ + char **opts; + int def, cur, ret, i; + + if ((ret = getLilo( &opts, &def, &cur )) != BO_OK) + return ret; + if (!*opt) + opt = 0; + else { + for (i = 0; opts[i]; i++) + if (!strcmp( opts[i], opt )) + goto oke; + freeStrArr( opts ); + return BO_NOENT; + } + oke: + freeStrArr( opts ); + return BO_OK; +} + +static void +commitLilo( void ) +{ + static const char *args[5] = { 0, "-w", "-R", 0, 0 }; + + args[0] = lilo; + args[3] = sdRec.osname; + runAndWait( (char **)args, environ ); +} + +static struct { + int (*get)( char ***, int *, int * ); + int (*set)( const char *, SdRec * ); + void (*commit)( void ); +} bootOpts[] = { + { getNull, setNull, 0 }, + { getGrub, setGrub, commitGrub }, + { getLilo, setLilo, commitLilo }, +}; + +int +getBootOptions( char ***opts, int *def, int *cur ) +{ + return bootOpts[bootManager].get( opts, def, cur ); +} + +int +setBootOption( const char *opt, SdRec *sdr ) +{ + int ret; + + if (sdr->osname) { + free( sdr->osname ); + sdr->osname = 0; + } + if (opt) { + if ((ret = bootOpts[bootManager].set( opt, sdr )) != BO_OK) + return ret; + if (!StrDup( &sdr->osname, opt )) + return BO_IO; /* BO_NOMEM */ + } + return BO_OK; +} + +void +commitBootOption( void ) +{ + if (sdRec.osname) { + bootOpts[bootManager].commit(); +/* + free( sdRec.osname ); + sdRec.osname = 0; +*/ + } +} + diff --git a/kdm/backend/choose.c b/kdm/backend/choose.c new file mode 100644 index 000000000..ead841228 --- /dev/null +++ b/kdm/backend/choose.c @@ -0,0 +1,1051 @@ +/* + +Copyright 1990, 1998 The Open Group +Copyright 2002-2004 Oswald Buddenhagen <[email protected]> + +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 + * + * chooser backend + */ + +#include <config.h> + +#ifdef XDMCP + +#include "dm.h" +#include "dm_error.h" +#include "dm_socket.h" + +#include <ctype.h> + +#ifdef __svr4__ +# include <sys/sockio.h> +#endif +#include <arpa/inet.h> + +#include <sys/ioctl.h> +#ifdef STREAMSCONN +# ifdef WINTCP /* NCR with Wollongong TCP */ +# include <netinet/ip.h> +# endif +# include <stropts.h> +# include <tiuser.h> +# include <netconfig.h> +# include <netdir.h> +#endif + +#if !defined(__GNU__) && !defined(__hpux) /* XXX __hpux might be wrong */ +# include <net/if.h> +#endif + +#include <netdb.h> + +typedef struct _IndirectUsers { + struct _IndirectUsers *next; + ARRAY8 client; + CARD16 connectionType; +} IndirectUsersRec, *IndirectUsersPtr; + +static IndirectUsersPtr indirectUsers; + +int +RememberIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType ) +{ + IndirectUsersPtr i; + + for (i = indirectUsers; i; i = i->next) + if (XdmcpARRAY8Equal( clientAddress, &i->client ) && + connectionType == i->connectionType) + return 1; + if (!(i = (IndirectUsersPtr)Malloc( sizeof(IndirectUsersRec) ))) + return 0; + if (!XdmcpCopyARRAY8( clientAddress, &i->client )) { + free( (char *)i ); + return 0; + } + i->connectionType = connectionType; + i->next = indirectUsers; + indirectUsers = i; + return 1; +} + +void +ForgetIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType ) +{ + IndirectUsersPtr *i, ni; + + for (i = &indirectUsers; *i; i = &(*i)->next) + if (XdmcpARRAY8Equal( clientAddress, &(*i)->client ) && + connectionType == (*i)->connectionType) + { + ni = (*i)->next; + XdmcpDisposeARRAY8( &(*i)->client ); + free( (char *)(*i) ); + (*i) = ni; + break; + } +} + +int +IsIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType ) +{ + IndirectUsersPtr i; + + for (i = indirectUsers; i; i = i->next) + if (XdmcpARRAY8Equal( clientAddress, &i->client ) && + connectionType == i->connectionType) + return 1; + return 0; +} + +typedef struct _Choices { + struct _Choices *next; + ARRAY8 client; + CARD16 connectionType; + ARRAY8 choice; + Time_t time; +} ChoiceRec, *ChoicePtr; + +static ChoicePtr choices; + +ARRAY8Ptr +IndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType ) +{ + ChoicePtr c, next, prev; + + prev = 0; + for (c = choices; c; c = next) { + next = c->next; + Debug( "choice checking timeout: %ld >? %d\n", + (long)(now - c->time), choiceTimeout ); + if (now - c->time > (Time_t)choiceTimeout) { + Debug( "timeout choice %ld > %d\n", + (long)(now - c->time), choiceTimeout ); + if (prev) + prev->next = next; + else + choices = next; + XdmcpDisposeARRAY8( &c->client ); + XdmcpDisposeARRAY8( &c->choice ); + free( (char *)c ); + } else { + if (XdmcpARRAY8Equal( clientAddress, &c->client ) && + connectionType == c->connectionType) + return &c->choice; + prev = c; + } + } + return 0; +} + +int +RegisterIndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType, + ARRAY8Ptr choice ) +{ + ChoicePtr c; + int insert; +#if 0 + int found = 0; +#endif + + Debug( "got indirect choice back\n" ); + for (c = choices; c; c = c->next) { + if (XdmcpARRAY8Equal( clientAddress, &c->client ) && + connectionType == c->connectionType) { +#if 0 + found = 1; +#endif + break; + } + } +#if 0 + if (!found) + return 0; +#endif + + insert = 0; + if (!c) { + insert = 1; + c = (ChoicePtr)Malloc( sizeof(ChoiceRec) ); + if (!c) + return 0; + c->connectionType = connectionType; + if (!XdmcpCopyARRAY8( clientAddress, &c->client )) { + free( (char *)c ); + return 0; + } + } else + XdmcpDisposeARRAY8( &c->choice ); + if (!XdmcpCopyARRAY8( choice, &c->choice )) { + XdmcpDisposeARRAY8( &c->client ); + free( (char *)c ); + return 0; + } + if (insert) { + c->next = choices; + choices = c; + } + c->time = now; + return 1; +} + +#if 0 +static void +RemoveIndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType ) +{ + ChoicePtr c, prev; + + prev = 0; + for (c = choices; c; c = c->next) { + if (XdmcpARRAY8Equal( clientAddress, &c->client ) && + connectionType == c->connectionType) + { + if (prev) + prev->next = c->next; + else + choices = c->next; + XdmcpDisposeARRAY8( &c->client ); + XdmcpDisposeARRAY8( &c->choice ); + free( (char *)c ); + return; + } + prev = c; + } +} +#endif + + +/* ####################### */ + + +typedef struct _hostAddr { + struct _hostAddr *next; + struct sockaddr *addr; + int addrlen; + xdmOpCode type; +} HostAddr; + +static HostAddr *hostAddrdb; + +typedef struct _hostName { + struct _hostName *next; + unsigned willing:1, alive:1; + ARRAY8 hostname, status; + CARD16 connectionType; + ARRAY8 hostaddr; +} HostName; + +static HostName *hostNamedb; + +static XdmcpBuffer directBuffer, broadcastBuffer; +static XdmcpBuffer buffer; + +static int socketFD; +#if defined(IPv6) && defined(AF_INET6) +static int socket6FD; +#endif + + +static void +doPingHosts() +{ + HostAddr *hosts; + + for (hosts = hostAddrdb; hosts; hosts = hosts->next) +#if defined(IPv6) && defined(AF_INET6) + XdmcpFlush( hosts->addr->sa_family == AF_INET6 ? socket6FD : socketFD, +#else + XdmcpFlush( socketFD, +#endif + hosts->type == QUERY ? &directBuffer : &broadcastBuffer, + (XdmcpNetaddr)hosts->addr, hosts->addrlen ); +} + +static int +addHostname( ARRAY8Ptr hostname, ARRAY8Ptr status, + struct sockaddr *addr, int will ) +{ + HostName **names, *name; + ARRAY8 hostAddr; + CARD16 connectionType; + + switch (addr->sa_family) { + case AF_INET: + hostAddr.data = + (CARD8 *)& ((struct sockaddr_in *)addr)->sin_addr; + hostAddr.length = 4; + connectionType = FamilyInternet; + break; +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: + hostAddr.data = + (CARD8 *)&((struct sockaddr_in6 *)addr)->sin6_addr; + hostAddr.length = 16; + connectionType = FamilyInternet6; + break; +#endif + default: + hostAddr.data = (CARD8 *)""; + hostAddr.length = 0; + connectionType = FamilyLocal; + break; + } + for (names = &hostNamedb; *names; names = &(*names)->next) { + name = *names; + if (connectionType == name->connectionType && + XdmcpARRAY8Equal( &hostAddr, &name->hostaddr )) + { + if (XdmcpARRAY8Equal( status, &name->status )) + return 0; + XdmcpDisposeARRAY8( &name->status ); + XdmcpDisposeARRAY8( hostname ); + + GSendInt( G_Ch_ChangeHost ); + goto gotold; + } + } + if (!(name = (HostName *)Malloc( sizeof(*name) ))) + return 0; + if (hostname->length) { + switch (addr->sa_family) { + case AF_INET: +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: +#endif + { + struct hostent *hostent; + char *host; + + hostent = gethostbyaddr( (char *)hostAddr.data, + hostAddr.length, addr->sa_family ); + if (hostent) { + XdmcpDisposeARRAY8( hostname ); + host = hostent->h_name; + XdmcpAllocARRAY8( hostname, strlen( host ) ); + memmove( hostname->data, host, hostname->length ); + } + } + } + } + if (!XdmcpAllocARRAY8( &name->hostaddr, hostAddr.length )) { + free( (char *)name ); + return 0; + } + memmove( name->hostaddr.data, hostAddr.data, hostAddr.length ); + name->connectionType = connectionType; + name->hostname = *hostname; + + *names = name; + name->next = 0; + + GSendInt( G_Ch_AddHost ); + gotold: + name->alive = 1; + name->willing = will; + name->status = *status; + + GSendInt( (int)name ); /* just an id */ + GSendNStr( (char *)name->hostname.data, name->hostname.length ); + GSendNStr( (char *)name->status.data, name->status.length ); + GSendInt( will ); + + return 1; +} + +static void +disposeHostname( HostName *host ) +{ + XdmcpDisposeARRAY8( &host->hostname ); + XdmcpDisposeARRAY8( &host->hostaddr ); + XdmcpDisposeARRAY8( &host->status ); + free( (char *)host ); +} + +static void +emptyHostnames( void ) +{ + HostName *host, *nhost; + + for (host = hostNamedb; host; host = nhost) { + nhost = host->next; + disposeHostname( host ); + } + hostNamedb = 0; +} + +static void +receivePacket( int sfd ) +{ + XdmcpHeader header; + ARRAY8 authenticationName; + ARRAY8 hostname; + ARRAY8 status; + int saveHostname = 0; +#if defined(IPv6) && defined(AF_INET6) + struct sockaddr_storage addr; +#else + struct sockaddr addr; +#endif + int addrlen; + + addrlen = sizeof(addr); + if (!XdmcpFill( sfd, &buffer, (XdmcpNetaddr)&addr, &addrlen )) + return; + if (!XdmcpReadHeader( &buffer, &header )) + return; + if (header.version != XDM_PROTOCOL_VERSION) + return; + hostname.data = 0; + status.data = 0; + authenticationName.data = 0; + switch (header.opcode) { + case WILLING: + if (XdmcpReadARRAY8( &buffer, &authenticationName ) && + XdmcpReadARRAY8( &buffer, &hostname ) && + XdmcpReadARRAY8( &buffer, &status )) { + if (header.length == 6 + authenticationName.length + + hostname.length + status.length) { + if (addHostname( &hostname, &status, + (struct sockaddr *)&addr, 1 )) + saveHostname = 1; + } + } + XdmcpDisposeARRAY8( &authenticationName ); + break; + case UNWILLING: + if (XdmcpReadARRAY8( &buffer, &hostname ) && + XdmcpReadARRAY8( &buffer, &status )) { + if (header.length == 4 + hostname.length + status.length) { + if (addHostname( &hostname, &status, + (struct sockaddr *)&addr, 0 )) + saveHostname = 1; + } + } + break; + default: + break; + } + if (!saveHostname) { + XdmcpDisposeARRAY8( &hostname ); + XdmcpDisposeARRAY8( &status ); + } +} + +static void +addHostaddr( HostAddr **hosts, struct sockaddr *addr, int len, xdmOpCode type ) +{ + HostAddr *host; + + Debug( "adding host %[*hhu, type %d\n", len, addr, type ); + for (host = *hosts; host; host = host->next) + if (host->type == type && host->addr->sa_family == addr->sa_family) + switch (addr->sa_family) { + case AF_INET: + { + struct sockaddr_in *na = (struct sockaddr_in *)addr; + struct sockaddr_in *oa = (struct sockaddr_in *)host->addr; + if (na->sin_port == oa->sin_port && + na->sin_addr.s_addr == oa->sin_addr.s_addr) + return; + break; + } +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: + { + struct sockaddr_in6 *na = (struct sockaddr_in6 *)addr; + struct sockaddr_in6 *oa = (struct sockaddr_in6 *)host->addr; + if (na->sin6_port == oa->sin6_port && + !memcmp( &na->sin6_addr, &oa->sin6_addr, 16 )) + return; + break; + } +#endif + default: /* ... */ + break; + } + Debug( " not dupe\n" ); + if (!(host = (HostAddr *)Malloc( sizeof(*host) ))) + return; + if (!(host->addr = (struct sockaddr *)Malloc( len ))) { + free( (char *)host ); + return; + } + memcpy( (char *)host->addr, (char *)addr, len ); + host->addrlen = len; + host->type = type; + host->next = *hosts; + *hosts = host; +} + +static void +registerHostaddr( struct sockaddr *addr, int len, xdmOpCode type ) +{ + addHostaddr( &hostAddrdb, addr, len, type ); +} + +static void +emptyPingHosts( void ) +{ + HostAddr *host, *nhost; + + for (host = hostAddrdb; host; host = nhost) { + nhost = host->next; + free( host->addr ); + free( host ); + } + hostAddrdb = 0; +} + +/* Handle variable length ifreq in BNR2 and later */ +#ifdef VARIABLE_IFREQ +# define ifr_size(p) \ + (sizeof(struct ifreq) + \ + (p->ifr_addr.sa_len > sizeof (p->ifr_addr) ? \ + p->ifr_addr.sa_len - sizeof (p->ifr_addr) : 0)) +#else +# define ifr_size(p) (sizeof(struct ifreq)) +#endif + +#define IFC_REQ(ifc) ifc.ifc_req + +#ifndef SYSV_SIOCGIFCONF +# define ifioctl ioctl +#endif + +static void +registerBroadcastForPing( void ) +{ + struct sockaddr_in in_addr; + +#ifdef __GNU__ + in_addr.sin_addr.s_addr = htonl( 0xFFFFFFFF ); + in_addr.sin_port = htons( XDM_UDP_PORT ); + registerHostaddr( (struct sockaddr *)&in_addr, sizeof(in_addr), + BROADCAST_QUERY ); +#else /* __GNU__ */ + struct ifconf ifc; + register struct ifreq *ifr; + struct sockaddr broad_addr; + char buf[2048], *cp, *cplim; +# ifdef WINTCP /* NCR with Wollongong TCP */ + int ipfd; + struct ifconf *ifcp; + struct strioctl ioc; + int n; + + ifcp = (struct ifconf *)buf; + ifcp->ifc_buf = buf + 4; + ifcp->ifc_len = sizeof(buf) - 4; + + if ((ipfd = open( "/dev/ip", O_RDONLY )) < 0) { + t_error( "RegisterBroadcastForPing() t_open(/dev/ip) failed" ); + return; + } + + ioc.ic_cmd = IPIOC_GETIFCONF; + ioc.ic_timout = 60; + ioc.ic_len = sizeof(buf); + ioc.ic_dp = (char *)ifcp; + + if (ioctl( ipfd, (int)I_STR, (char *)&ioc ) < 0) { + perror( "RegisterBroadcastForPing() ioctl(I_STR(IPIOC_GETIFCONF)) failed" ); + close( ipfd ); + return; + } + + for (ifr = ifcp->ifc_req, n = ifcp->ifc_len / sizeof(struct ifreq); + --n >= 0; ifr++) +# else /* WINTCP */ + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if (ifioctl(socketFD, (int)SIOCGIFCONF, (char *)&ifc) < 0) + return; + + cplim = (char *)IFC_REQ( ifc ) + ifc.ifc_len; + + for (cp = (char *)IFC_REQ( ifc ); cp < cplim; cp += ifr_size(ifr)) +# endif /* WINTCP */ + { +# ifndef WINTCP + ifr = (struct ifreq *)cp; +# endif + if (ifr->ifr_addr.sa_family != AF_INET) + continue; + + broad_addr = ifr->ifr_addr; + ((struct sockaddr_in *)&broad_addr)->sin_addr.s_addr = + htonl( INADDR_BROADCAST ); +# ifdef SIOCGIFBRDADDR + { + struct ifreq broad_req; + + broad_req = *ifr; +# ifdef WINTCP /* NCR with Wollongong TCP */ + ioc.ic_cmd = IPIOC_GETIFFLAGS; + ioc.ic_timout = 0; + ioc.ic_len = sizeof(broad_req); + ioc.ic_dp = (char *)&broad_req; + + if (ioctl (ipfd, I_STR, (char *) &ioc ) != -1 && +# else /* WINTCP */ + if (ifioctl( socketFD, SIOCGIFFLAGS, (char *)&broad_req ) != + -1 && +# endif /* WINTCP */ + (broad_req.ifr_flags & IFF_BROADCAST) && + (broad_req.ifr_flags & IFF_UP)) + { + broad_req = *ifr; +# ifdef WINTCP /* NCR with Wollongong TCP */ + ioc.ic_cmd = IPIOC_GETIFBRDADDR; + ioc.ic_timout = 0; + ioc.ic_len = sizeof(broad_req); + ioc.ic_dp = (char *)&broad_req; + + if (ioctl( ipfd, I_STR, (char *) &ioc) != -1 ) +# else /* WINTCP */ + if (ifioctl( socketFD, SIOCGIFBRDADDR, (char *)&broad_req ) + != -1) +# endif /* WINTCP */ + broad_addr = broad_req.ifr_addr; + else + continue; + } else + continue; + } +# endif + in_addr = *((struct sockaddr_in *)&broad_addr); + in_addr.sin_port = htons( XDM_UDP_PORT ); +# ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + in_addr.sin_len = sizeof(in_addr); +# endif + registerHostaddr( (struct sockaddr *)&in_addr, sizeof(in_addr), + BROADCAST_QUERY ); + } +#endif +} + +static int +makeSockAddrs( const char *name, HostAddr **hosts ) +{ +#if defined(IPv6) && defined(AF_INET6) + struct addrinfo *ai, *nai, hints; + bzero( &hints, sizeof(hints) ); + hints.ai_socktype = SOCK_DGRAM; + if (getaddrinfo( name, stringify( XDM_UDP_PORT ), &hints, &ai )) + return 0; + for (nai = ai; nai; nai = nai->ai_next) + if ((nai->ai_family == AF_INET) || (nai->ai_family == AF_INET6)) + addHostaddr( hosts, nai->ai_addr, nai->ai_addrlen, + (nai->ai_family == AF_INET ? + IN_MULTICAST( ((struct sockaddr_in *)nai->ai_addr)->sin_addr.s_addr ) : + IN6_IS_ADDR_MULTICAST( &((struct sockaddr_in6 *)nai->ai_addr)->sin6_addr )) ? + BROADCAST_QUERY : QUERY ); +#else + struct sockaddr_in in_addr; + /* Per RFC 1123, check first for IP address in dotted-decimal form */ + if ((in_addr.sin_addr.s_addr = inet_addr( name )) == (unsigned)-1) { + struct hostent *hostent; + if (!(hostent = gethostbyname( name )) || + hostent->h_addrtype != AF_INET) + return 0; + memcpy( &in_addr.sin_addr, hostent->h_addr, 4 ); + } + in_addr.sin_family = AF_INET; + in_addr.sin_port = htons( XDM_UDP_PORT ); +# ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + in_addr.sin_len = sizeof(in_addr); +# endif + addHostaddr( hosts, (struct sockaddr *)&in_addr, sizeof(in_addr), +# ifdef IN_MULTICAST + IN_MULTICAST( in_addr.sin_addr.s_addr ) ? + BROADCAST_QUERY : +# endif + QUERY ); +#endif + return 1; +} + +/* + * Register the address for this host. + * Called for interactively specified hosts. + * The special names "BROADCAST" and "*" look up all the broadcast + * addresses on the local host. + */ + +static int +registerForPing( const char *name ) +{ + Debug( "manual host registration: %s\n", name ); + if (!strcmp( name, "BROADCAST" ) || !strcmp( name, "*" )) + registerBroadcastForPing(); + else if (!makeSockAddrs( name, &hostAddrdb )) + return 0; + return 1; +} + +/*ARGSUSED*/ +static void +AddChooserHost( + CARD16 connectionType, + ARRAY8Ptr addr, + char *closure ATTR_UNUSED ) +{ + if (connectionType == FamilyBroadcast) { + registerBroadcastForPing(); + return; + } + Debug( "internal host registration: %[*hhu, family %hx\n", + addr->length, addr->data, connectionType ); + if (connectionType == FamilyInternet) { + struct sockaddr_in in_addr; + in_addr.sin_family = AF_INET; + memmove( &in_addr.sin_addr, addr->data, 4 ); + in_addr.sin_port = htons( XDM_UDP_PORT ); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + in_addr.sin_len = sizeof(in_addr); +#endif + registerHostaddr( (struct sockaddr *)&in_addr, sizeof(in_addr), +#ifdef IN_MULTICAST + IN_MULTICAST( in_addr.sin_addr.s_addr ) ? + BROADCAST_QUERY : +#endif + QUERY ); + } +#if defined(IPv6) && defined(AF_INET6) + else if (connectionType == FamilyInternet6) { + struct sockaddr_in6 in6_addr; + in6_addr.sin6_family = AF_INET6; + memmove( &in6_addr.sin6_addr, addr->data, 16 ); + in6_addr.sin6_port = htons( XDM_UDP_PORT ); +#ifdef SIN6_LEN + in6_addr.sin6_len = sizeof(in6_addr); +#endif + registerHostaddr( (struct sockaddr *)&in6_addr, sizeof(in6_addr), + IN6_IS_ADDR_MULTICAST( &in6_addr.sin6_addr ) ? + BROADCAST_QUERY : QUERY ); + } +#endif +} + +static ARRAYofARRAY8 AuthenticationNames; + +#if 0 +static void RegisterAuthenticationName( char *name, int namelen ) +{ + ARRAY8Ptr authName; + if (!XdmcpReallocARRAYofARRAY8( &AuthenticationNames, + AuthenticationNames.length + 1 )) + return; + authName = &AuthenticationNames.data[AuthenticationNames.length - 1]; + if (!XdmcpAllocARRAY8( authName, namelen )) + return; + memmove( authName->data, name, namelen ); +} +#endif + +static int +initXDMCP() +{ + XdmcpHeader header; +#if 0 + int i; +#endif +#ifndef STREAMSCONN +#ifdef SO_BROADCAST + int soopts; +#endif +#endif + + header.version = XDM_PROTOCOL_VERSION; + header.length = 1; +#if 0 + for (i = 0; i < (int)AuthenticationNames.length; i++) + header.length += 2 + AuthenticationNames.data[i].length; +#endif + + header.opcode = (CARD16)BROADCAST_QUERY; + XdmcpWriteHeader( &broadcastBuffer, &header ); + XdmcpWriteARRAYofARRAY8( &broadcastBuffer, &AuthenticationNames ); + + header.opcode = (CARD16)QUERY; + XdmcpWriteHeader( &directBuffer, &header ); + XdmcpWriteARRAYofARRAY8( &directBuffer, &AuthenticationNames ); + +#if defined(STREAMSCONN) + if ((socketFD = t_open( "/dev/udp", O_RDWR, 0 )) < 0) + return 0; + + if (t_bind( socketFD, NULL, NULL ) < 0) { + t_close( socketFD ); + return 0; + } + + /* + * This part of the code looks contrived. It will actually fit in nicely + * when the CLTS part of Xtrans is implemented. + */ + { + struct netconfig *nconf; + + if ((nconf = getnetconfigent( "udp" )) == NULL) { + t_unbind( socketFD ); + t_close( socketFD ); + return 0; + } + + if (netdir_options( nconf, ND_SET_BROADCAST, socketFD, NULL )) { + freenetconfigent( nconf ); + t_unbind( socketFD ); + t_close( socketFD ); + return 0; + } + + freenetconfigent( nconf ); + } +#else + if ((socketFD = socket( AF_INET, SOCK_DGRAM, 0 )) < 0) + return 0; +#if defined(IPv6) && defined(AF_INET6) + socket6FD = socket( AF_INET6, SOCK_DGRAM, 0 ); +#endif +# ifdef SO_BROADCAST + soopts = 1; + if (setsockopt( socketFD, SOL_SOCKET, SO_BROADCAST, (char *)&soopts, + sizeof(soopts) ) < 0) + perror( "setsockopt" ); +# endif +#endif + + return 1; +} + +static void ATTR_NORETURN +chooseHost( int hid ) +{ + HostName *h; +#if defined(IPv6) && defined(AF_INET6) + char addr[64]; +#endif + + for (h = hostNamedb; h; h = h->next) + if ((int)h == hid) { + /* XXX error handling */ + GSet( &mstrtalk ); + if ((td->displayType & d_location) == dLocal) { + GSendInt( D_RemoteHost ); +#if defined(IPv6) && defined(AF_INET6) + switch (h->connectionType) { + case FamilyInternet6: + inet_ntop( AF_INET6, h->hostaddr.data, addr, sizeof(addr) ); + break; + default: /* FamilyInternet */ + inet_ntop( AF_INET, h->hostaddr.data, addr, sizeof(addr) ); + break; + } + GSendStr( addr ); +#else + GSendStr( inet_ntoa( *(struct in_addr *)h->hostaddr.data ) ); +#endif + CloseGreeter( FALSE ); + SessionExit( EX_REMOTE ); + } else { + GSendInt( D_ChooseHost ); + GSendArr( td->clientAddr.length, (char *)td->clientAddr.data ); + GSendInt( td->connectionType ); + GSendArr( h->hostaddr.length, (char *)h->hostaddr.data ); + CloseGreeter( FALSE ); + goto bout; + } + break; + } +/* LogError( "Internal error: chose unexisting host\n" ); */ + bout: + SessionExit( EX_NORMAL ); +} + +static void +directChooseHost( const char *name ) +{ + HostAddr *hosts = 0; + + if (!makeSockAddrs( name, &hosts )) + return; + GSendInt( G_Ch_Exit ); + /* XXX error handling */ + GSet( &mstrtalk ); + if ((td->displayType & d_location) == dLocal) { + GSendInt( D_RemoteHost ); + GSendStr( name ); + CloseGreeter( FALSE ); + SessionExit( EX_REMOTE ); + } else { + GSendInt( D_ChooseHost ); + GSendArr( td->clientAddr.length, (char *)td->clientAddr.data ); + GSendInt( td->connectionType ); + GSendArr( hosts->addrlen, (char *)hosts->addr ); + CloseGreeter( FALSE ); + SessionExit( EX_NORMAL ); + } +} + +#define PING_TRIES 3 + +int +DoChoose() +{ + HostName **hp, *h; + char *host, **hostp; + struct timeval *to, tnow, nextPing; + int pingTry, n, cmd; + FD_TYPE rfds; + static int xdmcpInited; + + OpenGreeter(); + GSendInt( G_Choose ); + switch (cmd = CtrlGreeterWait( TRUE )) { + case G_Ready: + break; + default: /* error */ + return cmd; + } + + if (!xdmcpInited) { + if (!initXDMCP()) + SessionExit( EX_UNMANAGE_DPY ); + xdmcpInited = 1; + } + if ((td->displayType & d_location) == dLocal) { + /* XXX the config reader should do the lookup already */ + for (hostp = td->chooserHosts; *hostp; hostp++) + if (!registerForPing( *hostp )) + LogError( "Unkown host %\"s specified for local chooser preload of display %s\n", *hostp, td->name ); + } else + ForEachChooserHost( &td->clientAddr, td->connectionType, + AddChooserHost, 0 ); + + GSendInt( 0 ); /* entering async mode signal */ + + reping: + for (h = hostNamedb; h; h = h->next) + h->alive = 0; + pingTry = 0; + goto pingen; + + for (;;) { + to = 0; + if (pingTry <= PING_TRIES) { + gettimeofday( &tnow, 0 ); + if (nextPing.tv_sec < tnow.tv_sec || + (nextPing.tv_sec == tnow.tv_sec && + nextPing.tv_usec < tnow.tv_usec)) { + if (pingTry < PING_TRIES) { + pingen: + pingTry++; + doPingHosts(); + gettimeofday( &tnow, 0 ); + nextPing = tnow; + nextPing.tv_sec++; + } else { + for (hp = &hostNamedb; *hp; ) + if (!(*hp)->alive) { + h = (*hp)->next; + disposeHostname( *hp ); + GSendInt( G_Ch_RemoveHost ); + GSendInt( (int)*hp ); /* just an id */ + *hp = h; + } else + hp = &(*hp)->next; + goto noto; + } + } + to = &tnow; + tnow.tv_sec = nextPing.tv_sec - tnow.tv_sec; + tnow.tv_usec = nextPing.tv_usec - tnow.tv_usec; + if (tnow.tv_usec < 0) { + tnow.tv_usec += 1000000; + tnow.tv_sec--; + } + } + noto: + FD_ZERO( &rfds ); + FD_SET( grtproc.pipe.rfd, &rfds ); + FD_SET( socketFD, &rfds ); +#if defined(IPv6) && defined(AF_INET6) + if (socket6FD >= 0) + FD_SET( socket6FD, &rfds ); +#endif + n = grtproc.pipe.rfd; + if (socketFD > n) + n = socketFD; +#if defined(IPv6) && defined(AF_INET6) + if (socket6FD > n) + n = socket6FD; +#endif + if (select( n + 1, &rfds, 0, 0, to ) > 0) { + if (FD_ISSET( grtproc.pipe.rfd, &rfds )) + switch (cmd = CtrlGreeterWait( FALSE )) { + case -1: + break; + case G_Ch_Refresh: + goto reping; + case G_Ch_RegisterHost: + host = GRecvStr(); + if (!registerForPing( host )) { + GSendInt( G_Ch_BadHost ); + GSendStr( host ); + } + free( host ); + goto reping; + case G_Ch_DirectChoice: + host = GRecvStr(); + directChooseHost( host ); + GSendInt( G_Ch_BadHost ); + GSendStr( host ); + free( host ); + break; + case G_Ready: + chooseHost( GRecvInt() ); + /* NOTREACHED */ + default: + emptyHostnames(); + emptyPingHosts(); + return cmd; + } + if (FD_ISSET( socketFD, &rfds )) + receivePacket( socketFD ); +#if defined(IPv6) && defined(AF_INET6) + if (socket6FD >= 0 && FD_ISSET( socket6FD, &rfds )) + receivePacket( socket6FD ); +#endif + } + } + +} + +#endif /* XDMCP */ diff --git a/kdm/backend/client.c b/kdm/backend/client.c new file mode 100644 index 000000000..c807b6e15 --- /dev/null +++ b/kdm/backend/client.c @@ -0,0 +1,1760 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2000-2004 Oswald Buddenhagen <[email protected]> + +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 + * + * user verification and session initiation. + */ + +#include "dm.h" +#include "dm_auth.h" +#include "dm_error.h" + +#include <sys/stat.h> +#include <pwd.h> +#include <grp.h> +#ifdef SECURE_RPC +# include <rpc/rpc.h> +# include <rpc/key_prot.h> +extern int key_setnet( struct key_netstarg *arg ); +#endif +#ifdef K5AUTH +# include <krb5/krb5.h> +#endif +#ifdef HAVE_SETUSERCONTEXT +# include <login_cap.h> +#endif +#ifdef USE_PAM +# ifdef HAVE_PAM_PAM_APPL_H +# include <pam/pam_appl.h> +# else +# include <security/pam_appl.h> +# endif +#elif defined(_AIX) /* USE_PAM */ +# include <login.h> +# include <usersec.h> +extern int loginrestrictions( const char *Name, const int Mode, const char *Tty, char **Msg ); +extern int loginfailed( const char *User, const char *Host, const char *Tty ); +extern int loginsuccess( const char *User, const char *Host, const char *Tty, char **Msg ); +#else /* USE_PAM || _AIX */ +# ifdef KERBEROS +# include <sys/param.h> +# include <krb.h> +# ifndef NO_AFS +# include <kafs.h> +# endif +# endif +/* for nologin */ +# include <sys/types.h> +# include <unistd.h> +/* for expiration */ +# include <time.h> +#endif /* USE_PAM || _AIX */ +#ifdef HAVE_SHADOW +# include <shadow.h> +#endif +#include <signal.h> + +/* + * Session data, mostly what struct verify_info was for + */ +char *curuser; +char *curpass; +char *curtype; +char *newpass; +char **userEnviron; +char **systemEnviron; +static int curuid; +static int curgid; +int cursource; + +char *dmrcuser; +char *curdmrc; +char *newdmrc; + +static struct passwd *p; +#ifdef HAVE_SETUSERCONTEXT +# ifdef HAVE_LOGIN_GETCLASS +login_cap_t *lc; +# else +struct login_cap *lc; +# endif +#endif +#ifdef USE_PAM +static pam_handle_t *pamh; +#elif defined(_AIX) +static char tty[16], hostname[100]; +#else +# ifdef USESHADOW +static struct spwd *sp; +# endif +# ifdef KERBEROS +static char krbtkfile[MAXPATHLEN]; +# endif +#endif + +#define V_RET_AUTH \ + do { \ + PrepErrorGreet (); \ + GSendInt (V_AUTH); \ + return 0; \ + } while(0) + +#define V_RET_FAIL(m) \ + do { \ + PrepErrorGreet (); \ + GSendInt (V_MSG_ERR); \ + GSendStr (m); \ + GSendInt (V_FAIL); \ + return 0; \ + } while(0) + +#ifdef USE_PAM + +# ifdef PAM_MESSAGE_NONCONST +typedef struct pam_message pam_message_type; +typedef void *pam_gi_type; +# else +typedef const struct pam_message pam_message_type; +typedef const void *pam_gi_type; +# endif + +struct pam_data { + GConvFunc gconv; + int usecur; + int abort; +}; + +static int +PAM_conv( int num_msg, + pam_message_type **msg, + struct pam_response **resp, + void *appdata_ptr ) +{ + int count; + struct pam_response *reply; + struct pam_data *pd = (struct pam_data *)appdata_ptr; + + if (!(reply = Calloc( num_msg, sizeof(*reply) ))) + return PAM_CONV_ERR; + + ReInitErrorLog(); + Debug( "PAM_conv\n" ); + for (count = 0; count < num_msg; count++) + switch (msg[count]->msg_style) { + case PAM_TEXT_INFO: + Debug( " PAM_TEXT_INFO: %s\n", msg[count]->msg ); + PrepErrorGreet(); + GSendInt( V_MSG_INFO ); + GSendStr( msg[count]->msg ); + continue; + case PAM_ERROR_MSG: + Debug( " PAM_ERROR_MSG: %s\n", msg[count]->msg ); + PrepErrorGreet(); + GSendInt( V_MSG_ERR ); + GSendStr( msg[count]->msg ); + continue; + default: + /* could do better error handling here, but see below ... */ + if (pd->usecur) { + switch (msg[count]->msg_style) { + /* case PAM_PROMPT_ECHO_ON: cannot happen */ + case PAM_PROMPT_ECHO_OFF: + Debug( " PAM_PROMPT_ECHO_OFF (usecur): %s\n", msg[count]->msg ); + if (!curpass) + pd->gconv( GCONV_PASS, 0 ); + StrDup( &reply[count].resp, curpass ); + break; + default: + LogError( "Unknown PAM message style <%d>\n", msg[count]->msg_style ); + goto conv_err; + } + } else { + switch (msg[count]->msg_style) { + case PAM_PROMPT_ECHO_ON: + Debug( " PAM_PROMPT_ECHO_ON: %s\n", msg[count]->msg ); + reply[count].resp = pd->gconv( GCONV_NORMAL, msg[count]->msg ); + break; + case PAM_PROMPT_ECHO_OFF: + Debug( " PAM_PROMPT_ECHO_OFF: %s\n", msg[count]->msg ); + reply[count].resp = pd->gconv( GCONV_HIDDEN, msg[count]->msg ); + break; +#ifdef PAM_BINARY_PROMPT + case PAM_BINARY_PROMPT: + Debug( " PAM_BINARY_PROMPT\n" ); + reply[count].resp = pd->gconv( GCONV_BINARY, msg[count]->msg ); + break; +#endif + default: + LogError( "Unknown PAM message style <%d>\n", msg[count]->msg_style ); + goto conv_err; + } + } + if (!reply[count].resp) { + Debug( " PAM_conv aborted\n" ); + pd->abort = TRUE; + goto conv_err; + } + reply[count].resp_retcode = PAM_SUCCESS; /* unused in linux-pam */ + } + Debug( " PAM_conv success\n" ); + *resp = reply; + return PAM_SUCCESS; + + conv_err: + for (; count >= 0; count--) + if (reply[count].resp) + switch (msg[count]->msg_style) { + case PAM_PROMPT_ECHO_ON: + case PAM_PROMPT_ECHO_OFF: /* could wipe ... */ +#ifdef PAM_BINARY_PROMPT + case PAM_BINARY_PROMPT: /* ... that too ... */ +#endif + free( reply[count].resp ); + break; + } + free( reply ); + return PAM_CONV_ERR; +} + +static int +PAM_conv_null( int num_msg, + pam_message_type **msg, + struct pam_response **resp, + void *appdata_ptr ATTR_UNUSED ) +{ + int count; + struct pam_response *reply; + + if (!(reply = Calloc( num_msg, sizeof(*reply) ))) + return PAM_CONV_ERR; + + ReInitErrorLog(); + Debug( "PAM_conv_null\n" ); + for (count = 0; count < num_msg; count++) { + switch (msg[count]->msg_style) { + case PAM_TEXT_INFO: + Debug( " PAM_TEXT_INFO: %s\n", msg[count]->msg ); + continue; + case PAM_ERROR_MSG: + LogError( "PAM error message: %s\n", msg[count]->msg ); + continue; + default: + /* unknown */ + Debug( " PAM_<%d>\n", msg[count]->msg_style ); + free( reply ); + return PAM_CONV_ERR; + } + reply[count].resp_retcode = PAM_SUCCESS; /* unused in linux-pam */ + } + Debug( " PAM_conv_null success\n" ); + *resp = reply; + return PAM_SUCCESS; +} + +# ifdef PAM_FAIL_DELAY +static void +fail_delay( int retval ATTR_UNUSED, unsigned usec_delay ATTR_UNUSED, + void *appdata_ptr ATTR_UNUSED ) +{} +# endif + +static int +doPAMAuth( const char *psrv, struct pam_data *pdata ) +{ + pam_gi_type pitem; + struct pam_conv pconv; + int pretc; + + pdata->abort = FALSE; + pconv.conv = PAM_conv; + pconv.appdata_ptr = (void *)pdata; + Debug( " PAM service %s\n", psrv ); + if ((pretc = pam_start( psrv, curuser, &pconv, &pamh )) != PAM_SUCCESS) + goto pam_bail2; + if ((pretc = pam_set_item( pamh, PAM_TTY, td->name )) != PAM_SUCCESS) { + pam_bail: + pam_end( pamh, pretc ); + pamh = 0; + pam_bail2: + ReInitErrorLog(); + LogError( "PAM error: %s\n", pam_strerror( 0, pretc ) ); + V_RET_FAIL( 0 ); + } + if ((td->displayType & d_location) == dForeign) { + char *cp = strchr( td->name, ':' ); + *cp = 0; + pretc = pam_set_item( pamh, PAM_RHOST, td->name ); + *cp = ':'; + if (pretc != PAM_SUCCESS) + goto pam_bail; + } +# ifdef __sun__ /* Only Solaris <= 9, but checking it does not seem worth it. */ + else if (pam_set_item( pamh, PAM_RHOST, 0 ) != PAM_SUCCESS) + goto pam_bail; +# endif +# ifdef PAM_FAIL_DELAY + pam_set_item( pamh, PAM_FAIL_DELAY, (void *)fail_delay ); +# endif + ReInitErrorLog(); + + Debug( " pam_authenticate() ...\n" ); + pretc = pam_authenticate( pamh, + td->allowNullPasswd ? 0 : PAM_DISALLOW_NULL_AUTHTOK ); + ReInitErrorLog(); + Debug( " pam_authenticate() returned: %s\n", pam_strerror( pamh, pretc ) ); + if (pdata->abort) { + pam_end( pamh, PAM_SUCCESS ); + pamh = 0; + return 0; + } + if (!curuser) { + Debug( " asking PAM for user ...\n" ); + pam_get_item( pamh, PAM_USER, &pitem ); + ReInitErrorLog(); + StrDup( &curuser, (const char *)pitem ); + GSendInt( V_PUT_USER ); + GSendStr( curuser ); + } + if (pretc != PAM_SUCCESS) { + switch (pretc) { + case PAM_USER_UNKNOWN: + case PAM_AUTH_ERR: + case PAM_MAXTRIES: /* should handle this better ... */ + case PAM_AUTHINFO_UNAVAIL: /* returned for unknown users ... bogus */ + pam_end( pamh, pretc ); + pamh = 0; + V_RET_AUTH; + default: + pam_end( pamh, pretc ); + pamh = 0; + V_RET_FAIL( 0 ); + } + } + return 1; +} + +#endif /* USE_PAM */ + +static int +#if defined(USE_PAM) || defined(_AIX) +AccNoPass( const char *un ) +{ + struct passwd *pw = 0; +# ifdef HAVE_SHADOW /* (sic!) - not USESHADOW */ + struct spwd *spw; +# endif +#else +AccNoPass( const char *un, struct passwd *pw ) +{ +#endif + struct group *gr; + char **fp; + int hg; + + if (!*un) + return 0; + + if (cursource != PWSRC_MANUAL) + return 1; + + for (hg = 0, fp = td->noPassUsers; *fp; fp++) + if (**fp == '@') + hg = 1; + else if (!strcmp( un, *fp )) + return 1; + else if (!strcmp( "*", *fp )) { +#if defined(USE_PAM) || defined(_AIX) + if (!(pw = getpwnam( un ))) + return 0; + if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*') + continue; +# ifdef HAVE_SHADOW /* (sic!) - not USESHADOW */ + if ((spw = getspnam( un )) && + (spw->sp_pwdp[0] == '!' || spw->sp_pwdp[0] == '*')) + continue; +# endif +#endif + if (pw->pw_uid) + return 1; + } + +#if defined(USE_PAM) || defined(_AIX) + if (hg && (pw || (pw = getpwnam( un )))) { +#else + if (hg) { +#endif + for (setgrent(); (gr = getgrent()); ) + for (fp = td->noPassUsers; *fp; fp++) + if (**fp == '@' && !strcmp( gr->gr_name, *fp + 1 )) { + if (pw->pw_gid == gr->gr_gid) { + endgrent(); + return 1; + } + for (; *gr->gr_mem; gr->gr_mem++) + if (!strcmp( un, *gr->gr_mem )) { + endgrent(); + return 1; + } + } + endgrent(); + } + + return 0; +} + +#if !defined(USE_PAM) && !defined(_AIX) && defined(HAVE_SETUSERCONTEXT) +# define LC_RET0 do { login_close(lc); return 0; } while(0) +#else +# define LC_RET0 return 0 +#endif + +int +Verify( GConvFunc gconv, int rootok ) +{ +#ifdef USE_PAM + const char *psrv; + struct pam_data pdata; + int pretc, pnopass; + char psrvb[64]; +#elif defined(_AIX) + char *msg, *curret; + int i, reenter; +#else + struct stat st; + const char *nolg; + char *buf; + int fd; +# ifdef HAVE_GETUSERSHELL + char *s; +# endif +# if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW) + int tim, expir, warntime, quietlog; +# endif +#endif + + Debug( "Verify ...\n" ); + +#ifdef USE_PAM + + pnopass = FALSE; + if (!strcmp( curtype, "classic" )) { + if (!gconv( GCONV_USER, 0 )) + return 0; + if (AccNoPass( curuser )) { + gconv( GCONV_PASS_ND, 0 ); + if (!*curpass) { + pnopass = TRUE; + sprintf( psrvb, "%.31s-np", PAMService ); + psrv = psrvb; + } else + psrv = PAMService; + } else + psrv = PAMService; + pdata.usecur = TRUE; + } else { + sprintf( psrvb, "%.31s-%.31s", PAMService, curtype ); + psrv = psrvb; + pdata.usecur = FALSE; + } + pdata.gconv = gconv; + if (!doPAMAuth( psrv, &pdata )) + return 0; + +#elif defined(_AIX) + + if ((td->displayType & d_location) == dForeign) { + char *tmpch; + strncpy( hostname, td->name, sizeof(hostname) - 1 ); + hostname[sizeof(hostname)-1] = '\0'; + if ((tmpch = strchr( hostname, ':' ))) + *tmpch = '\0'; + } else + hostname[0] = '\0'; + + /* tty names should only be 15 characters long */ +# if 0 + for (i = 0; i < 15 && td->name[i]; i++) { + if (td->name[i] == ':' || td->name[i] == '.') + tty[i] = '_'; + else + tty[i] = td->name[i]; + } + tty[i] = '\0'; +# else + memcpy( tty, "/dev/xdm/", 9 ); + for (i = 0; i < 6 && td->name[i]; i++) { + if (td->name[i] == ':' || td->name[i] == '.') + tty[9 + i] = '_'; + else + tty[9 + i] = td->name[i]; + } + tty[9 + i] = '\0'; +# endif + + if (!strcmp( curtype, "classic" )) { + if (!gconv( GCONV_USER, 0 )) + return 0; + if (AccNoPass( curuser )) { + gconv( GCONV_PASS_ND, 0 ); + if (!*curpass) { + Debug( "accepting despite empty password\n" ); + goto done; + } + } else + if (!gconv( GCONV_PASS, 0 )) + return 0; + enduserdb(); + msg = NULL; + if ((i = authenticate( curuser, curpass, &reenter, &msg ))) { + Debug( "authenticate() failed: %s\n", msg ); + if (msg) + free( msg ); + loginfailed( curuser, hostname, tty ); + if (i == ENOENT || i == ESAD) + V_RET_AUTH; + else + V_RET_FAIL( 0 ); + } + if (reenter) { + LogError( "authenticate() requests more data: %s\n", msg ); + free( msg ); + V_RET_FAIL( 0 ); + } + } else if (!strcmp( curtype, "generic" )) { + if (!gconv( GCONV_USER, 0 )) + return 0; + for (curret = 0;;) { + msg = NULL; + if ((i = authenticate( curuser, curret, &reenter, &msg ))) { + Debug( "authenticate() failed: %s\n", msg ); + if (msg) + free( msg ); + loginfailed( curuser, hostname, tty ); + if (i == ENOENT || i == ESAD) + V_RET_AUTH; + else + V_RET_FAIL( 0 ); + } + if (curret) + free( curret ); + if (!reenter) + break; + if (!(curret = gconv( GCONV_HIDDEN, msg ))) + return 0; + free( msg ); + } + } else { + LogError( "Unsupported authentication type %\"s requested\n", curtype ); + V_RET_FAIL( 0 ); + } + if (msg) { + PrepErrorGreet(); + GSendInt( V_MSG_INFO ); + GSendStr( msg ); + free( msg ); + } + + done: + +#else + + if (strcmp( curtype, "classic" )) { + LogError( "Unsupported authentication type %\"s requested\n", curtype ); + V_RET_FAIL( 0 ); + } + + if (!gconv( GCONV_USER, 0 )) + return 0; + + if (!(p = getpwnam( curuser ))) { + Debug( "getpwnam() failed.\n" ); + gconv( GCONV_PASS, 0 ); + V_RET_AUTH; + } + if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') { + Debug( "account is locked\n" ); + gconv( GCONV_PASS, 0 ); + V_RET_AUTH; + } + +# ifdef USESHADOW + if ((sp = getspnam( curuser ))) { + p->pw_passwd = sp->sp_pwdp; + if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') { + Debug( "account is locked\n" ); + gconv( GCONV_PASS, 0 ); + V_RET_AUTH; + } + } else + Debug( "getspnam() failed: %m. Are you root?\n" ); +# endif + + if (!*p->pw_passwd) { + if (!td->allowNullPasswd) { + Debug( "denying user with empty password\n" ); + gconv( GCONV_PASS, 0 ); + V_RET_AUTH; + } + goto nplogin; + } + + if (AccNoPass( curuser, p )) { + nplogin: + gconv( GCONV_PASS_ND, 0 ); + if (!*curpass) { + Debug( "accepting password-less login\n" ); + goto done; + } + } else + if (!gconv( GCONV_PASS, 0 )) + return 0; + +# ifdef KERBEROS + if (p->pw_uid) { + int ret; + char realm[REALM_SZ]; + + if (krb_get_lrealm( realm, 1 )) { + LogError( "Can't get KerberosIV realm.\n" ); + V_RET_FAIL( 0 ); + } + + sprintf( krbtkfile, "%s.%.*s", TKT_ROOT, MAXPATHLEN - strlen( TKT_ROOT ) - 2, td->name ); + krb_set_tkt_string( krbtkfile ); + unlink( krbtkfile ); + + ret = krb_verify_user( curuser, "", realm, curpass, 1, "rcmd" ); + if (ret == KSUCCESS) { + chown( krbtkfile, p->pw_uid, p->pw_gid ); + Debug( "KerberosIV verify succeeded\n" ); + goto done; + } else if (ret != KDC_PR_UNKNOWN && ret != SKDC_CANT) { + LogError( "KerberosIV verification failure %\"s for %s\n", + krb_get_err_text( ret ), curuser ); + krbtkfile[0] = '\0'; + V_RET_FAIL( 0 ); + } + Debug( "KerberosIV verify failed: %s\n", krb_get_err_text( ret ) ); + } + krbtkfile[0] = '\0'; +# endif /* KERBEROS */ + +# if defined(ultrix) || defined(__ultrix__) + if (authenticate_user( p, curpass, NULL ) < 0) +# elif defined(HAVE_CRYPT) + if (strcmp( crypt( curpass, p->pw_passwd ), p->pw_passwd )) +# else + if (strcmp( curpass, p->pw_passwd )) +# endif + { + Debug( "password verify failed\n" ); + V_RET_AUTH; + } + + done: + +#endif /* !defined(USE_PAM) && !defined(_AIX) */ + + Debug( "restrict %s ...\n", curuser ); + +#if defined(USE_PAM) || defined(_AIX) + if (!(p = getpwnam( curuser ))) { + LogError( "getpwnam(%s) failed.\n", curuser ); + V_RET_FAIL( 0 ); + } +#endif + if (!p->pw_uid) { + if (!rootok && !td->allowRootLogin) + V_RET_FAIL( "Root logins are not allowed" ); + return 1; /* don't deny root to log in */ + } + +#ifdef USE_PAM + + Debug( " pam_acct_mgmt() ...\n" ); + pretc = pam_acct_mgmt( pamh, 0 ); + ReInitErrorLog(); + Debug( " pam_acct_mgmt() returned: %s\n", pam_strerror( pamh, pretc ) ); + if (pretc == PAM_NEW_AUTHTOK_REQD) { + pdata.usecur = FALSE; + pdata.gconv = conv_interact; + /* pam will have output a message already, so no PrepErrorGreet () */ + if (gconv != conv_interact || pnopass) { + pam_end( pamh, PAM_SUCCESS ); + pamh = 0; + GSendInt( V_CHTOK_AUTH ); + /* this cannot auth the wrong user, as only classic auths get here */ + while (!doPAMAuth( PAMService, &pdata )) + if (pdata.abort) + return 0; + GSendInt( V_PRE_OK ); + } else + GSendInt( V_CHTOK ); + for (;;) { + Debug( " pam_chauthtok() ...\n" ); + pretc = pam_chauthtok( pamh, PAM_CHANGE_EXPIRED_AUTHTOK ); + ReInitErrorLog(); + Debug( " pam_chauthtok() returned: %s\n", pam_strerror( pamh, pretc ) ); + if (pdata.abort) { + pam_end( pamh, PAM_SUCCESS ); + pamh = 0; + return 0; + } + if (pretc == PAM_SUCCESS) + break; + /* effectively there is only PAM_AUTHTOK_ERR */ + GSendInt( V_FAIL ); + } + if (curpass) + free( curpass ); + curpass = newpass; + newpass = 0; + } else if (pretc != PAM_SUCCESS) { + pam_end( pamh, pretc ); + pamh = 0; + V_RET_AUTH; + } + +#elif defined(_AIX) /* USE_PAM */ + + msg = NULL; + if (loginrestrictions( curuser, + ((td->displayType & d_location) == dForeign) ? S_RLOGIN : S_LOGIN, + tty, &msg ) == -1) + { + Debug( "loginrestrictions() - %s\n", msg ? msg : "error" ); + loginfailed( curuser, hostname, tty ); + PrepErrorGreet(); + if (msg) { + GSendInt( V_MSG_ERR ); + GSendStr( msg ); + } + GSendInt( V_AUTH ); + return 0; + } + if (msg) + free( (void *)msg ); + +#endif /* USE_PAM || _AIX */ + +#ifndef _AIX + +# ifdef HAVE_SETUSERCONTEXT +# ifdef HAVE_LOGIN_GETCLASS + lc = login_getclass( p->pw_class ); +# else + lc = login_getpwclass( p ); +# endif + if (!lc) + V_RET_FAIL( 0 ); + + p->pw_shell = login_getcapstr( lc, "shell", p->pw_shell, p->pw_shell ); +# endif + +# ifndef USE_PAM + +/* restrict_expired */ +# if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW) + +# if !defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || (!defined(HAVE_SETUSERCONTEXT) && defined(USESHADOW)) + if (sp) +# endif + { + +# define DEFAULT_WARN (2L * 7L) /* Two weeks */ + + tim = time( NULL ) / 86400L; + +# ifdef HAVE_SETUSERCONTEXT + quietlog = login_getcapbool( lc, "hushlogin", 0 ); + warntime = login_getcaptime( lc, "warnexpire", + DEFAULT_WARN * 86400L, + DEFAULT_WARN * 86400L ) / 86400L; +# else + quietlog = 0; +# ifdef USESHADOW + warntime = sp->sp_warn != -1 ? sp->sp_warn : DEFAULT_WARN; +# else + warntime = DEFAULT_WARN; +# endif +# endif + +# ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE + if (p->pw_expire) { + expir = p->pw_expire / 86400L; +# else + if (sp->sp_expire != -1) { + expir = sp->sp_expire; +# endif + if (tim > expir) { + PrepErrorGreet(); + GSendInt( V_MSG_ERR ); + GSendStr( "Your account has expired;" + " please contact your system administrator" ); + GSendInt( V_FAIL ); + LC_RET0; + } else if (tim > (expir - warntime) && !quietlog) { + ASPrintf( &buf, + "Warning: your account will expire in %d day(s)", + expir - tim ); + if (buf) { + PrepErrorGreet(); + GSendInt( V_MSG_INFO ); + GSendStr( buf ); + free( buf ); + } + } + } + +# ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE + if (p->pw_change) { + expir = p->pw_change / 86400L; +# else + if (!sp->sp_lstchg) { + PrepErrorGreet(); + GSendInt( V_MSG_ERR ); + GSendStr( "You are required to change your password immediately" + " (root enforced)" ); + /* XXX todo password change */ + GSendInt( V_FAIL ); + LC_RET0; + } else if (sp->sp_max != -1) { + expir = sp->sp_lstchg + sp->sp_max; + if (sp->sp_inact != -1 && tim > expir + sp->sp_inact) { + PrepErrorGreet(); + GSendInt( V_MSG_ERR ); + GSendStr( "Your account has expired;" + " please contact your system administrator" ); + GSendInt( V_FAIL ); + LC_RET0; + } +# endif + if (tim > expir) { + PrepErrorGreet(); + GSendInt( V_MSG_ERR ); + GSendStr( "You are required to change your password immediately" + " (password aged)" ); + /* XXX todo password change */ + GSendInt( V_FAIL ); + LC_RET0; + } else if (tim > (expir - warntime) && !quietlog) { + ASPrintf( &buf, + "Warning: your password will expire in %d day(s)", + expir - tim ); + if (buf) { + PrepErrorGreet(); + GSendInt( V_MSG_INFO ); + GSendStr( buf ); + free( buf ); + } + } + } + + } + +# endif /* HAVE_STRUCT_PASSWD_PW_EXPIRE || USESHADOW */ + +/* restrict_nologin */ +# ifndef _PATH_NOLOGIN +# define _PATH_NOLOGIN "/etc/nologin" +# endif + + if (( +# ifdef HAVE_SETUSERCONTEXT + /* Do we ignore a nologin file? */ + !login_getcapbool( lc, "ignorenologin", 0 )) && + (!stat( (nolg = login_getcapstr( lc, "nologin", "", NULL )), &st ) || +# endif + !stat( (nolg = _PATH_NOLOGIN), &st ))) + { + PrepErrorGreet(); + GSendInt( V_MSG_ERR ); + if (st.st_size && (fd = open( nolg, O_RDONLY )) >= 0) { + if ((buf = Malloc( st.st_size + 1 ))) { + if (read( fd, buf, st.st_size ) == st.st_size) { + buf[st.st_size] = 0; + GSendStr( buf ); + free( buf ); + close( fd ); + GSendInt( V_FAIL ); + LC_RET0; + } + free( buf ); + } + close( fd ); + } + GSendStr( "Logins are not allowed at the moment.\nTry again later" ); + GSendInt( V_FAIL ); + LC_RET0; + } + +/* restrict_time */ +# if defined(HAVE_SETUSERCONTEXT) && defined(HAVE_AUTH_TIMEOK) + if (!auth_timeok( lc, time( NULL ) )) { + PrepErrorGreet(); + GSendInt( V_MSG_ERR ); + GSendStr( "You are not allowed to login at the moment" ); + GSendInt( V_FAIL ); + LC_RET0; + } +# endif + +# ifdef HAVE_GETUSERSHELL + for (;;) { + if (!(s = getusershell())) { + Debug( "shell not in /etc/shells\n" ); + endusershell(); + V_RET_FAIL( "Your login shell is not listed in /etc/shells" ); + } + if (!strcmp( s, p->pw_shell )) { + endusershell(); + break; + } + } +# endif + +# endif /* !USE_PAM */ + +/* restrict_nohome */ +# ifdef HAVE_SETUSERCONTEXT + if (login_getcapbool( lc, "requirehome", 0 )) { + struct stat st; + if (!*p->pw_dir || stat( p->pw_dir, &st ) || st.st_uid != p->pw_uid) { + PrepErrorGreet(); + GSendInt( V_MSG_ERR ); + GSendStr( "Home folder not available" ); + GSendInt( V_FAIL ); + LC_RET0; + } + } +# endif + +#endif /* !_AIX */ + + return 1; + +} + + +static const char *envvars[] = { + "TZ", /* SYSV and SVR4, but never hurts */ +#ifdef _AIX + "AUTHSTATE", /* for kerberos */ +#endif + NULL +}; + + +#if defined(USE_PAM) && defined(HAVE_INITGROUPS) +static int num_saved_gids; +static gid_t *saved_gids; + +static int +saveGids( void ) +{ + num_saved_gids = getgroups( 0, 0 ); + if (!(saved_gids = Malloc( sizeof(gid_t) * num_saved_gids ))) + return 0; + if (getgroups( num_saved_gids, saved_gids ) < 0) { + LogError( "saving groups failed: %m\n" ); + return 0; + } + return 1; +} + +static int +restoreGids( void ) +{ + if (setgroups( num_saved_gids, saved_gids ) < 0) { + LogError( "restoring groups failed: %m\n" ); + return 0; + } + if (setgid( p->pw_gid ) < 0) { + LogError( "restoring gid failed: %m\n" ); + return 0; + } + return 1; +} +#endif /* USE_PAM && HAVE_INITGROUPS */ + +static int +resetGids( void ) +{ +#ifdef HAVE_INITGROUPS + if (setgroups( 0, &p->pw_gid /* anything */ ) < 0) { + LogError( "restoring groups failed: %m\n" ); + return 0; + } +#endif + if (setgid( 0 ) < 0) { + LogError( "restoring gid failed: %m\n" ); + return 0; + } + return 1; +} + +static int +SetGid( const char *name, int gid ) +{ + if (setgid( gid ) < 0) { + LogError( "setgid(%d) (user %s) failed: %m\n", gid, name ); + return 0; + } +#ifdef HAVE_INITGROUPS + if (initgroups( name, gid ) < 0) { + LogError( "initgroups for %s failed: %m\n", name ); + setgid( 0 ); + return 0; + } +#endif /* QNX4 doesn't support multi-groups, no initgroups() */ + return 1; +} + +static int +SetUid( const char *name, int uid ) +{ + if (setuid( uid ) < 0) { + LogError( "setuid(%d) (user %s) failed: %m\n", uid, name ); + return 0; + } + return 1; +} + +static int +SetUser( const char *name, int uid, int gid ) +{ + if (SetGid( name, gid )) { + if (SetUid( name, uid )) + return 1; + resetGids(); + } + return 0; +} + +#if defined(SECURE_RPC) || defined(K5AUTH) +static void +NukeAuth( int len, const char *name ) +{ + int i; + + for (i = 0; i < td->authNum; i++) + if (td->authorizations[i]->name_length == len && + !memcmp( td->authorizations[i]->name, name, len )) + { + memcpy( &td->authorizations[i], &td->authorizations[i+1], + sizeof(td->authorizations[i]) * (--td->authNum - i) ); + break; + } +} +#endif + +static void +mergeSessionArgs( int cansave ) +{ + char *mfname; + const char *fname; + int i, needsave; + + mfname = 0; + fname = ".dmrc"; + if ((!curdmrc || newdmrc) && *dmrcDir) + if (StrApp( &mfname, dmrcDir, "/", curuser, fname, (char *)0 )) + fname = mfname; + needsave = 0; + if (!curdmrc) { + curdmrc = iniLoad( fname ); + if (!curdmrc) { + StrDup( &curdmrc, "[Desktop]\nSession=default\n" ); + needsave = 1; + } + } + if (newdmrc) { + curdmrc = iniMerge( curdmrc, newdmrc ); + needsave = 1; + } + if (needsave && cansave) + if (!iniSave( curdmrc, fname ) && errno == ENOENT && mfname) { + for (i = 0; mfname[i]; i++) + if (mfname[i] == '/') { + mfname[i] = 0; + mkdir( mfname, 0755 ); + mfname[i] = '/'; + } + iniSave( curdmrc, mfname ); + } + if (mfname) + free( mfname ); +} + +static int removeAuth; +#ifdef USE_PAM +static int removeSession; +static int removeCreds; +#endif + +int +StartClient() +{ + const char *home, *sessargs, *desksess; + char **env, *xma; + char **argv, *fname, *str; +#ifdef USE_PAM + char **pam_env; +# ifdef _AIX + char **saved_env; +# endif + struct pam_conv pconv; + int pretc; +#else +# ifdef _AIX + char *msg; + char **theenv; + extern char **newenv; /* from libs.a, this is set up by setpenv */ +# endif +#endif +#ifdef HAVE_SETUSERCONTEXT + extern char **environ; +#endif + char *failsafeArgv[2], *lname; + int i, pid, lfd; + + if (StrCmp( dmrcuser, curuser )) { + if (curdmrc) { free( curdmrc ); curdmrc = 0; } + if (dmrcuser) { free( dmrcuser ); dmrcuser = 0; } + } + +#if defined(USE_PAM) || defined(_AIX) + if (!(p = getpwnam( curuser ))) { + LogError( "getpwnam(%s) failed.\n", curuser ); + return 0; + } +#endif + +#ifndef USE_PAM +# ifdef _AIX + msg = NULL; + loginsuccess( curuser, hostname, tty, &msg ); + if (msg) { + Debug( "loginsuccess() - %s\n", msg ); + free( (void *)msg ); + } +# else /* _AIX */ +# if defined(KERBEROS) && !defined(NO_AFS) + if (krbtkfile[0] != '\0') { + if (k_hasafs()) { + if (k_setpag() == -1) + LogError( "setpag() for %s failed\n", curuser ); + if ((ret = k_afsklog( NULL, NULL )) != KSUCCESS) + LogError( "AFS Warning: %s\n", krb_get_err_text( ret ) ); + } + } +# endif /* KERBEROS && AFS */ +# endif /* _AIX */ +#endif /* !PAM */ + + curuid = p->pw_uid; + curgid = p->pw_gid; + + env = baseEnv( curuser ); + xma = 0; + if (td->ctrl.fpath && StrDup( &xma, td->ctrl.fpath )) { + if ((td->allowShutdown == SHUT_ALL || + (td->allowShutdown == SHUT_ROOT && !curuser)) && + StrApp( &xma, ",maysd", (char *)0 )) + { + if (td->allowNuke == SHUT_ALL || + (td->allowNuke == SHUT_ROOT && !curuser)) + StrApp( &xma, ",mayfn", (char *)0 ); + StrApp( &xma, td->defSdMode == SHUT_FORCENOW ? ",fn" : + td->defSdMode == SHUT_TRYNOW ? ",tn" : ",sched", + (char *)0 ); + } + if ((td->displayType & d_location) == dLocal && AnyReserveDisplays()) + StrApp( &xma, ",rsvd", (char *)0 ); + } else + StrDup( &xma, "true" ); + StrApp( &xma, ",method=", curtype, (char *)0 ); + if (td_setup) + StrApp( &xma, ",auto", (char *)0 ); + if (xma) { + env = setEnv( env, "XDM_MANAGED", xma ); + free( xma ); + } + if (td->autoLock && cursource == PWSRC_AUTOLOGIN) + env = setEnv( env, "DESKTOP_LOCKED", "true" ); + env = setEnv( env, "PATH", curuid ? td->userPath : td->systemPath ); + env = setEnv( env, "SHELL", p->pw_shell ); + env = setEnv( env, "HOME", p->pw_dir ); +#if !defined(USE_PAM) && !defined(_AIX) && defined(KERBEROS) + if (krbtkfile[0] != '\0') + env = setEnv( env, "KRBTKFILE", krbtkfile ); +#endif + userEnviron = inheritEnv( env, envvars ); + env = systemEnv( p->pw_name ); + systemEnviron = setEnv( env, "HOME", p->pw_dir ); + Debug( "user environment:\n%[|''>'\n's" + "system environment:\n%[|''>'\n's" + "end of environments\n", + userEnviron, + systemEnviron ); + + /* + * for user-based authorization schemes, + * add the user to the server's allowed "hosts" list. + */ + for (i = 0; i < td->authNum; i++) { +#ifdef SECURE_RPC + if (td->authorizations[i]->name_length == 9 && + !memcmp( td->authorizations[i]->name, "SUN-DES-1", 9 )) + { + XHostAddress addr; + char netname[MAXNETNAMELEN+1]; + char domainname[MAXNETNAMELEN+1]; + + getdomainname( domainname, sizeof(domainname) ); + user2netname( netname, curuid, domainname ); + addr.family = FamilyNetname; + addr.length = strlen( netname ); + addr.address = netname; + XAddHost( dpy, &addr ); + } +#endif +#ifdef K5AUTH + if (td->authorizations[i]->name_length == 14 && + !memcmp( td->authorizations[i]->name, "MIT-KERBEROS-5", 14 )) + { + /* Update server's auth file with user-specific info. + * Don't need to AddHost because X server will do that + * automatically when it reads the cache we are about + * to point it at. + */ + XauDisposeAuth( td->authorizations[i] ); + td->authorizations[i] = + Krb5GetAuthFor( 14, "MIT-KERBEROS-5", td->name ); + SaveServerAuthorizations( td, td->authorizations, td->authNum ); + } +#endif + } + + if (*dmrcDir) + mergeSessionArgs( TRUE ); + + Debug( "now starting the session\n" ); + +#ifdef USE_PAM + /* the greeter is gone by now ... */ + pconv.conv = PAM_conv_null; + pconv.appdata_ptr = 0; + if ((pretc = pam_set_item( pamh, PAM_CONV, &pconv )) != PAM_SUCCESS) { + ReInitErrorLog(); + LogError( "pam_set_item() for %s failed: %s\n", + curuser, pam_strerror( pamh, pretc ) ); + return 0; + } + ReInitErrorLog(); +#endif + +#ifdef USE_PAM + +# ifdef HAVE_SETUSERCONTEXT + if (setusercontext( lc, p, p->pw_uid, LOGIN_SETGROUP )) { + LogError( "setusercontext(groups) for %s failed: %m\n", + curuser ); + return 0; + } +# else + if (!SetGid( curuser, curgid )) + return 0; +# endif + +# ifdef _AIX + if (!(pam_env = initStrArr( 0 ))) { + resetGids(); + return 0; + } + saved_env = environ; + environ = pam_env; +# endif + removeCreds = 1; /* set it first - i don't trust PAM's rollback */ + pretc = pam_setcred( pamh, 0 ); + ReInitErrorLog(); +# ifdef _AIX + pam_env = environ; + environ = saved_env; +# endif +# ifdef HAVE_INITGROUPS + /* This seems to be a strange place for it, but do it: + - after the initial groups are set + - after pam_setcred might have set something, even in the error case + - before pam_setcred(DELETE_CRED) might need it + */ + if (!saveGids()) + return 0; +# endif + if (pretc != PAM_SUCCESS) { + LogError( "pam_setcred() for %s failed: %s\n", + curuser, pam_strerror( pamh, pretc ) ); + resetGids(); + return 0; + } + + removeSession = 1; /* set it first - same as above */ + pretc = pam_open_session( pamh, 0 ); + ReInitErrorLog(); + if (pretc != PAM_SUCCESS) { + LogError( "pam_open_session() for %s failed: %s\n", + curuser, pam_strerror( pamh, pretc ) ); + resetGids(); + return 0; + } + + /* we don't want sessreg and the startup/reset scripts run with user + credentials. unfortunately, we can reset only the gids. */ + resetGids(); + +# define D_LOGIN_SETGROUP LOGIN_SETGROUP +#else /* USE_PAM */ +# define D_LOGIN_SETGROUP 0 +#endif /* USE_PAM */ + + removeAuth = 1; + chownCtrl( &td->ctrl, curuid ); + endpwent(); +#if !defined(USE_PAM) && defined(USESHADOW) && !defined(_AIX) + endspent(); +#endif + ClearCloseOnFork( mstrtalk.pipe->wfd ); + switch (pid = Fork()) { + case 0: + + sessreg( td, getpid(), curuser, curuid ); + + if (source( systemEnviron, td->startup, td_setup )) { + LogError( "Cannot execute startup script %\"s\n", td->startup ); + exit( 1 ); + } + + if (Setjmp( mstrtalk.errjmp )) + exit( 1 ); + GSet( &mstrtalk ); + + setsid(); + Signal( SIGINT, SIG_DFL ); + + /* Memory leaks are ok here as we exec() soon. */ + +#if defined(USE_PAM) || !defined(_AIX) + +# ifdef USE_PAM + /* pass in environment variables set by libpam and modules it called */ +# ifndef _AIX + pam_env = pam_getenvlist( pamh ); + ReInitErrorLog(); +# endif + if (pam_env) + for (; *pam_env; pam_env++) + userEnviron = putEnv( *pam_env, userEnviron ); +# endif + +# ifdef HAVE_SETLOGIN + if (setlogin( curuser ) < 0) { + LogError( "setlogin for %s failed: %m\n", curuser ); + exit( 1 ); + } +# define D_LOGIN_SETLOGIN LOGIN_SETLOGIN +# else +# define D_LOGIN_SETLOGIN 0 +# endif + +# if defined(USE_PAM) && defined(HAVE_INITGROUPS) + if (!restoreGids()) + exit( 1 ); +# endif + +# ifndef HAVE_SETUSERCONTEXT + +# ifdef USE_PAM + if (!SetUid( curuser, curuid )) + exit( 1 ); +# else + if (!SetUser( curuser, curuid, curgid )) + exit( 1 ); +# endif + +# else /* !HAVE_SETUSERCONTEXT */ + + /* + * Destroy environment. + * We need to do this before setusercontext() because that may + * set or reset some environment variables. + */ + if (!(environ = initStrArr( 0 ))) + exit( 1 ); + + /* + * Set the user's credentials: uid, gid, groups, + * environment variables, resource limits, and umask. + */ + if (setusercontext( lc, p, p->pw_uid, + LOGIN_SETALL & ~(D_LOGIN_SETGROUP|D_LOGIN_SETLOGIN) ) < 0) + { + LogError( "setusercontext for %s failed: %m\n", curuser ); + exit( 1 ); + } + + for (i = 0; environ[i]; i++) + userEnviron = putEnv( environ[i], userEnviron ); + +# endif /* !HAVE_SETUSERCONTEXT */ + +#else /* PAM || !_AIX */ + /* + * Set the user's credentials: uid, gid, groups, + * audit classes, user limits, and umask. + */ + if (setpcred( curuser, NULL ) == -1) { + LogError( "setpcred for %s failed: %m\n", curuser ); + exit( 1 ); + } + + /* + * Set the users process environment. Store protected variables and + * obtain updated user environment list. This call will initialize + * global 'newenv'. + */ + if (setpenv( curuser, PENV_INIT | PENV_ARGV | PENV_NOEXEC, + userEnviron, NULL ) != 0) + { + LogError( "Can't set %s's process environment\n", curuser ); + exit( 1 ); + } + userEnviron = newenv; + +#endif /* _AIX */ + + /* + * for user-based authorization schemes, + * use the password to get the user's credentials. + */ +#ifdef SECURE_RPC + /* do like "keylogin" program */ + if (!curpass[0]) + LogInfo( "No password for NIS provided.\n" ); + else { + char netname[MAXNETNAMELEN+1], secretkey[HEXKEYBYTES+1]; + int nameret, keyret; + int len; + int key_set_ok = 0; + struct key_netstarg netst; + + nameret = getnetname( netname ); + Debug( "user netname: %s\n", netname ); + len = strlen( curpass ); + if (len > 8) + bzero( curpass + 8, len - 8 ); + keyret = getsecretkey( netname, secretkey, curpass ); + Debug( "getsecretkey returns %d, key length %d\n", + keyret, strlen( secretkey ) ); + netst.st_netname = netname; + memcpy( netst.st_priv_key, secretkey, HEXKEYBYTES ); + memset( netst.st_pub_key, 0, HEXKEYBYTES ); + if (key_setnet( &netst ) < 0) + Debug( "Could not set secret key.\n" ); + /* is there a key, and do we have the right password? */ + if (keyret == 1) { + if (*secretkey) { + keyret = key_setsecret( secretkey ); + Debug( "key_setsecret returns %d\n", keyret ); + if (keyret == -1) + LogError( "Failed to set NIS secret key\n" ); + else + key_set_ok = 1; + } else { + /* found a key, but couldn't interpret it */ + LogError( "Password incorrect for NIS principal %s\n", + nameret ? netname : curuser ); + } + } + if (!key_set_ok) + NukeAuth( 9, "SUN-DES-1" ); + bzero( secretkey, strlen( secretkey ) ); + } +#endif +#ifdef K5AUTH + /* do like "kinit" program */ + if (!curpass[0]) + LogInfo( "No password for Kerberos5 provided.\n" ); + else + if ((str = Krb5Init( curuser, curpass, td->name ))) + userEnviron = setEnv( userEnviron, "KRB5CCNAME", str ); + else + NukeAuth( 14, "MIT-KERBEROS-5" ); +#endif /* K5AUTH */ + if (td->autoReLogin) { + GSendInt( D_ReLogin ); + GSendStr( curuser ); + GSendStr( curpass ); + GSendStr( newdmrc ); + } + if (curpass) + bzero( curpass, strlen( curpass ) ); + SetUserAuthorization( td ); + home = getEnv( userEnviron, "HOME" ); + if (home) { + if (chdir( home ) < 0) { + LogError( "Cannot chdir to %s's home %s: %m, using /\n", + curuser, home ); + home = 0; + userEnviron = setEnv( userEnviron, "HOME", "/" ); + goto cdroot; + } + ASPrintf( &lname, td->clientLogFile, td->name ); + if ((lfd = creat( lname, 0600 )) < 0) { + LogWarn( "Cannot create session log file %s: %m\n", lname ); + free( lname ); + goto tmperr; + } + } else { + cdroot: + chdir( "/" ); + tmperr: + ASPrintf( &lname, "/tmp/xerr-%s-%s", curuser, td->name ); + unlink( lname ); + if ((lfd = open( lname, O_WRONLY|O_CREAT|O_EXCL, 0600 )) < 0) { + LogError( "Cannot create fallback session log file %s: %m\n", + lname ); + goto logerr; + } + } + dup2( lfd, 1 ); + dup2( lfd, 2 ); + close( lfd ); + logerr: + free( lname ); + if (!*dmrcDir) + mergeSessionArgs( home != 0 ); + if (!(desksess = iniEntry( curdmrc, "Desktop", "Session", 0 ))) + desksess = "failsafe"; /* only due to OOM */ + GSendInt( D_User ); + GSendInt( curuid ); + GSendStr( curuser ); + GSendStr( desksess ); + close( mstrtalk.pipe->wfd ); + userEnviron = setEnv( userEnviron, "DESKTOP_SESSION", desksess ); + for (i = 0; td->sessionsDirs[i]; i++) { + fname = 0; + if (StrApp( &fname, td->sessionsDirs[i], "/", desksess, ".desktop", (char *)0 )) { + if ((str = iniLoad( fname ))) { + if (!StrCmp( iniEntry( str, "Desktop Entry", "Hidden", 0 ), "true" ) || + !(sessargs = iniEntry( str, "Desktop Entry", "Exec", 0 ))) + sessargs = ""; + free( str ); + free( fname ); + goto gotit; + } + free( fname ); + } + } + if (!strcmp( desksess, "failsafe" ) || + !strcmp( desksess, "default" ) || + !strcmp( desksess, "custom" )) + sessargs = desksess; + else + sessargs = ""; + gotit: + if (!(argv = parseArgs( (char **)0, td->session )) || + !(argv = addStrArr( argv, sessargs, -1 ))) + exit( 1 ); + if (argv[0] && *argv[0]) { + Debug( "executing session %\"[s\n", argv ); + execute( argv, userEnviron ); + LogError( "Session %\"s execution failed: %m\n", argv[0] ); + } else + LogError( "Session has no command/arguments\n" ); + failsafeArgv[0] = td->failsafeClient; + failsafeArgv[1] = 0; + execute( failsafeArgv, userEnviron ); + LogError( "Failsafe client %\"s execution failed: %m\n", + failsafeArgv[0] ); + exit( 1 ); + case -1: + RegisterCloseOnFork( mstrtalk.pipe->wfd ); + LogError( "Forking session on %s failed: %m\n", td->name ); + return 0; + default: + RegisterCloseOnFork( mstrtalk.pipe->wfd ); + Debug( "StartSession, fork succeeded %d\n", pid ); + return pid; + } +} + +void +SessionExit( int status ) +{ + int pid; +#ifdef USE_PAM + int pretc; +#endif + + Signal( SIGTERM, SIG_IGN ); + + if (removeAuth) { + if (source( systemEnviron, td->reset, td_setup )) + LogError( "Cannot execute reset script %\"s\n", td->reset ); + sessreg( td, 0, 0, 0 ); + + switch ((pid = Fork())) { + case 0: +#if defined(USE_PAM) && defined(HAVE_INITGROUPS) + if (restoreGids() && SetUid( curuser, curuid )) +#else + if (SetUser( curuser, curuid, curgid )) +#endif + + { + RemoveUserAuthorization( td ); +#ifdef K5AUTH + Krb5Destroy( td->name ); +#endif /* K5AUTH */ +#if !defined(USE_PAM) && !defined(_AIX) +# ifdef KERBEROS + if (krbtkfile[0]) { + (void)dest_tkt(); +# ifndef NO_AFS + if (k_hasafs()) + (void)k_unlog(); +# endif + } +# endif +#endif /* !USE_PAM && !_AIX*/ + } + exit( 0 ); + case -1: + LogError( "Cannot clean up session: fork() failed: %m" ); + break; + default: + Wait4( pid ); + break; + } + } + +#ifdef USE_PAM + if (removeCreds) { +# ifdef HAVE_INITGROUPS + restoreGids(); +# endif + if (removeSession) + if ((pretc = pam_close_session( pamh, 0 )) != PAM_SUCCESS) + LogError( "pam_close_session() failed: %s\n", + pam_strerror( pamh, pretc ) ); + if ((pretc = pam_setcred( pamh, PAM_DELETE_CRED )) != PAM_SUCCESS) + LogError( "pam_setcred(DELETE_CRED) failed: %s\n", + pam_strerror( pamh, pretc ) ); + resetGids(); + } + if (pamh) { + pam_end( pamh, PAM_SUCCESS ); + ReInitErrorLog(); + } +#endif + + /* make sure the server gets reset after the session is over */ + if (td->serverPid >= 2) { + if (!td->terminateServer && td->resetSignal) + TerminateProcess( td->serverPid, td->resetSignal ); + } else + ResetServer( td ); + Debug( "display %s exiting with status %d\n", td->name, status ); + exit( status ); +} + +int +ReadDmrc() +{ + char *data, *fname = 0; + int len, pid, pfd[2], err; + + if (!dmrcuser || !dmrcuser[0] || !(p = getpwnam( dmrcuser ))) + return GE_NoUser; + + if (*dmrcDir) { + if (!StrApp( &fname, dmrcDir, "/", dmrcuser, ".dmrc", (char *)0 )) + return GE_Error; + if (!(curdmrc = iniLoad( fname ))) { + free( fname ); + return GE_Ok; + } + free( fname ); + return GE_NoFile; + } + + if (!StrApp( &fname, p->pw_dir, "/.dmrc", (char *)0 )) + return GE_Error; + if (pipe( pfd )) + return GE_Error; + if ((pid = Fork()) < 0) { + close( pfd[0] ); + close( pfd[1] ); + return GE_Error; + } + if (!pid) { + if (!SetUser( p->pw_name, p->pw_uid, p->pw_gid )) + exit( 0 ); + if (!(data = iniLoad( fname ))) { + static const int m1 = -1; + write( pfd[1], &m1, sizeof(int) ); + exit( 0 ); + } + len = strlen( data ); + write( pfd[1], &len, sizeof(int) ); + write( pfd[1], data, len + 1 ); + exit( 0 ); + } + close( pfd[1] ); + free( fname ); + err = GE_Error; + if (Reader( pfd[0], &len, sizeof(int) ) == sizeof(int)) { + if (len == -1) + err = GE_Denied; + else if ((curdmrc = Malloc( len + 1 ))) { + if (Reader( pfd[0], curdmrc, len + 1 ) == len + 1) + err = GE_Ok; + else { + free( curdmrc ); + curdmrc = 0; + } + } + } + close( pfd[0] ); + (void)Wait4( pid ); + return err; +} diff --git a/kdm/backend/ctrl.c b/kdm/backend/ctrl.c new file mode 100644 index 000000000..d813af0f2 --- /dev/null +++ b/kdm/backend/ctrl.c @@ -0,0 +1,955 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2001-2005 Oswald Buddenhagen <[email protected]> + +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 + * + * display manager + */ + +#include "dm.h" +#include "dm_socket.h" +#include "dm_error.h" + +#include <string.h> +#include <signal.h> +#include <pwd.h> + +static void +acceptSock( CtrlRec *cr ) +{ + struct cmdsock *cs; + int fd; + + if ((fd = accept( cr->fd, 0, 0 )) < 0) { + bust: + LogError( "Error accepting command connection\n" ); + return; + } + if (!(cs = Malloc( sizeof(*cs) ))) { + close( fd ); + goto bust; + } + cs->sock.fd = fd; + cs->sock.buffer = 0; + cs->sock.buflen = 0; + cs->next = cr->css; + cr->css = cs; + fcntl( fd, F_SETFL, fcntl( fd, F_GETFL ) | O_NONBLOCK ); + RegisterCloseOnFork( fd ); + RegisterInput( fd ); +} + +static void +nukeSock( struct cmdsock *cs ) +{ + UnregisterInput( cs->sock.fd ); + CloseNClearCloseOnFork( cs->sock.fd ); + if (cs->sock.buffer) + free( cs->sock.buffer ); + free( cs ); +} + + +static CtrlRec ctrl = { 0, 0, -1, 0, 0, { -1, 0, 0 } }; + +void +openCtrl( struct display *d ) +{ + CtrlRec *cr; + const char *dname; + char *sockdir; + struct sockaddr_un sa; + + if (!*fifoDir) + return; + if (d) { + cr = &d->ctrl, dname = d->name; + if (!memcmp( dname, "localhost:", 10 )) + dname += 9; + } else + cr = &ctrl, dname = 0; + if (cr->fifo.fd < 0) { + if (mkdir( fifoDir, 0755 )) { + if (errno != EEXIST) { + LogError( "mkdir %\"s failed; no control FiFos will be available\n", + fifoDir ); + return; + } + } else + chmod( fifoDir, 0755 ); /* override umask */ + StrApp( &cr->fpath, fifoDir, dname ? "/xdmctl-" : "/xdmctl", + dname, (char *)0 ); + if (cr->fpath) { + unlink( cr->fpath ); + if (mkfifo( cr->fpath, 0 ) < 0) + LogError( "Cannot create control FiFo %\"s\n", cr->fpath ); + else { + cr->gid = fifoGroup; + if (!d) + chown( cr->fpath, -1, fifoGroup ); + chmod( cr->fpath, 0620 ); + if ((cr->fifo.fd = open( cr->fpath, O_RDWR | O_NONBLOCK )) >= 0) { + RegisterCloseOnFork( cr->fifo.fd ); + RegisterInput( cr->fifo.fd ); + goto fifok; + } + unlink( cr->fpath ); + LogError( "Cannot open control FiFo %\"s\n", cr->fpath ); + } + free( cr->fpath ); + cr->fpath = 0; + } + } + fifok: + if (cr->fd < 0) { + /* fifoDir is created above already */ + sockdir = 0; + StrApp( &sockdir, fifoDir, dname ? "/dmctl-" : "/dmctl", + dname, (char *)0 ); + if (sockdir) { + StrApp( &cr->path, sockdir, "/socket", (char *)0 ); + if (cr->path) { + if (strlen( cr->path ) >= sizeof(sa.sun_path)) + LogError( "path %\"s too long; no control sockets will be available\n", + cr->path ); + else if (mkdir( sockdir, 0755 ) && errno != EEXIST) + LogError( "mkdir %\"s failed; no control sockets will be available\n", + sockdir ); + else { + if (!d) + chown( sockdir, -1, fifoGroup ); + chmod( sockdir, 0750 ); + if ((cr->fd = socket( PF_UNIX, SOCK_STREAM, 0 )) < 0) + LogError( "Cannot create control socket\n" ); + else { + unlink( cr->path ); + sa.sun_family = AF_UNIX; + strcpy( sa.sun_path, cr->path ); + if (!bind( cr->fd, (struct sockaddr *)&sa, sizeof(sa) )) { + if (!listen( cr->fd, 5 )) { + chmod( cr->path, 0666 ); + RegisterCloseOnFork( cr->fd ); + RegisterInput( cr->fd ); + free( sockdir ); + return; + } + unlink( cr->path ); + LogError( "Cannot listen on control socket %\"s\n", + cr->path ); + } else + LogError( "Cannot bind control socket %\"s\n", + cr->path ); + close( cr->fd ); + cr->fd = -1; + } + } + free( cr->path ); + cr->path = 0; + } + free( sockdir ); + } + } +} + +void +closeCtrl( struct display *d ) +{ + CtrlRec *cr = d ? &d->ctrl : &ctrl; + + if (cr->fd >= 0) { + UnregisterInput( cr->fd ); + CloseNClearCloseOnFork( cr->fd ); + cr->fd = -1; + unlink( cr->path ); + *strrchr( cr->path, '/' ) = 0; + rmdir( cr->path ); + free( cr->path ); + cr->path = 0; + while (cr->css) { + struct cmdsock *cs = cr->css; + cr->css = cs->next; + nukeSock( cs ); + } + } + if (cr->fifo.fd >= 0) { + UnregisterInput( cr->fifo.fd ); + CloseNClearCloseOnFork( cr->fifo.fd ); + cr->fifo.fd = -1; + unlink( cr->fpath ); + free( cr->fpath ); + cr->fpath = 0; + if (cr->fifo.buffer) + free( cr->fifo.buffer ); + cr->fifo.buffer = 0; + cr->fifo.buflen = 0; + } +} + +void +chownCtrl( CtrlRec *cr, int uid ) +{ + if (cr->fpath) + chown( cr->fpath, uid, -1 ); + if (cr->path) { + char *ptr = strrchr( cr->path, '/' ); + *ptr = 0; + chown( cr->path, uid, -1 ); + *ptr = '/'; + } +} + +void +updateCtrl( void ) +{ + unsigned ffl, slc; + + ffl = 0; + if (ctrl.path) + for (ffl = strlen( ctrl.path ), slc = 2; ;) + if (ctrl.path[--ffl] == '/') + if (!--slc) + break; + if (ffl != strlen( fifoDir ) || memcmp( fifoDir, ctrl.path, ffl ) || + ctrl.gid != fifoGroup) + { + closeCtrl( 0 ); + openCtrl( 0 ); + } +} + + +static void +fLog( struct display *d, int fd, const char *sts, const char *msg, ... ) +{ + char *fmsg, *otxt; + const char *what; + int olen; + va_list va; + + va_start( va, msg ); + VASPrintf( &fmsg, msg, va ); + va_end( va ); + if (!fmsg) + return; + if (fd >= 0) { + olen = ASPrintf( &otxt, "%s\t%\\s\n", sts, fmsg ); + if (otxt) { + Writer( fd, otxt, olen ); + free( otxt ); + } + what = "socket"; + } else + what = "FiFo"; + if (d) + Debug( "control %s for %s: %s - %s", what, d->name, sts, fmsg ); + else + Debug( "global control %s: %s - %s", what, sts, fmsg ); + free( fmsg ); +} + + +static char * +unQuote( const char *str ) +{ + char *ret, *adp; + + if (!(ret = Malloc( strlen( str ) + 1 ))) + return 0; + for (adp = ret; *str; str++, adp++) + if (*str == '\\') + switch (*++str) { + case 0: str--; /* fallthrough */ + case '\\': *adp = '\\'; break; + case 'n': *adp = '\n'; break; + case 't': *adp = '\t'; break; + default: *adp++ = '\\'; *adp = *str; break; + } + else + *adp = *str; + *adp = 0; + return ret; +} + +static void +str_cat_l( char **bp, const char *str, int max ) +{ + int dnl = StrNLen( str, max ); + memcpy( *bp, str, dnl ); + *bp += dnl; +} + +static void +str_cat( char **bp, const char *str ) +{ + int dnl = strlen( str ); + memcpy( *bp, str, dnl ); + *bp += dnl; +} + +static void +sd_cat( char **bp, SdRec *sdr ) +{ + if (sdr->how == SHUT_HALT) + str_cat( bp, "halt," ); + else + str_cat( bp, "reboot," ); + if (sdr->start == TO_INF) + str_cat( bp, "0," ); + else + *bp += sprintf( *bp, "%d,", sdr->start ); + if (sdr->timeout == TO_INF) + str_cat( bp, "-1," ); + else + *bp += sprintf( *bp, "%d,", sdr->timeout ); + if (sdr->force == SHUT_ASK) + str_cat( bp, "ask" ); + else if (sdr->force == SHUT_FORCE) + str_cat( bp, "force" ); + else if (sdr->force == SHUT_FORCEMY) + str_cat( bp, "forcemy" ); + else + str_cat( bp, "cancel" ); + *bp += sprintf( *bp, ",%d,%s", sdr->uid, sdr->osname ? sdr->osname : "-" ); +} + +static void +emitXSessC( struct display *di, struct display *d, void *ctx ) +{ + char *dname, *bp; + char cbuf[1024]; + + bp = cbuf; + *bp++ = '\t'; + dname = di->name; + if (!memcmp( dname, "localhost:", 10 )) + dname += 9; + str_cat_l( &bp, dname, sizeof(cbuf)/2 ); + *bp++ = ','; +#ifdef HAVE_VTS + if (di->serverVT) + bp += sprintf( bp, "vt%d", di->serverVT ); +#endif + *bp++ = ','; +#ifdef XDMCP + if (di->status == remoteLogin) { + *bp++ = ','; + str_cat_l( &bp, di->remoteHost, sizeof(cbuf)/3 ); + } else +#endif + { + if (di->userName) + str_cat_l( &bp, di->userName, sizeof(cbuf)/5 ); + *bp++ = ','; + if (di->sessName) + str_cat_l( &bp, di->sessName, sizeof(cbuf)/5 ); + } + *bp++ = ','; + if (di == d) + *bp++ = '*'; + if (di->userSess >= 0 && + (d ? (d->userSess != di->userSess && + (d->allowNuke == SHUT_NONE || + (d->allowNuke == SHUT_ROOT && d->userSess))) : + !fifoAllowNuke)) + *bp++ = '!'; + Writer( (int)ctx, cbuf, bp - cbuf ); +} + +static void +emitTTYSessC( STRUCTUTMP *ut, struct display *d, void *ctx ) +{ + struct passwd *pw; + char *bp; + int vt, l; + char cbuf[sizeof(ut->ut_line) + sizeof(ut->ut_user) + sizeof(ut->ut_host) + 16]; + char user[sizeof(ut->ut_user) + 1]; + +#ifndef BSD_UTMP + if (ut->ut_type != USER_PROCESS) + l = 0; + else +#endif + { + l = StrNLen( ut->ut_user, sizeof(ut->ut_user) ); + memcpy( user, ut->ut_user, l ); + } + user[l] = 0; + bp = cbuf; + *bp++ = '\t'; + str_cat_l( &bp, ut->ut_line, sizeof(ut->ut_line) ); + *bp++ = ','; + if (*ut->ut_host) { + *bp++ = '@'; + str_cat_l( &bp, ut->ut_host, sizeof(ut->ut_host) ); + } +#ifdef HAVE_VTS + else if ((vt = TTYtoVT( ut->ut_line ))) + bp += sprintf( bp, "vt%d", vt ); +#endif + *bp++ = ','; + str_cat( &bp, user ); + *bp++ = ','; + /* blank: session type unknown */ + *bp++ = ','; + /* blank: certainly not querying display */ + *bp++ = 't'; + if (*user && + (d ? ((d->allowNuke == SHUT_NONE || + (d->allowNuke == SHUT_ROOT && d->userSess)) && + (!(pw = getpwnam( user )) || d->userSess != (int)pw->pw_uid)) : + !fifoAllowNuke)) + *bp++ = '!'; + Writer( (int)ctx, cbuf, bp - cbuf ); +} + +static void +processCtrl( const char *string, int len, int fd, struct display *d ) +{ +#define Reply(t) Writer (fd, t, strlen (t)) + + struct display *di; + const char *word; + char **ar, **ap, *args, *bp; + SdRec sdr; + char cbuf[1024]; + + if (!(ar = initStrArr( 0 ))) + return; + for (word = string; ; string++, len--) + if (!len || *string == '\t') { + if (!(ar = addStrArr( ar, word, string - word ))) + return; + if (!len) + break; + word = string + 1; + } + word = fd >= 0 ? "socket" : "FiFo"; + if (d) + Debug( "control %s for %s received %'[s\n", word, d->name, ar ); + else + Debug( "global control %s received %'[s\n", word, ar ); + if (ar[0]) { + if (fd >= 0 && !strcmp( ar[0], "caps" )) { + if (ar[1]) + goto exce; + Reply( "ok\tkdm\tlist\t" ); + if (bootManager != BO_NONE) + Reply( "bootoptions\t" ); + if (d) { + if ((d->displayType & d_location) == dLocal) +#ifdef HAVE_VTS + Reply( "local\tactivate\t" ); +#else + Reply( "local\t" ); +#endif + if (d->allowShutdown != SHUT_NONE) { + if (d->allowShutdown == SHUT_ROOT && d->userSess) + Reply( "shutdown root\t" ); + else + Reply( "shutdown\t" ); + Reply( "shutdown ask\t" ); + if (d->allowNuke != SHUT_NONE) { + if (d->allowNuke == SHUT_ROOT && d->userSess) + Reply( "nuke root\t" ); + else + Reply( "nuke\t" ); + } + } + if ((d->displayType & d_location) == dLocal && + AnyReserveDisplays()) + Writer( fd, cbuf, sprintf( cbuf, "reserve %d\t", + idleReserveDisplays() ) ); + Reply( "lock\tsuicide\n" ); + } else { + if (fifoAllowShutdown) { + Reply( "shutdown\t" ); + if (fifoAllowNuke) + Reply( "nuke\t" ); + } + if (AnyReserveDisplays()) + Writer( fd, cbuf, sprintf( cbuf, "reserve %d\t", + idleReserveDisplays() ) ); +#ifdef HAVE_VTS + Reply( "login\tactivate\n" ); +#else + Reply( "login\n" ); +#endif + } + goto bust; + } else if (fd >= 0 && !strcmp( ar[0], "list" )) { + int flags = lstRemote | lstTTY; + if (ar[1]) { + if (!strcmp( ar[1], "all" )) + flags = lstRemote | lstPassive | lstTTY; + else if (!strcmp( ar[1], "alllocal" )) + flags = lstPassive | lstTTY; + else { + fLog( d, fd, "bad", "invalid list scope %\"s", ar[1] ); + goto bust; + } + if (ar[2]) + goto exce; + } + Reply( "ok" ); + ListSessions( flags, d, (void *)fd, emitXSessC, emitTTYSessC ); + Reply( "\n" ); + goto bust; + } else if (!strcmp( ar[0], "reserve" )) { + int lt = 60; /* XXX make default timeout configurable? */ + if (ar[1]) { + lt = strtol( ar[1], &bp, 10 ); + if (lt < 15 || *bp) { + fLog( d, fd, "bad", "invalid timeout %\"s", ar[1] ); + goto bust; + } + if (ar[2]) + goto exce; + } + if (d && (d->displayType & d_location) != dLocal) { + fLog( d, fd, "perm", "display is not local" ); + goto bust; + } + if (!StartReserveDisplay( lt )) { + fLog( d, fd, "noent", "no reserve display available" ); + goto bust; + } +#ifdef HAVE_VTS + } else if (!strcmp( ar[0], "activate" )) { + int vt; + if (!ar[1]) + goto miss; + if (ar[2]) + goto exce; + if (d && (d->displayType & d_location) != dLocal) { + fLog( d, fd, "perm", "display is not local" ); + goto bust; + } + if (ar[1][0] != 'v' || ar[1][1] != 't' || + (vt = atoi( ar[1] + 2 )) <= 0) + { + if (!(di = FindDisplayByName( ar[1] ))) { + fLog( d, fd, "noent", "display not found" ); + goto bust; + } + if ((di->displayType & d_location) != dLocal) { + fLog( d, fd, "inval", "target display is not local" ); + goto bust; + } + if (!di->serverVT) { + fLog( d, fd, "noent", "target display has no VT assigned" ); + goto bust; + } + vt = di->serverVT; + } + if (!activateVT( vt )) { + fLog( d, fd, "inval", "VT switch failed" ); + goto bust; + } +#endif + } else if (!strcmp( ar[0], "shutdown" )) { + ap = ar; + if (!*++ap) + goto miss; + sdr.force = SHUT_CANCEL; + sdr.osname = 0; + if (!strcmp( *ap, "status" )) { + if (fd < 0) + goto bust; + if (*++ap) + goto exce; + bp = cbuf; + *bp++ = 'o'; + *bp++ = 'k'; + if (sdRec.how) { + str_cat( &bp, "\tglobal," ); + sd_cat( &bp, &sdRec ); + } + if (d && d->hstent->sdRec.how) { + str_cat( &bp, "\tlocal," ); + sd_cat( &bp, &d->hstent->sdRec ); + } + *bp++ = '\n'; + Writer( fd, cbuf, bp - cbuf ); + goto bust; + } else if (!strcmp( *ap, "cancel" )) { + sdr.how = 0; + sdr.start = 0; + if (ap[1]) { + if (!d) + goto exce; + if (!strcmp( *++ap, "global" )) + sdr.start = TO_INF; + else if (strcmp( *ap, "local" )) { + fLog( d, fd, "bad", "invalid cancel scope %\"s", *ap ); + goto bust; + } + } + } else { + if (!strcmp( *ap, "reboot" )) + sdr.how = SHUT_REBOOT; + else if (!strcmp( *ap, "halt" )) + sdr.how = SHUT_HALT; + else { + fLog( d, fd, "bad", "invalid type %\"s", *ap ); + goto bust; + } + sdr.uid = -1; + if (!*++ap) + goto miss; + if (**ap == '=') { + switch (setBootOption( *ap + 1, &sdr )) { + case BO_NOMAN: + fLog( d, fd, "notsup", "boot options unavailable" ); + goto bust; + case BO_NOENT: + fLog( d, fd, "noent", "no such boot option" ); + goto bust; + case BO_IO: + fLog( d, fd, "io", "io error" ); + goto bust; + } + if (!*++ap) + goto miss; + } + sdr.start = strtol( *ap, &bp, 10 ); + if (bp != *ap && !*bp) { + if (**ap == '+') + sdr.start += now; + if (!*++ap) + goto miss; + sdr.timeout = strtol( *ap, &bp, 10 ); + if (bp == *ap || *bp) { + fLog( d, fd, "bad", "invalid timeout %\"s", ar[3] ); + goto bust; + } + if (**ap == '+') + sdr.timeout += sdr.start ? sdr.start : now; + if (sdr.timeout < 0) + sdr.timeout = TO_INF; + else { + if (!*++ap) + goto miss; + if (!strcmp( *ap, "force" )) + sdr.force = SHUT_FORCE; + else if (d && !strcmp( *ap, "forcemy" )) + sdr.force = SHUT_FORCEMY; + else if (strcmp( *ap, "cancel" )) { + fLog( d, fd, "bad", "invalid timeout action %\"s", + *ap ); + goto bust; + } + } + } else { + sdr.timeout = 0; + if (d && !strcmp( *ap, "ask" )) + sdr.force = SHUT_ASK; + else if (!strcmp( *ap, "forcenow" )) + sdr.force = SHUT_FORCE; + else if (!strcmp( *ap, "schedule" )) + sdr.timeout = TO_INF; + else if (strcmp( *ap, "trynow" )) { + fLog( d, fd, "bad", "invalid mode %\"s", *ap ); + goto bust; + } + } + } + if (*++ap) + goto exce; + if (d) { + sdr.uid = d->userSess >= 0 ? d->userSess : 0; + if (d->allowShutdown == SHUT_NONE || + (d->allowShutdown == SHUT_ROOT && sdr.uid && + sdr.force != SHUT_ASK)) + { + fLog( d, fd, "perm", "shutdown forbidden" ); + goto bust; + } + if (!sdr.how && !sdr.start) { + if (d->hstent->sdRec.osname) + free( d->hstent->sdRec.osname ); + d->hstent->sdRec = sdr; + } else { + if (sdRec.how && sdRec.force == SHUT_FORCE && + ((d->allowNuke == SHUT_NONE && sdRec.uid != sdr.uid) || + (d->allowNuke == SHUT_ROOT && sdr.uid))) + { + fLog( d, fd, "perm", "overriding forced shutdown forbidden" ); + goto bust; + } + if (sdr.force == SHUT_FORCE && + (d->allowNuke == SHUT_NONE || + (d->allowNuke == SHUT_ROOT && sdr.uid))) + { + fLog( d, fd, "perm", "forced shutdown forbidden" ); + goto bust; + } + if (!sdr.start) { + if (d->hstent->sdRec.osname) + free( d->hstent->sdRec.osname ); + d->hstent->sdRec = sdr; + } else { + if (!sdr.how) + cancelShutdown(); + else { + if (sdRec.osname) + free( sdRec.osname ); + sdRec = sdr; + } + } + } + } else { + if (!fifoAllowShutdown) { + fLog( d, fd, "perm", "shutdown forbidden" ); + goto bust; + } + if (sdRec.how && sdRec.force == SHUT_FORCE && + sdRec.uid != -1 && !fifoAllowNuke) + { + fLog( d, fd, "perm", "overriding forced shutdown forbidden" ); + goto bust; + } + if (!sdr.how) + cancelShutdown(); + else { + if (sdr.force != SHUT_CANCEL) { + if (!fifoAllowNuke) { + fLog( d, fd, "perm", "forced shutdown forbidden" ); + goto bust; + } + } else { + if (!sdr.start && !sdr.timeout && AnyActiveDisplays()) { + fLog( d, fd, "busy", "user sessions running" ); + goto bust; + } + } + sdr.uid = -1; + if (sdRec.osname) + free( sdRec.osname ); + sdRec = sdr; + } + } + } else if (fd >= 0 && !strcmp( ar[0], "listbootoptions" )) { + char **opts; + int def, cur, i, j; + + if (ar[1]) + goto exce; + switch (getBootOptions( &opts, &def, &cur )) { + case BO_NOMAN: + fLog( d, fd, "notsup", "boot options unavailable" ); + goto bust; + case BO_IO: + fLog( d, fd, "io", "io error" ); + goto bust; + } + Reply( "ok\t" ); + for (i = 0; opts[i]; i++) { + bp = cbuf; + if (i) + *bp++ = ' '; + for (j = 0; opts[i][j]; j++) + if (opts[i][j] == ' ') { + *bp++ = '\\'; + *bp++ = 's'; + } else + *bp++ = opts[i][j]; + Writer( fd, cbuf, bp - cbuf ); + } + freeStrArr( opts ); + Writer( fd, cbuf, sprintf( cbuf, "\t%d\t%d\n", def, cur ) ); + goto bust; + } else if (d) { + if (!strcmp( ar[0], "lock" )) { + if (ar[1]) + goto exce; + d->hstent->lock = 1; + } else if (!strcmp( ar[0], "unlock" )) { + if (ar[1]) + goto exce; + d->hstent->lock = 0; + } else if (!strcmp( ar[0], "suicide" )) { + if (ar[1]) + goto exce; + if (d->status == running && d->pid != -1) { + TerminateProcess( d->pid, SIGTERM ); + d->status = raiser; + } + } else { + fLog( d, fd, "nosys", "unknown command" ); + goto bust; + } + } else { + if (!strcmp( ar[0], "login" )) { + int nuke; + if (arrLen( ar ) < 5) { + miss: + fLog( d, fd, "bad", "missing argument(s)" ); + goto bust; + } + if (!(di = FindDisplayByName( ar[1] ))) { + fLog( d, fd, "noent", "display %s not found", ar[1] ); + goto bust; + } + if (ar[5]) { + if (!(args = unQuote( ar[5] ))) { + fLog( d, fd, "nomem", "out of memory" ); + goto bust; + } + if (ar[6]) { + free( args ); + exce: + fLog( d, fd, "bad", "excess argument(s)" ); + goto bust; + } + setNLogin( di, ar[3], ar[4], args, 2 ); + free( args ); + } else + setNLogin( di, ar[3], ar[4], 0, 2 ); + nuke = !strcmp( ar[2], "now" ); + switch (di->status) { + case running: + if (di->pid != -1 && (di->userSess < 0 || nuke)) { + TerminateProcess( di->pid, SIGTERM ); + di->status = raiser; + } + break; + case remoteLogin: + if (di->serverPid != -1 && nuke) + TerminateProcess( di->serverPid, di->termSignal ); + break; + case reserve: + di->status = notRunning; + break; + case textMode: +#ifndef HAVE_VTS + SwitchToX( di ); +#endif + break; + default: + break; + } + } else { + fLog( d, fd, "nosys", "unknown command" ); + goto bust; + } + } + if (fd >= 0) + Reply( "ok\n" ); + } + bust: + freeStrArr( ar ); +} + +static int +handleChan( struct display *d, struct bsock *cs, int fd, FD_TYPE *reads ) +{ + char *bufp, *nbuf, *obuf, *eol; + int len, bl, llen; + char buf[1024]; + + bl = cs->buflen; + obuf = cs->buffer; + if (bl <= 0 && FD_ISSET( cs->fd, reads )) { + FD_CLR( cs->fd, reads ); + bl = -bl; + memcpy( buf, obuf, bl ); + if ((len = Reader( cs->fd, buf + bl, sizeof(buf) - bl )) <= 0) + return -1; + bl += len; + bufp = buf; + } else { + len = 0; + bufp = obuf; + } + if (bl > 0) { + if ((eol = memchr( bufp, '\n', bl ))) { + llen = eol - bufp + 1; + bl -= llen; + if (bl) { + if (!(nbuf = Malloc( bl ))) + return -1; + memcpy( nbuf, bufp + llen, bl ); + } else + nbuf = 0; + cs->buffer = nbuf; + cs->buflen = bl; + processCtrl( bufp, llen - 1, fd, d ); + if (obuf) + free( obuf ); + return 1; + } else if (!len) { + if (fd >= 0) + cs->buflen = -bl; + else + fLog( d, -1, "bad", "unterminated command" ); + } + } + return 0; +} + +int +handleCtrl( FD_TYPE *reads, struct display *d ) +{ + CtrlRec *cr = d ? &d->ctrl : &ctrl; + struct cmdsock *cs, **csp; + + if (cr->fifo.fd >= 0) { + switch (handleChan( d, &cr->fifo, -1, reads )) { + case -1: + if (cr->fifo.buffer) + free( cr->fifo.buffer ); + cr->fifo.buflen = 0; + break; + case 1: + return 1; + default: + break; + } + } + if (cr->fd >= 0 && FD_ISSET( cr->fd, reads )) + acceptSock( cr ); + else { + for (csp = &cr->css; (cs = *csp); ) { + switch (handleChan( d, &cs->sock, cs->sock.fd, reads )) { + case -1: + *csp = cs->next; + nukeSock( cs ); + continue; + case 1: + return 1; + default: + break; + } + csp = &cs->next; + } + } + return 0; +} diff --git a/kdm/backend/daemon.c b/kdm/backend/daemon.c new file mode 100644 index 000000000..79d9e47ff --- /dev/null +++ b/kdm/backend/daemon.c @@ -0,0 +1,78 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2000,2001,2003 Oswald Buddenhagen <[email protected]> + +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 + */ + +#include "dm.h" +#include "dm_error.h" + +void +BecomeDaemon( void ) +{ + int pfd[2]; + + /* + * fork so that the process goes into the background automatically. Also + * has a nice side effect of having the child process get inherited by + * init (pid 1). + * Create a pipe and block on it, so the parent knows when the child is + * done with detaching. This eliminates the possibility that the child + * might get killed when the init script that's running xdm exits. + */ + + if (pipe( pfd )) + pfd[0] = pfd[1] = -1; /* so what ...? */ + switch (fork ()) { + case 0: + /* child */ + break; + case -1: + /* error */ + LogError( "Daemon fork failed: %m\n" ); + break; + + default: + /* parent */ + close( pfd[1] ); + read( pfd[0], &pfd[1] /* dummy */, 1 ); + exit( 0 ); + } + + /* don't use daemon() - it doesn't buy us anything but an additional fork */ + + setsid(); + + close( pfd[0] ); + close( pfd[1] ); /* tell parent that we're done with detaching */ + + chdir( "/" ); +} diff --git a/kdm/backend/dm.c b/kdm/backend/dm.c new file mode 100644 index 000000000..e696a1a5e --- /dev/null +++ b/kdm/backend/dm.c @@ -0,0 +1,1652 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2000-2005 Oswald Buddenhagen <[email protected]> + +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 + * + * display manager + */ + +#include "dm.h" +#include "dm_auth.h" +#include "dm_error.h" + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdarg.h> +#include <signal.h> +#include <sys/stat.h> + +#ifdef HAVE_VTS +# include <sys/ioctl.h> +# include <sys/vt.h> +#endif + +static void SigHandler( int n ); +static int ScanConfigs( int force ); +static void StartDisplays( void ); +#define XS_KEEP 0 +#define XS_RESTART 1 +#define XS_RETRY 2 +static void ExitDisplay( struct display *d, int endState, int serverCmd, int goodExit ); +static void rStopDisplay( struct display *d, int endState ); +static void MainLoop( void ); + +static int signalFds[2]; + +#if !defined(HAVE_SETPROCTITLE) && !defined(NOXDMTITLE) +static char *Title; +static int TitleLen; +#endif + +static int StorePid( void ); + +static int Stopping; +SdRec sdRec = { 0, 0, 0, TO_INF, TO_INF, 0, 0, 0 }; + +time_t now; + +char *prog, *progpath; + +int +main( int argc, char **argv ) +{ + int oldpid, oldumask, fd, noDaemonMode; + char *pt, *errorLogFile, **opts; + + /* make sure at least world write access is disabled */ + if (((oldumask = umask( 022 )) & 002) == 002) + (void)umask( oldumask ); + + /* give /dev/null as stdin */ + if ((fd = open( "/dev/null", O_RDONLY )) > 0) { + dup2( fd, 0 ); + close( fd ); + } + if (fcntl( 1, F_GETFD ) < 0) + dup2( 0, 1 ); + if (fcntl( 2, F_GETFD ) < 0) + dup2( 0, 2 ); + + if (argv[0][0] == '/') { + if (!StrDup( &progpath, argv[0] )) + Panic( "Out of memory" ); + } else +#ifdef __linux__ + { + /* note that this will resolve symlinks ... */ + int len; + char fullpath[PATH_MAX]; + if ((len = readlink( "/proc/self/exe", fullpath, sizeof(fullpath) )) < 0) + Panic( "Invoke with full path specification or mount /proc" ); + if (!StrNDup( &progpath, fullpath, len )) + Panic( "Out of memory" ); + } +#else +# if 0 + Panic( "Must be invoked with full path specification" ); +# else + { + char directory[PATH_MAX+1]; + if (!getcwd( directory, sizeof(directory) )) + Panic( "Can't find myself (getcwd failed)" ); + if (strchr( argv[0], '/' )) + StrApp( &progpath, directory, "/", argv[0], (char *)0 ); + else { + int len; + char *path, *pathe, *name, *thenam, nambuf[PATH_MAX+1]; + + if (!(path = getenv( "PATH" ))) + Panic( "Can't find myself (no PATH)" ); + len = strlen( argv[0] ); + name = nambuf + PATH_MAX - len; + memcpy( name, argv[0], len + 1 ); + *--name = '/'; + do { + if (!(pathe = strchr( path, ':' ))) + pathe = path + strlen( path ); + len = pathe - path; + if (!len || (len == 1 && *path == '.')) { + len = strlen( directory ); + path = directory; + } + thenam = name - len; + if (thenam >= nambuf) { + memcpy( thenam, path, len ); + if (!access( thenam, X_OK )) + goto found; + } + path = pathe; + } while (*path++ != '\0'); + Panic( "Can't find myself (not in PATH)" ); + found: + if (!StrDup( &progpath, thenam )) + Panic( "Out of memory" ); + } + } +# endif +#endif + prog = strrchr( progpath, '/' ) + 1; + +#if !defined(HAVE_SETPROCTITLE) && !defined(NOXDMTITLE) + Title = argv[0]; + TitleLen = (argv[argc - 1] + strlen( argv[argc - 1] )) - Title; +#endif + + /* + * Parse command line options + */ + noDaemonMode = getppid(); + errorLogFile = 0; + if (!(opts = Malloc( 2 * sizeof(char *) ))) + return 1; + opts[0] = (char *)""; + opts[1] = 0; + while (*++argv) { + if (**argv != '-') + break; + pt = *argv + 1; + if (*pt == '-') + pt++; + if (!strcmp( pt, "help" ) || !strcmp( pt, "h" )) { + printf( "Usage: %s [options] [tty]\n" +" -daemon\t - Daemonize even when started by init\n" +" -nodaemon\t - Don't daemonize even when started from command line\n" +" -config <file> - Use alternative master configuration file\n" +" -xrm <res>\t - Override frontend-specific resource\n" +" -error <file>\t - Use alternative log file\n" +" -debug <num>\t - Debug option bitfield:\n" +"\t\t\t0x1 - core log\n" +"\t\t\t0x2 - config reader log\n" +"\t\t\t0x4 - greeter log\n" +"\t\t\t0x8 - IPC log\n" +"\t\t\t0x10 - session sub-daemon post-fork delay\n" +"\t\t\t0x20 - config reader post-start delay\n" +"\t\t\t0x40 - greeter post-start delay\n" +"\t\t\t0x80 - don't use syslog\n" +"\t\t\t0x100 - core Xauth log\n" +"\t\t\t0x400 - valgrind config reader and greeter\n" +"\t\t\t0x800 - strace config reader and greeter\n" + , prog ); + exit( 0 ); + } else if (!strcmp( pt, "daemon" )) + noDaemonMode = 0; + else if (!strcmp( pt, "nodaemon" )) + noDaemonMode = 1; + else if (argv[1] && !strcmp( pt, "config" )) + StrDup( opts, *++argv ); + else if (argv[1] && !strcmp( pt, "xrm" )) + opts = addStrArr( opts, *++argv, -1 ); + else if (argv[1] && !strcmp( pt, "debug" )) + sscanf( *++argv, "%i", &debugLevel ); + else if (argv[1] && (!strcmp( pt, "error" ) || !strcmp( pt, "logfile" ))) + errorLogFile = *++argv; + else { + fprintf( stderr, "\"%s\" is an unknown option or is missing a parameter\n", *argv ); + exit( 1 ); + } + } + + /* + * Only allow root to run in non-debug mode to avoid problems + */ + if (!debugLevel && getuid()) { + fprintf( stderr, "Only root wants to run %s\n", prog ); + exit( 1 ); + } + + InitErrorLog( errorLogFile ); + + if (noDaemonMode != 1) + BecomeDaemon(); + + /* + * Step 1 - load configuration parameters + */ + if (!InitResources( opts ) || ScanConfigs( FALSE ) < 0) + LogPanic( "Config reader failed. Aborting ...\n" ); + + /* SUPPRESS 560 */ + if ((oldpid = StorePid())) { + if (oldpid == -1) + LogError( "Can't create/lock pid file %s\n", pidFile ); + else + LogError( "Can't lock pid file %s, another xdm is running (pid %d)\n", + pidFile, oldpid ); + exit( 1 ); + } + +#ifdef NEED_ENTROPY + AddOtherEntropy(); +#endif + + /* + * We used to clean up old authorization files here. As authDir is + * supposed to be /var/run/xauth or /tmp, we needn't to care for it. + */ + +#ifdef XDMCP + init_session_id(); +#else + Debug( "not compiled for XDMCP\n" ); +#endif + if (pipe( signalFds )) + LogPanic( "Unable to create signal notification pipe.\n" ); + RegisterInput( signalFds[0] ); + RegisterCloseOnFork( signalFds[0] ); + RegisterCloseOnFork( signalFds[1] ); + (void)Signal( SIGTERM, SigHandler ); + (void)Signal( SIGINT, SigHandler ); + (void)Signal( SIGHUP, SigHandler ); + (void)Signal( SIGCHLD, SigHandler ); + (void)Signal( SIGUSR1, SigHandler ); + + /* + * Step 2 - run a sub-daemon for each entry + */ +#ifdef XDMCP + UpdateListenSockets(); +#endif + openCtrl( 0 ); + MainLoop(); + closeCtrl( 0 ); + if (sdRec.how) { + commitBootOption(); + if (Fork() <= 0) { + char *cmd = sdRec.how == SHUT_HALT ? cmdHalt : cmdReboot; + execute( parseArgs( (char **)0, cmd ), (char **)0 ); + LogError( "Failed to execute shutdown command %\"s\n", cmd ); + exit( 1 ); + } else { + sigset_t mask; + sigemptyset( &mask ); + sigaddset( &mask, SIGCHLD ); + sigaddset( &mask, SIGHUP ); + sigsuspend( &mask ); + } + } + Debug( "nothing left to do, exiting\n" ); + return 0; +} + + +#ifdef HAVE_VTS +int +TTYtoVT( const char *tty ) +{ + return memcmp( tty, "tty", 3 ) ? 0 : atoi( tty + 3 ); +} + +int +activateVT( int vt ) +{ + int ret = 0; + int con = open( "/dev/console", O_RDONLY ); + if (con >= 0) { + if (!ioctl( con, VT_ACTIVATE, vt )) + ret = 1; + close( con ); + } + return ret; +} + + +static void +WakeDisplay( struct display *d ) +{ + if (d->status == textMode) + d->status = (d->displayType & d_lifetime) == dReserve ? reserve : notRunning; +} +#endif + +enum utState { UtDead, UtWait, UtActive }; + +struct utmps { + struct utmps *next; +#ifndef HAVE_VTS + struct display *d; +#endif + time_t time; + enum utState state; + int hadSess; +}; + +#define TIME_LOG 40 +#define TIME_RELOG 10 + +static struct utmps *utmpList; +static time_t utmpTimeout = TO_INF; + +static void +bombUtmp( void ) +{ + struct utmps *utp; + + while ((utp = utmpList)) { +#ifdef HAVE_VTS + ForEachDisplay( WakeDisplay ); +#else + utp->d->status = notRunning; +#endif + utmpList = utp->next; + free( utp ); + } +} + +static void +CheckUtmp( void ) +{ + static time_t modtim; + time_t nck; + time_t ends; + struct utmps *utp, **utpp; + struct stat st; +#ifdef BSD_UTMP + int fd; + struct utmp ut[1]; +#else + STRUCTUTMP *ut; +#endif + + if (!utmpList) + return; + if (stat( UTMP_FILE, &st )) { + LogError( UTMP_FILE " not found - cannot use console mode\n" ); + bombUtmp(); + return; + } + if (modtim != st.st_mtime) { + Debug( "rescanning " UTMP_FILE "\n" ); + for (utp = utmpList; utp; utp = utp->next) + utp->state = UtDead; +#ifdef BSD_UTMP + if ((fd = open( UTMP_FILE, O_RDONLY )) < 0) { + LogError( "Cannot open " UTMP_FILE " - cannot use console mode\n" ); + bombUtmp(); + return; + } + while (Reader( fd, ut, sizeof(ut[0]) ) == sizeof(ut[0])) +#else + SETUTENT(); + while ((ut = GETUTENT())) +#endif + { + for (utp = utmpList; utp; utp = utp->next) { +#ifdef HAVE_VTS + char **line; + for (line = consoleTTYs; *line; line++) + if (!strncmp( *line, ut->ut_line, sizeof(ut->ut_line) )) + goto hitlin; + continue; + hitlin: +#else + if (strncmp( utp->d->console, ut->ut_line, sizeof(ut->ut_line) )) + continue; +#endif +#ifdef BSD_UTMP + if (!*ut->ut_user) { +#else + if (ut->ut_type != USER_PROCESS) { +#endif +#ifdef HAVE_VTS + if (utp->state == UtActive) + break; +#endif + utp->state = UtWait; + } else { + utp->hadSess = 1; + utp->state = UtActive; + } + if (utp->time < ut->ut_time) /* theoretically superfluous */ + utp->time = ut->ut_time; + break; + } + } +#ifdef BSD_UTMP + close( fd ); +#else + ENDUTENT(); +#endif + modtim = st.st_mtime; + } + for (utpp = &utmpList; (utp = *utpp); ) { + if (utp->state != UtActive) { + if (utp->state == UtDead) /* shouldn't happen ... */ + utp->time = 0; + ends = utp->time + (utp->hadSess ? TIME_RELOG : TIME_LOG); + if (ends <= now) { +#ifdef HAVE_VTS + ForEachDisplay( WakeDisplay ); + Debug( "console login timed out\n" ); +#else + utp->d->status = notRunning; + Debug( "console login for %s at %s timed out\n", + utp->d->name, utp->d->console ); +#endif + *utpp = utp->next; + free( utp ); + continue; + } else + nck = ends; + } else + nck = TIME_RELOG + now; + if (nck < utmpTimeout) + utmpTimeout = nck; + utpp = &(*utpp)->next; + } +} + +static void +#ifdef HAVE_VTS +SwitchToTty( void ) +#else +SwitchToTty( struct display *d ) +#endif +{ + struct utmps *utp; +#ifdef HAVE_VTS + int vt; +#endif + + if (!(utp = Malloc( sizeof(*utp) ))) { +#ifdef HAVE_VTS + ForEachDisplay( WakeDisplay ); +#else + d->status = notRunning; +#endif + return; + } +#ifndef HAVE_VTS + d->status = textMode; + utp->d = d; +#endif + utp->time = now; + utp->hadSess = 0; + utp->next = utmpList; + utmpList = utp; + CheckUtmp(); + +#ifdef HAVE_VTS + if ((vt = TTYtoVT( *consoleTTYs ))) + activateVT( vt ); +#endif + + /* XXX output something useful here */ +} + +#ifdef HAVE_VTS +static void +StopToTTY( struct display *d ) +{ + if ((d->displayType & d_location) == dLocal) + switch (d->status) { + default: + rStopDisplay( d, DS_TEXTMODE | 0x100 ); + case reserve: + case textMode: + break; + } +} + +static void +CheckTTYMode( void ) +{ + struct display *d; + + for (d = displays; d; d = d->next) + if (d->status == zombie) + return; + + SwitchToTty(); +} + +#else + +void +SwitchToX( struct display *d ) +{ + struct utmps *utp, **utpp; + + for (utpp = &utmpList; (utp = *utpp); utpp = &(*utpp)->next) + if (utp->d == d) { + *utpp = utp->next; + free( utp ); + d->status = notRunning; + return; + } +} +#endif + +#ifdef XDMCP +static void +StartRemoteLogin( struct display *d ) +{ + char **argv; + int pid; + + Debug( "StartRemoteLogin for %s\n", d->name ); + /* HACK: omitting LoadDisplayResources( d ) here! */ + switch (pid = Fork()) { + case 0: + argv = PrepServerArgv( d, d->serverArgsRemote ); + if (!(argv = addStrArr( argv, "-once", 5 )) || + !(argv = addStrArr( argv, "-query", 6 )) || + !(argv = addStrArr( argv, d->remoteHost, -1 ))) + exit( 1 ); + Debug( "exec %\"[s\n", argv ); + (void)execv( argv[0], argv ); + LogError( "X server %\"s cannot be executed\n", argv[0] ); + exit( 1 ); + case -1: + LogError( "Forking X server for remote login failed: %m" ); + d->status = notRunning; + return; + default: + break; + } + Debug( "X server forked, pid %d\n", pid ); + d->serverPid = pid; + + d->status = remoteLogin; +} +#endif + + +static void +StopInactiveDisplay( struct display *d ) +{ + if (d->status != remoteLogin && d->userSess < 0) + StopDisplay( d ); +} + +static void +stoppen( int force ) +{ +#ifdef XDMCP + request_port = 0; + UpdateListenSockets(); +#endif + if (force) + ForEachDisplay( StopDisplay ); + else + ForEachDisplay( StopInactiveDisplay ); + Stopping = 1; +} + + +void +setNLogin( struct display *d, + const char *nuser, const char *npass, char *nargs, int rl ) +{ + struct disphist *he = d->hstent; + he->rLogin = + (ReStr( &he->nuser, nuser ) && + ReStr( &he->npass, npass ) && + ReStr( &he->nargs, nargs )) ? rl : 0; + Debug( "set next login for %s, level %d\n", nuser, rl ); +} + +static void +processDPipe( struct display *d ) +{ + char *user, *pass, *args; + int cmd; + GTalk dpytalk; +#ifdef XDMCP + int ct, len; + ARRAY8 ca, ha; +#endif + + dpytalk.pipe = &d->pipe; + if (Setjmp( dpytalk.errjmp )) { + StopDisplay( d ); + return; + } + GSet( &dpytalk ); + if (!GRecvCmd( &cmd )) { + /* process already exited */ + UnregisterInput( d->pipe.rfd ); + return; + } + switch (cmd) { + case D_User: + d->userSess = GRecvInt(); + d->userName = GRecvStr(); + d->sessName = GRecvStr(); + break; + case D_ReLogin: + user = GRecvStr(); + pass = GRecvStr(); + args = GRecvStr(); + setNLogin( d, user, pass, args, 1 ); + free( args ); + free( pass ); + free( user ); + break; +#ifdef XDMCP + case D_ChooseHost: + ca.data = (unsigned char *)GRecvArr( &len ); + ca.length = (CARD16)len; + ct = GRecvInt(); + ha.data = (unsigned char *)GRecvArr( &len ); + ha.length = (CARD16)len; + RegisterIndirectChoice( &ca, ct, &ha ); + XdmcpDisposeARRAY8( &ha ); + XdmcpDisposeARRAY8( &ca ); + break; + case D_RemoteHost: + if (d->remoteHost) + free( d->remoteHost ); + d->remoteHost = GRecvStr(); + break; +#endif + case D_XConnOk: + startingServer = 0; + break; + default: + LogError( "Internal error: unknown D_* command %d\n", cmd ); + StopDisplay( d ); + break; + } +} + +static void +emitXSessG( struct display *di, struct display *d, void *ctx ATTR_UNUSED ) +{ + GSendStr( di->name ); + GSendStr( "" ); +#ifdef HAVE_VTS + GSendInt( di->serverVT ); +#endif +#ifdef XDMCP + if (di->status == remoteLogin) { + GSendStr( "" ); + GSendStr( di->remoteHost ); + } else +#endif + { + GSendStr( di->userName ); + GSendStr( di->sessName ); + } + GSendInt( di == d ? isSelf : 0 ); +} + +static void +emitTTYSessG( STRUCTUTMP *ut, struct display *d ATTR_UNUSED, void *ctx ATTR_UNUSED ) +{ + GSendStrN( ut->ut_line, sizeof(ut->ut_line) ); + GSendStrN( ut->ut_host, sizeof(ut->ut_host) ); +#ifdef HAVE_VTS + GSendInt( TTYtoVT( ut->ut_line ) ); +#endif +#ifdef BSD_UTMP + GSendStrN( *ut->ut_user ? ut->ut_user : 0, sizeof(ut->ut_user) ); +#else + GSendStrN( ut->ut_type == USER_PROCESS ? ut->ut_user : 0, sizeof(ut->ut_user) ); +#endif + GSendStr( 0 ); /* session type unknown */ + GSendInt( isTTY ); +} + +static void +processGPipe( struct display *d ) +{ + char **opts, *option; + int cmd, ret, dflt, curr; + GTalk dpytalk; + + dpytalk.pipe = &d->gpipe; + if (Setjmp( dpytalk.errjmp )) { + StopDisplay( d ); + return; + } + GSet( &dpytalk ); + if (!GRecvCmd( &cmd )) { + /* process already exited */ + UnregisterInput( d->gpipe.rfd ); + return; + } + switch (cmd) { + case G_ListBootOpts: + ret = getBootOptions( &opts, &dflt, &curr ); + GSendInt( ret ); + if (ret == BO_OK) { + GSendArgv( opts ); + freeStrArr( opts ); + GSendInt( dflt ); + GSendInt( curr ); + } + break; + case G_Shutdown: + sdRec.how = GRecvInt(); + sdRec.start = GRecvInt(); + sdRec.timeout = GRecvInt(); + sdRec.force = GRecvInt(); + sdRec.uid = GRecvInt(); + option = GRecvStr(); + setBootOption( option, &sdRec ); + if (option) + free( option ); + break; + case G_QueryShutdown: + GSendInt( sdRec.how ); + GSendInt( sdRec.start ); + GSendInt( sdRec.timeout ); + GSendInt( sdRec.force ); + GSendInt( sdRec.uid ); + GSendStr( sdRec.osname ); + break; + case G_List: + ListSessions( GRecvInt(), d, 0, emitXSessG, emitTTYSessG ); + GSendInt( 0 ); + break; +#ifdef HAVE_VTS + case G_Activate: + activateVT( GRecvInt() ); + break; +#endif + case G_Console: +#ifdef HAVE_VTS + if (*consoleTTYs) { /* sanity check against greeter */ + ForEachDisplay( StopToTTY ); + CheckTTYMode(); + } +#else + if (*d->console) /* sanity check against greeter */ + rStopDisplay( d, DS_TEXTMODE ); +#endif + break; + default: + LogError( "Internal error: unknown G_* command %d\n", cmd ); + StopDisplay( d ); + break; + } +} + + +static int +ScanConfigs( int force ) +{ + int ret; + + if ((ret = LoadDMResources( force )) <= 0) + return ret; + ScanServers(); +#ifdef XDMCP + ScanAccessDatabase( force ); +#endif + return 1; +} + +static void +MarkDisplay( struct display *d ) +{ + d->stillThere = 0; +} + +static void +RescanConfigs( int force ) +{ + if (ScanConfigs( force ) > 0) { +#ifdef XDMCP + UpdateListenSockets(); +#endif + updateCtrl(); + } +} + +void +cancelShutdown( void ) +{ + sdRec.how = 0; + if (sdRec.osname) { + free( sdRec.osname ); + sdRec.osname = 0; + } + Stopping = 0; + RescanConfigs( TRUE ); +} + + +static void +ReapChildren( void ) +{ + int pid; + struct display *d; + waitType status; + + while ((pid = waitpid( -1, &status, WNOHANG )) > 0) + { + Debug( "manager wait returns pid %d sig %d core %d code %d\n", + pid, waitSig( status ), waitCore( status ), waitCode( status ) ); + /* SUPPRESS 560 */ + if ((d = FindDisplayByPid( pid ))) { + d->pid = -1; + UnregisterInput( d->pipe.rfd ); + GClosen (&d->pipe); + UnregisterInput( d->gpipe.rfd ); + GClosen (&d->gpipe); + closeCtrl( d ); + switch (waitVal( status )) { +#ifdef XDMCP + case EX_REMOTE: + Debug( "display exited with EX_REMOTE\n" ); + ExitDisplay( d, DS_REMOTE, 0, 0 ); + break; +#endif + case EX_NORMAL: + /* (any type of) session ended */ + Debug( "display exited with EX_NORMAL\n" ); + if ((d->displayType & d_lifetime) == dReserve) + ExitDisplay( d, DS_RESERVE, 0, 0 ); + else + ExitDisplay( d, DS_RESTART, XS_KEEP, TRUE ); + break; +#if 0 + case EX_REMANAGE_DPY: + /* user session ended */ + Debug( "display exited with EX_REMANAGE_DPY\n" ); + ExitDisplay( d, DS_RESTART, XS_KEEP, TRUE ); + break; +#endif + case EX_OPENFAILED_DPY: + /* WaitForServer() failed */ + LogError( "Display %s cannot be opened\n", d->name ); +#ifdef XDMCP + /* + * no display connection was ever made, tell the + * terminal that the open attempt failed + */ + if ((d->displayType & d_origin) == dFromXDMCP) + SendFailed( d, "cannot open display" ); +#endif + ExitDisplay( d, DS_RESTART, XS_RETRY, FALSE ); + break; + case waitCompose( SIGTERM,0,0 ): + /* killed before/during WaitForServer() + - local Xserver died + - display stopped (is zombie) + - "login now" and "suicide" pipe commands (is raiser) + */ + Debug( "display exited on SIGTERM\n" ); + ExitDisplay( d, DS_RESTART, XS_RETRY, FALSE ); + break; + case EX_AL_RESERVER_DPY: + /* - killed after WaitForServer() + - Xserver dead after remote session exit + */ + Debug( "display exited with EX_AL_RESERVER_DPY\n" ); + ExitDisplay( d, DS_RESTART, XS_RESTART, FALSE ); + break; + case EX_RESERVER_DPY: + /* induced by greeter: + - could not secure display + - requested by user + */ + Debug( "display exited with EX_RESERVER_DPY\n" ); + ExitDisplay( d, DS_RESTART, XS_RESTART, TRUE ); + break; + case EX_UNMANAGE_DPY: + /* some fatal error */ + Debug( "display exited with EX_UNMANAGE_DPY\n" ); + ExitDisplay( d, DS_REMOVE, 0, 0 ); + break; + default: + /* prolly crash */ + LogError( "Unknown session exit code %d (sig %d) from manager process\n", + waitCode( status ), waitSig( status ) ); + ExitDisplay( d, DS_REMOVE, 0, 0 ); + break; + } + } else if ((d = FindDisplayByServerPid( pid ))) { + d->serverPid = -1; + switch (d->status) { + case zombie: + Debug( "zombie X server for display %s reaped\n", d->name ); +#ifdef HAVE_VTS + if (d->serverVT && d->zstatus != DS_REMOTE) { + if (d->follower) { + d->follower->serverVT = d->serverVT; + d->follower = 0; + } else { + int con = open( "/dev/console", O_RDONLY ); + if (con >= 0) { + struct vt_stat vtstat; + ioctl( con, VT_GETSTATE, &vtstat ); + if (vtstat.v_active == d->serverVT) { + int vt = 1; + struct display *di; + for (di = displays; di; di = di->next) + if (di != d && di->serverVT) + vt = di->serverVT; + for (di = displays; di; di = di->next) + if (di != d && di->serverVT && + (di->userSess >= 0 || + di->status == remoteLogin)) + vt = di->serverVT; + ioctl( con, VT_ACTIVATE, vt ); + } + ioctl( con, VT_DISALLOCATE, d->serverVT ); + close( con ); + } + } + d->serverVT = 0; + } +#endif + rStopDisplay( d, d->zstatus ); + break; + case phoenix: + Debug( "phoenix X server arises, restarting display %s\n", + d->name ); + d->status = notRunning; + break; + case remoteLogin: + Debug( "remote login X server for display %s exited\n", + d->name ); + d->status = ((d->displayType & d_lifetime) == dReserve) ? + reserve : notRunning; + break; + case raiser: + LogError( "X server for display %s terminated unexpectedly\n", + d->name ); + /* don't kill again */ + break; + case running: + if (startingServer == d && d->serverStatus != ignore) { + if (d->serverStatus == starting && waitCode( status ) != 47) + LogError( "X server died during startup\n" ); + StartServerFailed(); + break; + } + LogError( "X server for display %s terminated unexpectedly\n", + d->name ); + if (d->pid != -1) { + Debug( "terminating session pid %d\n", d->pid ); + TerminateProcess( d->pid, SIGTERM ); + } + break; + case notRunning: + case textMode: + case reserve: + /* this cannot happen */ + Debug( "X server exited for passive (%d) session on display %s\n", + (int)d->status, d->name ); + break; + } + } else + Debug( "unknown child termination\n" ); + } +#ifdef NEED_ENTROPY + AddOtherEntropy(); +#endif +} + +static int +wouldShutdown( void ) +{ + struct display *d; + + if (sdRec.force != SHUT_CANCEL) { + if (sdRec.force == SHUT_FORCEMY) + for (d = displays; d; d = d->next) + if (d->status == remoteLogin || + (d->userSess >= 0 && d->userSess != sdRec.uid)) + return 0; + return 1; + } + return !AnyActiveDisplays(); +} + +FD_TYPE WellKnownSocketsMask; +int WellKnownSocketsMax; +int WellKnownSocketsCount; + +void +RegisterInput( int fd ) +{ + /* can be omited, as it is always called right after opening a socket + if (!FD_ISSET (fd, &WellKnownSocketsMask)) + */ + { + FD_SET( fd, &WellKnownSocketsMask ); + if (fd > WellKnownSocketsMax) + WellKnownSocketsMax = fd; + WellKnownSocketsCount++; + } +} + +void +UnregisterInput( int fd ) +{ + /* the check _is_ necessary, as some handles are unregistered before + the regular close sequence. + */ + if (FD_ISSET( fd, &WellKnownSocketsMask )) { + FD_CLR( fd, &WellKnownSocketsMask ); + WellKnownSocketsCount--; + } +} + +static void +SigHandler( int n ) +{ + int olderrno = errno; + char buf = (char)n; + /* Debug( "caught signal %d\n", n ); this hangs in syslog() */ + write( signalFds[1], &buf, 1 ); +#ifdef __EMX__ + (void)Signal( n, SigHandler ); +#endif + errno = olderrno; +} + +static void +MainLoop( void ) +{ + struct display *d; + struct timeval *tvp, tv; + time_t to; + int nready; + char buf; + FD_TYPE reads; + + Debug( "MainLoop\n" ); + time( &now ); + while ( +#ifdef XDMCP + AnyListenSockets() || +#endif + (Stopping ? AnyRunningDisplays() : AnyDisplaysLeft())) + { + if (!Stopping) + StartDisplays(); + to = TO_INF; + if (sdRec.how) { + if (sdRec.start != TO_INF && now < sdRec.start) { + /*if (sdRec.start < to)*/ + to = sdRec.start; + } else { + sdRec.start = TO_INF; + if (now >= sdRec.timeout) { + sdRec.timeout = TO_INF; + if (wouldShutdown()) + stoppen( TRUE ); + else + cancelShutdown(); + } else { + stoppen( FALSE ); + /*if (sdRec.timeout < to)*/ + to = sdRec.timeout; + } + } + } + if (serverTimeout < to) + to = serverTimeout; + if (utmpTimeout < to) + to = utmpTimeout; + if (to == TO_INF) + tvp = 0; + else { + to -= now; + if (to < 0) + to = 0; + tv.tv_sec = to; + tv.tv_usec = 0; + tvp = &tv; + } + reads = WellKnownSocketsMask; + nready = select( WellKnownSocketsMax + 1, &reads, 0, 0, tvp ); + Debug( "select returns %d\n", nready ); + time( &now ); +#ifdef NEED_ENTROPY + AddTimerEntropy(); +#endif + if (now >= serverTimeout) { + serverTimeout = TO_INF; + StartServerTimeout(); + } + if (now >= utmpTimeout) { + utmpTimeout = TO_INF; + CheckUtmp(); + } + if (nready > 0) { + /* + * we restart after the first handled fd, as + * a) it makes things simpler + * b) the probability that multiple fds trigger at once is + * ridiculously small. we handle it in the next iteration. + */ + /* XXX a cleaner solution would be a callback mechanism */ + if (FD_ISSET( signalFds[0], &reads )) { + if (read( signalFds[0], &buf, 1 ) != 1) + LogPanic( "Signal notification pipe broken.\n" ); + switch (buf) { + case SIGTERM: + case SIGINT: + Debug( "shutting down entire manager\n" ); + stoppen( TRUE ); + break; + case SIGHUP: + LogInfo( "Rescanning all config files\n" ); + ForEachDisplay( MarkDisplay ); + RescanConfigs( TRUE ); + break; + case SIGCHLD: + ReapChildren(); + if (!Stopping && autoRescan) + RescanConfigs( FALSE ); + break; + case SIGUSR1: + if (startingServer && + startingServer->serverStatus == starting) + StartServerSuccess(); + break; + } + continue; + } +#ifdef XDMCP + if (ProcessListenSockets( &reads )) + continue; +#endif /* XDMCP */ + if (handleCtrl( &reads, 0 )) + continue; + /* Must be last (because of the breaks)! */ + again: + for (d = displays; d; d = d->next) { + if (handleCtrl( &reads, d )) + goto again; + if (d->pipe.rfd >= 0 && FD_ISSET( d->pipe.rfd, &reads )) { + processDPipe( d ); + break; + } + if (d->gpipe.rfd >= 0 && FD_ISSET( d->gpipe.rfd, &reads )) { + processGPipe( d ); + break; + } + } + } + } +} + +static void +CheckDisplayStatus( struct display *d ) +{ + if ((d->displayType & d_origin) == dFromFile && !d->stillThere) + StopDisplay( d ); + else if ((d->displayType & d_lifetime) == dReserve && + d->status == running && d->userSess < 0 && !d->idleTimeout) + rStopDisplay( d, DS_RESERVE ); + else if (d->status == notRunning) + if (LoadDisplayResources( d ) < 0) { + LogError( "Unable to read configuration for display %s; " + "stopping it.\n", d->name ); + StopDisplay( d ); + return; + } +} + +static void +KickDisplay( struct display *d ) +{ + if (d->status == notRunning) + StartDisplay( d ); + if (d->serverStatus == awaiting && !startingServer) + StartServer( d ); +} + +#ifdef HAVE_VTS +static int active_vts; + +static int +GetBusyVTs( void ) +{ + struct vt_stat vtstat; + int con; + + if (active_vts == -1) { + vtstat.v_state = 0; + if ((con = open( "/dev/console", O_RDONLY )) >= 0) { + ioctl( con, VT_GETSTATE, &vtstat ); + close( con ); + } + active_vts = vtstat.v_state; + } + return active_vts; +} + +static void +AllocateVT( struct display *d ) +{ + struct display *cd; + int i, tvt, volun; + + if ((d->displayType & d_location) == dLocal && + d->status == notRunning && !d->serverVT && d->reqSrvVT >= 0) + { + if (d->reqSrvVT && d->reqSrvVT < 16) + d->serverVT = d->reqSrvVT; + else { + for (i = tvt = 0;;) { + if (serverVTs[i]) { + tvt = atoi( serverVTs[i++] ); + volun = 0; + if (tvt < 0) { + tvt = -tvt; + volun = 1; + } + if (!tvt || tvt >= 16) + continue; + } else { + if (++tvt >= 16) + break; + volun = 1; + } + for (cd = displays; cd; cd = cd->next) { + if (cd->reqSrvVT == tvt && /* protect from lusers */ + (cd->status != zombie || cd->zstatus != DS_REMOVE)) + goto next; + if (cd->serverVT == tvt) { + if (cd->status != zombie || cd->zstatus == DS_REMOTE) + goto next; + if (!cd->follower) { + d->serverVT = -1; + cd->follower = d; + return; + } + } + } + if (!volun || !((1 << tvt) & GetBusyVTs())) { + d->serverVT = tvt; + return; + } + next: ; + } + } + } +} +#endif + +static void +StartDisplays( void ) +{ + ForEachDisplay( CheckDisplayStatus ); + CloseGetter(); +#ifdef HAVE_VTS + active_vts = -1; + ForEachDisplayRev( AllocateVT ); +#endif + ForEachDisplay( KickDisplay ); +} + +void +StartDisplay( struct display *d ) +{ + if (Stopping) { + Debug( "stopping display %s because shutdown is scheduled\n", d->name ); + StopDisplay( d ); + return; + } + +#ifdef HAVE_VTS + if (d->serverVT < 0) + return; +#endif + + d->status = running; + if ((d->displayType & d_location) == dLocal) { + Debug( "StartDisplay %s\n", d->name ); + /* don't bother pinging local displays; we'll + * certainly notice when they exit + */ + d->pingInterval = 0; + if (d->authorize) { + SetLocalAuthorization( d ); + /* + * reset the server after writing the authorization information + * to make it read the file (for compatibility with old + * servers which read auth file only on reset instead of + * at first connection) + */ + if (d->serverPid != -1 && d->resetForAuth && d->resetSignal) + kill( d->serverPid, d->resetSignal ); + } + if (d->serverPid == -1) { + d->serverStatus = awaiting; + return; + } + } else { + Debug( "StartDisplay %s, try %d\n", d->name, d->startTries + 1 ); + /* this will only happen when using XDMCP */ + if (d->authorizations) + SaveServerAuthorizations( d, d->authorizations, d->authNum ); + } + StartDisplayP2( d ); +} + +void +StartDisplayP2( struct display *d ) +{ + char *cname, *cgname; + int pid; + + openCtrl( d ); + Debug( "forking session\n" ); + ASPrintf( &cname, "sub-daemon for display %s", d->name ); + ASPrintf( &cgname, "greeter for display %s", d->name ); + pid = GFork( &d->pipe, "master daemon", cname, + &d->gpipe, cgname ); + switch (pid) { + case 0: + SetTitle( d->name ); + if (debugLevel & DEBUG_WSESS) + sleep( 100 ); + mstrtalk.pipe = &d->pipe; + (void)Signal( SIGPIPE, SIG_IGN ); + SetAuthorization( d ); + WaitForServer( d ); + if ((d->displayType & d_location) == dLocal) { + GSet( &mstrtalk ); + GSendInt( D_XConnOk ); + } + ManageSession( d ); + /* NOTREACHED */ + case -1: + closeCtrl( d ); + d->status = notRunning; + break; + default: + Debug( "forked session, pid %d\n", pid ); + + /* (void) fcntl (d->pipe.rfd, F_SETFL, O_NONBLOCK); */ + /* (void) fcntl (d->gpipe.rfd, F_SETFL, O_NONBLOCK); */ + RegisterInput( d->pipe.rfd ); + RegisterInput( d->gpipe.rfd ); + + d->pid = pid; + d->hstent->lock = d->hstent->rLogin = d->hstent->goodExit = + d->hstent->sdRec.how = 0; + d->lastStart = now; + break; + } +} + +/* + * transition from running to zombie, textmode, reserve or deleted + */ + +static void +rStopDisplay( struct display *d, int endState ) +{ + Debug( "stopping display %s to state %d\n", d->name, endState ); + AbortStartServer( d ); + d->idleTimeout = 0; + if (d->serverPid != -1 || d->pid != -1) { + if (d->pid != -1) + TerminateProcess( d->pid, SIGTERM ); + if (d->serverPid != -1) + TerminateProcess( d->serverPid, d->termSignal ); + d->status = zombie; + d->zstatus = endState & 0xff; + Debug( " zombiefied\n" ); + } else if (endState == DS_TEXTMODE) { +#ifdef HAVE_VTS + d->status = textMode; + CheckTTYMode(); + } else if (endState == (DS_TEXTMODE | 0x100)) { + d->status = textMode; +#else + SwitchToTty( d ); +#endif + } else if (endState == DS_RESERVE) + d->status = reserve; +#ifdef XDMCP + else if (endState == DS_REMOTE) + StartRemoteLogin( d ); +#endif + else { +#ifndef HAVE_VTS + SwitchToX( d ); +#endif + RemoveDisplay( d ); + } +} + +void +StopDisplay( struct display *d ) +{ + rStopDisplay( d, DS_REMOVE ); +} + +static void +ExitDisplay( + struct display *d, + int endState, + int serverCmd, + int goodExit ) +{ + struct disphist *he; + + if (d->status == raiser) { + serverCmd = XS_KEEP; + goodExit = TRUE; + } + + Debug( "ExitDisplay %s, " + "endState = %d, serverCmd = %d, GoodExit = %d\n", + d->name, endState, serverCmd, goodExit ); + + d->userSess = -1; + if (d->userName) + free( d->userName ); + d->userName = 0; + if (d->sessName) + free( d->sessName ); + d->sessName = 0; + he = d->hstent; + he->lastExit = now; + he->goodExit = goodExit; + if (he->sdRec.how) { + if (he->sdRec.force == SHUT_ASK && + (AnyActiveDisplays() || d->allowShutdown == SHUT_ROOT)) + { + endState = DS_RESTART; + } else { + if (!sdRec.how || sdRec.force != SHUT_FORCE || + !((d->allowNuke == SHUT_NONE && sdRec.uid != he->sdRec.uid) || + (d->allowNuke == SHUT_ROOT && he->sdRec.uid))) + { + if (sdRec.osname) + free( sdRec.osname ); + sdRec = he->sdRec; + if (now < sdRec.timeout || wouldShutdown()) + endState = DS_REMOVE; + } else if (he->sdRec.osname) + free( he->sdRec.osname ); + he->sdRec.how = 0; + he->sdRec.osname = 0; + } + } + if (d->status == zombie) + rStopDisplay( d, d->zstatus ); + else { + if (Stopping) { + StopDisplay( d ); + return; + } + if (endState != DS_RESTART || + (d->displayType & d_origin) != dFromFile) + { + rStopDisplay( d, endState ); + } else { + if (serverCmd == XS_RETRY) { + if ((d->displayType & d_location) == dLocal) { + if (he->lastExit - d->lastStart < 120) { + LogError( "Unable to fire up local display %s;" + " disabling.\n", d->name ); + StopDisplay( d ); + return; + } + } else { + if (++d->startTries > d->startAttempts) { + LogError( "Disabling foreign display %s" + " (too many attempts)\n", d->name ); + StopDisplay( d ); + return; + } + } + } else + d->startTries = 0; + if (d->serverPid != -1 && + (serverCmd != XS_KEEP || d->terminateServer)) + { + Debug( "killing X server for %s\n", d->name ); + TerminateProcess( d->serverPid, d->termSignal ); + d->status = phoenix; + } else + d->status = notRunning; + } + } +} + + +static int pidFd; +static FILE *pidFilePtr; + +static int +StorePid( void ) +{ + int oldpid; + + if (pidFile[0] != '\0') { + pidFd = open( pidFile, O_RDWR ); + if (pidFd == -1 && errno == ENOENT) + pidFd = open( pidFile, O_RDWR|O_CREAT, 0666 ); + if (pidFd == -1 || !(pidFilePtr = fdopen( pidFd, "r+" ))) { + LogError( "process-id file %s cannot be opened\n", + pidFile ); + return -1; + } + if (fscanf( pidFilePtr, "%d\n", &oldpid ) != 1) + oldpid = -1; + fseek( pidFilePtr, 0l, 0 ); + if (lockPidFile) { +#ifdef F_SETLK +# ifndef SEEK_SET +# define SEEK_SET 0 +# endif + struct flock lock_data; + lock_data.l_type = F_WRLCK; + lock_data.l_whence = SEEK_SET; + lock_data.l_start = lock_data.l_len = 0; + if (fcntl( pidFd, F_SETLK, &lock_data ) == -1) { + if (errno == EAGAIN) + return oldpid; + else + return -1; + } +#else +# ifdef LOCK_EX + if (flock( pidFd, LOCK_EX|LOCK_NB ) == -1) { + if (errno == EWOULDBLOCK) + return oldpid; + else + return -1; + } +# else + if (lockf( pidFd, F_TLOCK, 0 ) == -1) { + if (errno == EACCES) + return oldpid; + else + return -1; + } +# endif +#endif + } + fprintf( pidFilePtr, "%ld\n", (long)getpid() ); + (void)fflush( pidFilePtr ); + RegisterCloseOnFork( pidFd ); + } + return 0; +} + +#if 0 +void +UnlockPidFile( void ) +{ + if (lockPidFile) +# ifdef F_SETLK + { + struct flock lock_data; + lock_data.l_type = F_UNLCK; + lock_data.l_whence = SEEK_SET; + lock_data.l_start = lock_data.l_len = 0; + (void)fcntl( pidFd, F_SETLK, &lock_data ); + } +# else +# ifdef F_ULOCK + lockf( pidFd, F_ULOCK, 0 ); +# else + flock( pidFd, LOCK_UN ); +# endif +# endif + close( pidFd ); + fclose( pidFilePtr ); +} +#endif + +void +SetTitle( const char *name ) +{ +#if !defined(HAVE_SETPROCTITLE) && !defined(NOXDMTITLE) + char *p; + int left; +#endif + + ASPrintf( &prog, "%s: %s", prog, name ); + ReInitErrorLog(); +#ifdef HAVE_SETPROCTITLE + setproctitle( "%s", name ); +#elif !defined(NOXDMTITLE) + p = Title; + left = TitleLen; + + *p++ = '-'; + --left; + while (*name && left > 0) { + *p++ = *name++; + --left; + } + while (left > 0) { + *p++ = '\0'; + --left; + } +#endif +} diff --git a/kdm/backend/dm.h b/kdm/backend/dm.h new file mode 100644 index 000000000..6d2f59768 --- /dev/null +++ b/kdm/backend/dm.h @@ -0,0 +1,620 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2000-2005 Oswald Buddenhagen <[email protected]> + +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 + * + * global xdm core declarations + */ + +#ifndef _DM_H_ +#define _DM_H_ 1 + +#include "greet.h" +#include <config.ci> + +#include <X11/X.h> /* FamilyInternet6 */ +#include <X11/Xos.h> +#include <X11/Xfuncs.h> +#include <X11/Xmd.h> +#include <X11/Xauth.h> +#include <X11/Intrinsic.h> + +#include <sys/param.h> +#ifdef HAVE_LIMITS_H +# include <limits.h> +#endif + +#include <time.h> +#define Time_t time_t + +#include <stdlib.h> +#include <errno.h> + +#ifdef XDMCP +# if defined(__osf__) +/* someone somewhere defines QUERY under Tru64 which confuses Xdmcp.h */ +# undef QUERY +# endif +# include <X11/Xdmcp.h> +#endif + +#ifndef PATH_MAX +# ifdef MAXPATHLEN +# define PATH_MAX MAXPATHLEN +# else +# define PATH_MAX 1024 +# endif +#endif + +#include <sys/wait.h> +#define waitCode(w) (WIFEXITED(w) ? WEXITSTATUS(w) : 0) +#define waitSig(w) (WIFSIGNALED(w) ? WTERMSIG(w) : 0) +#ifdef WCOREDUMP +# define waitCore(w) (WCOREDUMP(w)) +#else +# define waitCore(w) 0 /* not in POSIX. so what? */ +#endif +typedef int waitType; + +#define waitCompose(sig,core,code) ((sig) * 256 + (core) * 128 + (code)) +#define waitVal(w) waitCompose(waitSig(w), waitCore(w), waitCode(w)) +#define WaitCode(w) ((w) & 0x7f) +#define WaitCore(w) (((w) >> 7) & 1) +#define WaitSig(w) (((w) >> 8) & 0xff) + +#include <sys/time.h> +#define FD_TYPE fd_set + +#include <setjmp.h> +#if defined(__EMX__) || (defined(__NetBSD__) && defined(__sparc__)) /* XXX netbsd? */ +# define Setjmp(e) setjmp(e) +# define Longjmp(e,v) longjmp(e,v) +# define Jmp_buf jmp_buf +#else +# define Setjmp(e) sigsetjmp(e,1) +# define Longjmp(e,v) siglongjmp(e,v) +# define Jmp_buf sigjmp_buf +#endif + +#include <utmp.h> +#ifdef HAVE_UTMPX +# include <utmpx.h> +# define STRUCTUTMP struct utmpx +# define UTMPNAME utmpxname +# define SETUTENT setutxent +# define GETUTENT getutxent +# define PUTUTLINE pututxline +# define ENDUTENT endutxent +# define LASTLOG lastlogx +# define ut_time ut_tv.tv_sec +# define ll_time ll_tv.tv_sec +#else +# define STRUCTUTMP struct utmp +# define UTMPNAME utmpname +# define SETUTENT setutent +# define GETUTENT getutent +# define PUTUTLINE pututline +# define ENDUTENT endutent +# define LASTLOG lastlog +#endif +#ifndef HAVE_STRUCT_UTMP_UT_USER +# define ut_user ut_name +#endif +#ifndef WTMP_FILE +# ifdef _PATH_WTMPX +# define WTMP_FILE _PATH_WTMPX +# elif defined(_PATH_WTMP) +# define WTMP_FILE _PATH_WTMP +# else +# define WTMP_FILE "/usr/adm/wtmp" +# endif +#endif +#ifndef UTMP_FILE +# ifdef _PATH_UTMPX +# define UTMP_FILE _PATH_UTMPX +# elif defined(_PATH_UTMP) +# define UTMP_FILE _PATH_UTMP +# else +# define UTMP_FILE "/etc/utmp" +# endif +#endif + +#ifdef HAVE_NETCONFIG_H +# define STREAMSCONN +#else +# define UNIXCONN +# define TCPCONN +# ifdef FamilyInternet6 +# define IPv6 +# endif +# ifdef HAVE_NETDNET_DN_H +# define DNETCONN +# endif +#endif + +#if !defined(HAVE_ARC4RANDOM) && !defined(DEV_RANDOM) +# define NEED_ENTROPY +#endif + +typedef struct GPipe { + int wfd, rfd; + char *who; +} GPipe; + +typedef struct GTalk { + GPipe *pipe; + Jmp_buf errjmp; +} GTalk; + +typedef struct GProc { + GPipe pipe; + int pid; +} GProc; + +typedef enum displayStatus { notRunning = 0, running, zombie, phoenix, raiser, + textMode, reserve, remoteLogin } DisplayStatus; + +typedef enum serverStatus { ignore = 0, awaiting, starting, + terminated, killed, pausing } ServerStatus; + +typedef struct RcStr { + struct RcStr *next; + char *str; + int cnt; +} RcStr; + +typedef struct CfgDep { + RcStr *name; + long time; +} CfgDep; + +typedef struct CfgArr { + char *data; /* config value array; allocated */ + long *idx; /* config index array; alias */ + CfgDep dep; /* filestamp */ + int numCfgEnt; /* number of config entries */ +} CfgArr; + +struct bsock { + int fd; + int buflen; + char *buffer; +}; + +struct cmdsock { + struct cmdsock *next; + struct bsock sock; /* buffered fd of the socket */ +}; + +typedef struct { + struct cmdsock *css; /* open connections */ + + char *path; /* filename of the socket */ + int fd; /* fd of the socket */ + int gid; /* owner group of the socket */ + + char *fpath; /* filename of the fifo */ + struct bsock fifo; /* buffered fd of the fifo */ +} CtrlRec; + +struct display { + struct display *next; + struct disphist *hstent; /* display history entry */ + + /* basic display information */ + char *name; /* DISPLAY name -- also referenced in hstent */ + char *class2; /* display class (may be NULL) */ + int displayType; /* location/origin/lifetime */ + CfgArr cfg; /* config data array */ + + /* display state */ + DisplayStatus status; /* current status */ + int zstatus; /* substatus while zombie */ + int pid; /* process id of child */ + int serverPid; /* process id of server (-1 if none) */ +#ifdef HAVE_VTS + int serverVT; /* server VT (0 = none, -1 = pending) */ + struct display *follower; /* on exit, hand VT to this display */ +#endif + ServerStatus serverStatus; /* X server startup state */ + Time_t lastStart; /* time of last display start */ + int startTries; /* current start try */ + int stillThere; /* state during HUP processing */ + int userSess; /* -1=nobody, otherwise uid */ + char *userName; + char *sessName; + CtrlRec ctrl; /* command socket & fifo */ + GPipe pipe; /* comm master <-> slave */ + GPipe gpipe; /* comm master <-> greeter */ +#ifdef XDMCP + char *remoteHost; /* for X -query type remote login */ + /* XDMCP state */ + unsigned sessionID; /* ID of active session */ + ARRAY8 peer; /* display peer address */ + ARRAY8 from; /* XDMCP port of display */ + unsigned displayNumber; /* numerical part of name */ + int useChooser; /* Run the chooser for this display */ + ARRAY8 clientAddr; /* for chooser picking */ + unsigned connectionType; /* ... */ + int xdmcpFd; +#endif + + CONF_CORE_LOCAL_DEFS + + int idleTimeout; /* abort login after that time */ + + unsigned short *authNameLens; /* authorization protocol name lens */ + + /* information potentially derived from resources */ + int authNameNum; /* number of protocol names */ + Xauth **authorizations; /* authorization data */ + int authNum; /* number of authorizations */ + char *authFile; /* file to store authorization in */ +}; + +typedef struct { + unsigned how:2, /* 0=none 1=reboot 2=halt (SHUT_*) */ + force:2; + int uid; + int start; + int timeout; + char *osname; + time_t bmstamp; + int osindex; +} SdRec; + +struct disphist { + struct disphist *next; + char *name; + Time_t lastExit; /* time of last display exit */ + unsigned rLogin:2, /* 0=nothing 1=relogin 2=login */ + lock:1, /* screen locker running */ + goodExit:1; /* was the last exit "peaceful"? */ + SdRec sdRec; + char *nuser, *npass, *nargs; +}; + +#ifdef XDMCP + +#define PROTO_TIMEOUT (30 * 60) /* 30 minutes should be long enough */ + +struct protoDisplay { + struct protoDisplay *next; + XdmcpNetaddr address; /* UDP address */ + int addrlen; /* UDP address length */ + unsigned long date; /* creation date */ + CARD16 displayNumber; + CARD16 connectionType; + ARRAY8 connectionAddress; + CARD32 sessionID; + Xauth *fileAuthorization; + Xauth *xdmcpAuthorization; + ARRAY8 authenticationName; + ARRAY8 authenticationData; + XdmAuthKeyRec key; +}; +#endif /* XDMCP */ + +/* status code for RStopDisplay */ +#define DS_RESTART 0 +#define DS_TEXTMODE 1 +#define DS_RESERVE 2 +#define DS_REMOTE 3 +#define DS_REMOVE 4 + +/* command codes dpy process -> master process */ +#define D_User 1 +#define D_ReLogin 2 +#define D_ChooseHost 4 +#define D_RemoteHost 5 +#define D_XConnOk 6 + +extern int debugLevel; + +CONF_CORE_GLOBAL_DECLS + +/* in daemon.c */ +void BecomeDaemon( void ); + +/* in dm.c */ +extern char *prog, *progpath; +extern time_t now; +extern SdRec sdRec; +void StartDisplay( struct display *d ); +void StartDisplayP2( struct display *d ); +void StopDisplay( struct display *d ); +void SetTitle( const char *name ); +void SwitchToX( struct display *d ); +void setNLogin( struct display *d, + const char *nuser, const char *npass, char *nargs, + int rl ); +void cancelShutdown( void ); +int TTYtoVT( const char *tty ); +int activateVT( int vt ); + +/* in ctrl.c */ +void openCtrl( struct display *d ); +void closeCtrl( struct display *d ); +int handleCtrl( FD_TYPE *reads, struct display *d ); +void chownCtrl( CtrlRec *cr, int uid ); +void updateCtrl( void ); + +/* in dpylist.c */ +extern struct display *displays; /* that's ugly ... */ +int AnyDisplaysLeft( void ); +void ForEachDisplay( void (*f)( struct display * ) ); +#ifdef HAVE_VTS +void ForEachDisplayRev( void (*f)( struct display * ) ); +#endif +void RemoveDisplay( struct display *old ); +struct display + *FindDisplayByName( const char *name ), +#ifdef XDMCP + *FindDisplayBySessionID( CARD32 sessionID ), + *FindDisplayByAddress( XdmcpNetaddr addr, int addrlen, CARD16 displayNumber ), +#endif /* XDMCP */ + *FindDisplayByPid( int pid ), + *FindDisplayByServerPid( int serverPid ), + *NewDisplay( const char *name ); +int AnyActiveDisplays( void ); +int AnyRunningDisplays( void ); +int AnyReserveDisplays( void ); +int idleReserveDisplays( void ); +int AllLocalDisplaysLocked( struct display *dp ); +int StartReserveDisplay( int lt ); +void ReapReserveDisplays( void ); + +/* in reset.c */ +void pseudoReset( void ); + +/* in resource.c */ +char **FindCfgEnt( struct display *d, int id ); +int InitResources( char **argv ); +int LoadDMResources( int force ); +int LoadDisplayResources( struct display *d ); +void ScanServers( void ); +void CloseGetter( void ); +int startConfig( int what, CfgDep *dep, int force ); +RcStr *newStr( char *str ); +void delStr( RcStr *str ); +extern GTalk cnftalk; + +/* in session.c */ +extern struct display *td; +extern const char *td_setup; +char **baseEnv( const char *user ); +char **inheritEnv( char **env, const char **what ); +char **systemEnv( const char *user ); +int source( char **env, const char *file, const char *arg ); +void ManageSession( struct display *d ); + +extern GTalk mstrtalk, grttalk; +extern GProc grtproc; +void OpenGreeter( void ); +int CloseGreeter( int force ); +int CtrlGreeterWait( int wreply ); +void PrepErrorGreet( void ); +char *conv_interact( int what, const char *prompt ); + +/* process.c */ +typedef void (*SIGFUNC)( int ); +SIGFUNC Signal( int, SIGFUNC Handler ); + +void RegisterInput( int fd ); +void UnregisterInput( int fd ); +void RegisterCloseOnFork( int fd ); +void ClearCloseOnFork( int fd ); +void CloseNClearCloseOnFork( int fd ); +int Fork( void ); +int Wait4( int pid ); +void execute( char **argv, char **env ); +int runAndWait( char **args, char **env ); +FILE *pOpen( char **what, char m, int *pid ); +int pClose( FILE *f, int pid ); +char *locate( const char *exe ); +void TerminateProcess( int pid, int sig ); + +void GSet( GTalk *talk); /* call before GOpen! */ +int GFork( GPipe *pajp, const char *pname, char *cname, + GPipe *ogp, char *cgname ); +void GClosen( GPipe *pajp ); +int GOpen( GProc *proc, + char **argv, const char *what, char **env, char *cname, + GPipe *gp ); +int GClose( GProc *proc, GPipe *gp, int force ); + +void GSendInt( int val ); +int GRecvInt( void ); +int GRecvCmd( int *cmd ); +void GSendArr( int len, const char *data ); +char *GRecvArr( int *len ); +int GRecvStrBuf( char *buf ); +int GRecvArrBuf( char *buf ); +void GSendStr( const char *buf ); +void GSendNStr( const char *buf, int len ); /* exact len, buf != 0 */ +void GSendStrN( const char *buf, int len ); /* maximal len */ +char *GRecvStr( void ); +void GSendArgv( char **argv ); +void GSendStrArr( int len, char **data ); +char **GRecvStrArr( int *len ); +char **GRecvArgv( void ); + +/* client.c */ +#define GCONV_NORMAL 0 +#define GCONV_HIDDEN 1 +#define GCONV_USER 2 +#define GCONV_PASS 3 +#define GCONV_PASS_ND 4 +#define GCONV_BINARY 5 +typedef char *(*GConvFunc)( int what, const char *prompt ); +int Verify( GConvFunc gconv, int rootok ); +int StartClient( void ); +void SessionExit( int status ) ATTR_NORETURN; +int ReadDmrc( void ); +extern char **userEnviron, **systemEnviron; +extern char *curuser, *curpass, *curtype, *newpass, + *dmrcuser, *curdmrc, *newdmrc; +extern int cursource; +#define PWSRC_MANUAL 0 +#define PWSRC_AUTOLOGIN 1 +#define PWSRC_RELOGIN 2 + +/* server.c */ +char **PrepServerArgv( struct display *d, const char *args ); +void StartServer( struct display *d ); +void AbortStartServer( struct display *d ); +void StartServerSuccess( void ); +void StartServerFailed( void ); +void StartServerTimeout( void ); +extern struct display *startingServer; +extern time_t serverTimeout; + +void WaitForServer( struct display *d ); +void ResetServer( struct display *d ); +int PingServer(struct display *d ); +extern Display *dpy; + +/* in util.c */ +void *Calloc( size_t nmemb, size_t size ); +void *Malloc( size_t size ); +void *Realloc( void *ptr, size_t size ); +void WipeStr( char *str ); +int StrCmp( const char *s1, const char *s2 ); +#ifdef HAVE_STRNLEN +# define StrNLen(s, m) strnlen(s, m) +#else +int StrNLen( const char *s, int max ); +#endif +int StrNDup( char **dst, const char *src, int len ); +int StrDup( char **dst, const char *src ); +int arrLen( char **arr ); +void freeStrArr( char **arr ); +char **initStrArr( char **arr ); +char **xCopyStrArr( int rn, char **arr ); +/* Note: the following functions free the old data even in case of failure */ +int ReStrN( char **dst, const char *src, int len ); +int ReStr( char **dst, const char *src ); +int StrApp( char **dst, ... ); +char **addStrArr( char **arr, const char *str, int len ); +char **parseArgs( char **argv, const char *string ); +/* End note */ +char **setEnv( char **e, const char *name, const char *value ); +char **putEnv( const char *string, char **env ); +const char *getEnv( char **e, const char *name ); +const char *localHostname( void ); +int Reader( int fd, void *buf, int len ); +int Writer( int fd, const void *buf, int len ); +int fGets( char *buf, int max, FILE *f ); +time_t mTime( const char *fn ); +void ListSessions( int flags, struct display *d, void *ctx, + void (*emitXSess)( struct display *, struct display *, void * ), + void (*emitTTYSess)( STRUCTUTMP *, struct display *, void * ) ); + +/* in inifile.c */ +char *iniLoad( const char *fname ); +int iniSave( const char *data, const char *fname ); +char *iniEntry( char *data, const char *section, const char *key, const char *value ); +char *iniMerge( char *data, const char *newdata ); + +/* in bootman.c */ +int getBootOptions( char ***opts, int *def, int *cur ); +int setBootOption( const char *opt, SdRec *sdr ); +void commitBootOption( void ); + +/* in netaddr.c */ +char *NetaddrAddress( char *netaddrp, int *lenp ); +char *NetaddrPort( char *netaddrp, int *lenp ); +int ConvertAddr( char *saddr, int *len, char **addr ); +int NetaddrFamily( char *netaddrp ); +int addressEqual( char *a1, int len1, char *a2, int len2 ); + +#ifdef XDMCP + +/* in xdmcp.c */ +char *NetworkAddressToHostname( CARD16 connectionType, ARRAY8Ptr connectionAddress ); +void SendFailed( struct display *d, const char *reason ); +void init_session_id( void ); + +/* in policy.c */ +struct sockaddr; +ARRAY8Ptr Accept( struct sockaddr *from, int fromlen, CARD16 displayNumber ); +ARRAY8Ptr ChooseAuthentication( ARRAYofARRAY8Ptr authenticationNames ); +int CheckAuthentication( struct protoDisplay *pdpy, ARRAY8Ptr displayID, ARRAY8Ptr name, ARRAY8Ptr data ); +int SelectAuthorizationTypeIndex( ARRAY8Ptr authenticationName, ARRAYofARRAY8Ptr authorizationNames ); +int SelectConnectionTypeIndex( ARRAY16Ptr connectionTypes, ARRAYofARRAY8Ptr connectionAddresses ); +int Willing( ARRAY8Ptr addr, CARD16 connectionType, ARRAY8Ptr authenticationName, ARRAY8Ptr status, xdmOpCode type ); + +/* in protodpy.c */ +void DisposeProtoDisplay( struct protoDisplay *pdpy ); + +struct protoDisplay *FindProtoDisplay( XdmcpNetaddr address, int addrlen, + CARD16 displayNumber ); +struct protoDisplay *NewProtoDisplay( XdmcpNetaddr address, int addrlen, + CARD16 displayNumber, + CARD16 connectionType, + ARRAY8Ptr connectionAddress, + CARD32 sessionID ); + +#define FamilyBroadcast 0xffff +typedef void (*ChooserFunc)( CARD16 connectionType, ARRAY8Ptr addr, char *closure ); +typedef void (*ListenFunc)( ARRAY8Ptr addr, void **closure ); + +/* in access.c */ +ARRAY8Ptr getLocalAddress( void ); +int AcceptableDisplayAddress( ARRAY8Ptr clientAddress, CARD16 connectionType, xdmOpCode type ); +int ForEachMatchingIndirectHost( ARRAY8Ptr clientAddress, CARD16 connectionType, ChooserFunc function, char *closure ); +void ScanAccessDatabase( int force ); +int UseChooser( ARRAY8Ptr clientAddress, CARD16 connectionType ); +void ForEachChooserHost( ARRAY8Ptr clientAddress, CARD16 connectionType, ChooserFunc function, char *closure ); +void ForEachListenAddr( ListenFunc listenfunction, ListenFunc mcastfcuntion, void **closure ); + +/* in choose.c */ +ARRAY8Ptr IndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType ); +int IsIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType ); +int RememberIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType ); +void ForgetIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType ); +int RegisterIndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType, ARRAY8Ptr choice ); +int DoChoose( void ); + +/* socket.c or streams.c */ +void UpdateListenSockets( void ); +int AnyListenSockets( void ); +int ProcessListenSockets( FD_TYPE *reads ); + +/* in xdmcp.c */ +void ProcessRequestSocket( int fd ); + +#endif /* XDMCP */ + +/* in sessreg.c */ +void sessreg( struct display *d, int pid, const char *user, int uid ); + +#endif /* _DM_H_ */ diff --git a/kdm/backend/dm_auth.h b/kdm/backend/dm_auth.h new file mode 100644 index 000000000..ccf4697b8 --- /dev/null +++ b/kdm/backend/dm_auth.h @@ -0,0 +1,99 @@ +/************************************************************ + +Copyright 1998 by Thomas E. Dickey <[email protected]> + + All Rights Reserved + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +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 ABOVE LISTED COPYRIGHT HOLDER(S) 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(s) of the above copyright +holders shall not be used in advertising or otherwise to promote the +sale, use or other dealings in this Software without prior written +authorization. + +********************************************************/ + +#ifndef _DM_AUTH_H_ +#define _DM_AUTH_H_ 1 + +#include "dm.h" + +void MitInitAuth( unsigned short name_len, const char *name ); +Xauth *MitGetAuth( unsigned short namelen, const char *name ); + +#ifdef HASXDMAUTH +void XdmInitAuth( unsigned short name_len, const char *name ); +Xauth *XdmGetAuth( unsigned short namelen, const char *name ); +# ifdef XDMCP +void XdmGetXdmcpAuth( struct protoDisplay *pdpy, + unsigned short authorizationNameLen, + const char *authorizationName ); +int XdmCheckAuthentication( struct protoDisplay *pdpy, + ARRAY8Ptr displayID, + ARRAY8Ptr authenticationName, + ARRAY8Ptr authenticationData ); +# else +# define XdmGetXdmcpAuth NULL +# endif +#endif + +#ifdef SECURE_RPC +void SecureRPCInitAuth( unsigned short name_len, const char *name ); +Xauth *SecureRPCGetAuth( unsigned short name_len, const char *name ); +#endif + +#ifdef K5AUTH +void Krb5InitAuth( unsigned short name_len, const char *name ); +Xauth *Krb5GetAuth( unsigned short name_len, const char *name ); + +Xauth *Krb5GetAuthFor( unsigned short name_len, const char *name, const char *dname ); +char *Krb5Init( const char *user, const char *passwd, const char *dname ); +void Krb5Destroy( const char *dname ); +#endif + +/* auth.c */ +int ValidAuthorization( unsigned short name_length, const char *name ); + + +#ifdef XDMCP + +void +SetProtoDisplayAuthorization( struct protoDisplay *pdpy, + unsigned short authorizationNameLen, + const char *authorizationName ); + +#endif /* XDMCP */ + +int SaveServerAuthorizations( struct display *d, Xauth **auths, int count ); +void CleanUpFileName( const char *src, char *dst, int len ); +void RemoveUserAuthorization( struct display *d ); +void SetAuthorization( struct display *d ); +void SetLocalAuthorization( struct display *d ); +void SetUserAuthorization( struct display *d ); + +/* genauth.c */ +int GenerateAuthData( char *auth, int len ); +#ifdef NEED_ENTROPY +void AddPreGetEntropy( void ); +void AddOtherEntropy( void ); +void AddTimerEntropy( void ); +#endif + +#endif /* _DM_AUTH_H_ */ diff --git a/kdm/backend/dm_error.h b/kdm/backend/dm_error.h new file mode 100644 index 000000000..3570c18fc --- /dev/null +++ b/kdm/backend/dm_error.h @@ -0,0 +1,58 @@ +/************************************************************ + +Copyright 1998 by Thomas E. Dickey <[email protected]> + + All Rights Reserved + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +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 ABOVE LISTED COPYRIGHT HOLDER(S) 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(s) of the above copyright +holders shall not be used in advertising or otherwise to promote the +sale, use or other dealings in this Software without prior written +authorization. + +********************************************************/ + + +#ifndef _DM_ERROR_H_ +#define _DM_ERROR_H_ 1 + +#include "greet.h" + +#include <stdarg.h> + +void GDebug( const char *fmt, ... ); +void Debug( const char *fmt, ... ); +void LogInfo( const char *fmt, ... ); +void LogWarn( const char *fmt, ... ); +void LogError( const char *fmt, ... ); +void LogPanic( const char *fmt, ... ) ATTR_NORETURN; +void LogOutOfMem( void ); +void Panic( const char *mesg ) ATTR_NORETURN; +void InitErrorLog( const char *errorLogFile ); +#ifdef USE_SYSLOG +void ReInitErrorLog( void ); +#else +# define ReInitErrorLog() while(0) +#endif +int ASPrintf( char **strp, const char *fmt, ... ); +int VASPrintf( char **strp, const char *fmt, va_list args ); + +#endif /* _DM_ERROR_H_ */ diff --git a/kdm/backend/dm_socket.h b/kdm/backend/dm_socket.h new file mode 100644 index 000000000..56a39fd0e --- /dev/null +++ b/kdm/backend/dm_socket.h @@ -0,0 +1,72 @@ +/************************************************************ + +Copyright 1998 by Thomas E. Dickey <[email protected]> +Copyright 2002-2004 Oswald Buddenhagen <[email protected]> + + All Rights Reserved + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +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 ABOVE LISTED COPYRIGHT HOLDER(S) 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(s) of the above copyright +holders shall not be used in advertising or otherwise to promote the +sale, use or other dealings in this Software without prior written +authorization. + +********************************************************/ + +#ifndef _DM_SOCKET_H_ +#define _DM_SOCKET_H_ 1 + +#ifndef __Lynx__ +# include <sys/socket.h> +#else +# include <socket.h> +#endif + +#ifdef TCPCONN +# include <netinet/in.h> +#endif + +#ifdef UNIXCONN +# ifndef __Lynx__ +# include <sys/un.h> +# else +# include <un.h> +# endif +#endif + +#ifdef DNETCONN +# include <netdnet/dn.h> +#endif + +#if (defined(__svr4__) && !defined(__sun__)) && defined(SIOCGIFCONF) +# define SYSV_SIOCGIFCONF +int ifioctl( int fd, int cmd, char *arg ); +#else +# define ifioctl ioctl +#endif + +#ifdef BSD +# if (BSD >= 199103) +# define VARIABLE_IFREQ +# endif +#endif + +#endif /* _DM_SOCKET_H_ */ diff --git a/kdm/backend/dpylist.c b/kdm/backend/dpylist.c new file mode 100644 index 000000000..b512293f7 --- /dev/null +++ b/kdm/backend/dpylist.c @@ -0,0 +1,294 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2000-2005 Oswald Buddenhagen <[email protected]> + +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 + * + * a simple linked list of known displays + */ + +#include "dm.h" +#include "dm_error.h" + +struct display *displays; +static struct disphist *disphist; + +int +AnyDisplaysLeft( void ) +{ + return displays != (struct display *)0; +} + +int +AnyActiveDisplays( void ) +{ + struct display *d; + + for (d = displays; d; d = d->next) + if (d->status == remoteLogin || d->userSess >= 0) + return 1; + return 0; +} + +int +AnyRunningDisplays( void ) +{ + struct display *d; + + for (d = displays; d; d = d->next) + switch (d->status) { + case notRunning: + case textMode: + case reserve: + break; + default: + return 1; + } + return 0; +} + +int +AnyReserveDisplays( void ) +{ + struct display *d; + + for (d = displays; d; d = d->next) + if ((d->displayType & d_lifetime) == dReserve) + return 1; + return 0; +} + +int +idleReserveDisplays( void ) +{ + struct display *d; + int cnt = 0; + + for (d = displays; d; d = d->next) + if (d->status == reserve) + cnt++; + return cnt; +} + +int +StartReserveDisplay( int lt ) +{ + struct display *d, *rd; + + for (rd = 0, d = displays; d; d = d->next) + if (d->status == reserve) + rd = d; + if (rd) { + rd->idleTimeout = lt; + rd->status = notRunning; + return 1; + } + return 0; +} + +void +ForEachDisplay( void (*f)( struct display * ) ) +{ + struct display *d, *next; + + for (d = displays; d; d = next) { + next = d->next; + (*f)( d ); + } +} + +#ifdef HAVE_VTS +static void +_forEachDisplayRev( struct display *d, void (*f)( struct display * ) ) +{ + if (d) { + if (d->next) + _forEachDisplayRev( d->next, f ); + (*f)( d ); + } +} + +void +ForEachDisplayRev( void (*f)( struct display * ) ) +{ + _forEachDisplayRev( displays, f ); +} +#endif + +struct display * +FindDisplayByName( const char *name ) +{ + struct display *d; + + for (d = displays; d; d = d->next) + if (!strcmp( name, d->name )) + return d; + return 0; +} + +struct display * +FindDisplayByPid( int pid ) +{ + struct display *d; + + for (d = displays; d; d = d->next) + if (pid == d->pid) + return d; + return 0; +} + +struct display * +FindDisplayByServerPid( int serverPid ) +{ + struct display *d; + + for (d = displays; d; d = d->next) + if (serverPid == d->serverPid) + return d; + return 0; +} + +#ifdef XDMCP + +struct display * +FindDisplayBySessionID( CARD32 sessionID ) +{ + struct display *d; + + for (d = displays; d; d = d->next) + if (sessionID == d->sessionID) + return d; + return 0; +} + +struct display * +FindDisplayByAddress( XdmcpNetaddr addr, int addrlen, CARD16 displayNumber ) +{ + struct display *d; + + for (d = displays; d; d = d->next) + if ((d->displayType & d_origin) == dFromXDMCP && + d->displayNumber == displayNumber && + addressEqual( (XdmcpNetaddr)d->from.data, d->from.length, + addr, addrlen )) + return d; + return 0; +} + +#endif /* XDMCP */ + +#define IfFree(x) if (x) free( (char *)x ) + +void +RemoveDisplay( struct display *old ) +{ + struct display *d, **dp; + int i; + + for (dp = &displays; (d = *dp); dp = &(*dp)->next) { + if (d == old) { + Debug( "Removing display %s\n", d->name ); + *dp = d->next; + IfFree( d->class2 ); + IfFree( d->cfg.data ); + delStr( d->cfg.dep.name ); +#ifdef XDMCP + IfFree( d->remoteHost ); +#endif + if (d->authorizations) { + for (i = 0; i < d->authNum; i++) + XauDisposeAuth( d->authorizations[i] ); + free( (char *)d->authorizations ); + } + if (d->authFile) { + (void)unlink( d->authFile ); + free( d->authFile ); + } + IfFree( d->authNameLens ); +#ifdef XDMCP + XdmcpDisposeARRAY8( &d->peer ); + XdmcpDisposeARRAY8( &d->from ); + XdmcpDisposeARRAY8( &d->clientAddr ); +#endif + free( (char *)d ); + break; + } + } +} + +static struct disphist * +FindHist( const char *name ) +{ + struct disphist *hstent; + + for (hstent = disphist; hstent; hstent = hstent->next) + if (!strcmp( hstent->name, name )) + return hstent; + return 0; +} + +struct display * +NewDisplay( const char *name ) +{ + struct display *d; + struct disphist *hstent; + + if (!(hstent = FindHist( name ))) { + if (!(hstent = Calloc( 1, sizeof(*hstent) ))) + return 0; + if (!StrDup( &hstent->name, name )) { + free( hstent ); + return 0; + } + hstent->next = disphist; disphist = hstent; + } + + if (!(d = (struct display *)Calloc( 1, sizeof(*d) ))) + return 0; + d->next = displays; + d->hstent = hstent; + d->name = hstent->name; + /* initialize fields (others are 0) */ + d->pid = -1; + d->serverPid = -1; + d->ctrl.fd = -1; + d->ctrl.fifo.fd = -1; + d->pipe.rfd = -1; + d->pipe.wfd = -1; + d->gpipe.rfd = -1; + d->gpipe.wfd = -1; + d->userSess = -1; +#ifdef XDMCP + d->xdmcpFd = -1; +#endif + displays = d; + Debug( "created new display %s\n", d->name ); + return d; +} diff --git a/kdm/backend/error.c b/kdm/backend/error.c new file mode 100644 index 000000000..0680ed3a5 --- /dev/null +++ b/kdm/backend/error.c @@ -0,0 +1,130 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2000-2004 Oswald Buddenhagen <[email protected]> + +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 + * + * Log display manager errors to a file as + * we generally do not have a terminal to talk to + * or use syslog if it exists + */ + +#include "dm.h" +#include "dm_error.h" + +#include <unistd.h> +#include <stdio.h> + +#define PRINT_QUOTES +#define PRINT_ARRAYS +#define LOG_DEBUG_MASK DEBUG_CORE +#define LOG_PANIC_EXIT 1 +#define NEED_ASPRINTF +#define STATIC +#include "printf.c" + +void +GDebug( const char *fmt, ... ) +{ + va_list args; + + if (debugLevel & DEBUG_HLPCON) { + va_start( args, fmt ); + Logger( DM_DEBUG, fmt, args ); + va_end( args ); + } +} + +void +Panic( const char *mesg ) +{ + int fd = open( "/dev/console", O_WRONLY ); + write( fd, "xdm panic: ", 11 ); + write( fd, mesg, strlen( mesg ) ); + write( fd, "\n", 1 ); +#ifdef USE_SYSLOG + ReInitErrorLog(); + syslog( LOG_ALERT, "%s", mesg ); +#endif + exit( 1 ); +} + +#ifdef USE_SYSLOG +void +ReInitErrorLog() +{ + if (!(debugLevel & DEBUG_NOSYSLOG)) + InitLog(); +} +#endif + +void +InitErrorLog( const char *errorLogFile ) +{ + int fd; + char buf[128]; + +#ifdef USE_SYSLOG + ReInitErrorLog(); +#endif + /* We do this independently of using syslog, as we cannot redirect + * the output of external programs to syslog. + */ + if (!errorLogFile || strcmp( errorLogFile, "-" )) { + if (!errorLogFile) { + sprintf( buf, "/var/log/%s.log", prog ); + errorLogFile = buf; + } + if ((fd = open( errorLogFile, O_CREAT | O_APPEND | O_WRONLY, 0666 )) < 0) + LogError( "Cannot open log file %s\n", errorLogFile ); + else { +#ifdef USE_SYSLOG +# ifdef USE_PAM +# define PAMLOG " PAM logs messages related to authentication to authpriv.*." +# else +# define PAMLOG +# endif +# define WARNMSG \ + "********************************************************************************\n" \ + "Note that your system uses syslog. All of kdm's internally generated messages\n" \ + "(i.e., not from libraries and external programs/scripts it uses) go to the\n" \ + "daemon.* syslog facility; check your syslog configuration to find out to which\n" \ + "file(s) it is logged." PAMLOG "\n" \ + "********************************************************************************\n\n" + if (!lseek( fd, 0, SEEK_END )) + write( fd, WARNMSG, sizeof(WARNMSG) - 1 ); +#endif + dup2( fd, 1 ); + close( fd ); + dup2( 1, 2 ); + } + } +} + diff --git a/kdm/backend/genauth.c b/kdm/backend/genauth.c new file mode 100644 index 000000000..2978851e1 --- /dev/null +++ b/kdm/backend/genauth.c @@ -0,0 +1,490 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2003-2004 Oswald Buddenhagen <[email protected]> + +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 + */ + +#include "dm.h" +#include "dm_auth.h" +#include "dm_error.h" + +#ifdef NEED_ENTROPY + +# include <signal.h> + +/* ####################################################################### */ + +/* + * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved. + * Copyright (c) 2001-2002 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "dm_socket.h" + +#include <string.h> + +#ifndef INADDR_LOOPBACK +# define INADDR_LOOPBACK 0x7F000001U +#endif + +#ifndef offsetof +# define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +static int +getPrngdBytes( char *buf, int len, + unsigned short tcp_port, const char *socket_path ) +{ + int fd, addr_len, rval, errors; + char msg[2]; + struct sockaddr *addr; + struct sockaddr_in addr_in; + struct sockaddr_un addr_un; + int af; + SIGFUNC old_sigpipe; + + if (tcp_port) { + memset( &addr_in, 0, sizeof(addr_in) ); + af = addr_in.sin_family = AF_INET; + addr_in.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); + addr_in.sin_port = htons( tcp_port ); + addr_len = sizeof(addr_in); + addr = (struct sockaddr *)&addr_in; + } else if (*socket_path) { + unsigned spl = strlen( socket_path ); + if (spl >= sizeof(addr_un.sun_path)) { + LogError( "get_random_prngd: " + "Random pool path is too long\n" ); + return -1; + } + af = addr_un.sun_family = AF_UNIX; + strncpy( addr_un.sun_path, socket_path, + sizeof(addr_un.sun_path) ); + addr_len = offsetof( struct sockaddr_un, sun_path ) + spl + 1; + addr = (struct sockaddr *)&addr_un; + } else + return -1; + + old_sigpipe = Signal( SIGPIPE, SIG_IGN ); + + errors = 0; + rval = -1; +reopen: + if ((fd = socket( af, SOCK_STREAM, 0 )) < 0) { + LogError( "Couldn't create socket: %m\n" ); + goto done; + } + + if (connect( fd, (struct sockaddr *)addr, addr_len )) { + if (af == AF_INET) + LogError( "Couldn't connect to PRNGD port %d: %m\n", + tcp_port ); + else + LogError( "Couldn't connect to PRNGD socket %\"s: %m\n", + socket_path ); + goto done; + } + + /* Send blocking read request to PRNGD */ + msg[0] = 0x02; + msg[1] = len; + + if (Writer( fd, msg, sizeof(msg) ) != sizeof(msg)) { + if (errno == EPIPE && errors < 10) { + close( fd ); + errors++; + goto reopen; + } + LogError( "Couldn't write to PRNGD socket: %m\n" ); + goto done; + } + + if (Reader( fd, buf, len ) != len) { + if (errno == EPIPE && errors < 10) { + close( fd ); + errors++; + goto reopen; + } + LogError( "Couldn't read from PRNGD socket: %m\n" ); + goto done; + } + + rval = 0; +done: + Signal( SIGPIPE, old_sigpipe ); + if (fd != -1) + close( fd ); + return rval; +} + +/* ####################################################################### */ + +/* + * Stolen from the Linux kernel. + * + * Copyright Theodore Ts'o, 1994, 1995, 1996, 1997, 1998, 1999. All + * rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +static unsigned epool[32], erotate, eadd_ptr; + +static void +add_entropy( unsigned const *in, int nwords ) +{ + static unsigned const twist_table[8] = { + 0, 0x3b6e20c8, 0x76dc4190, 0x4db26158, + 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 }; + unsigned i, w; + int new_rotate; + + while (nwords--) { + w = *in++; + w = (w<<erotate | w>>(32-erotate)) & 0xffffffff; + i = eadd_ptr = (eadd_ptr - 1) & 31; + new_rotate = erotate + 14; + if (i) + new_rotate = erotate + 7; + erotate = new_rotate & 31; + w ^= epool[(i + 26) & 31]; + w ^= epool[(i + 20) & 31]; + w ^= epool[(i + 14) & 31]; + w ^= epool[(i + 7) & 31]; + w ^= epool[(i + 1) & 31]; + w ^= epool[i]; + epool[i] = (w >> 3) ^ twist_table[w & 7]; + } +} + +/* ####################################################################### */ + +/* + * This code implements something close to the MD5 message-digest + * algorithm. This code is based on code written by Colin Plumb + * in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + */ + +/* The four core functions - F1 is optimized somewhat */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1 (z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define pmd5_step(f, w, x, y, z, data, s) \ + (w += (f(x, y, z) + data) & 0xffffffff, w = w<<s | w>>(32-s), w += x) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. + */ +static void +pmd5_hash( unsigned *out, unsigned const in[16] ) +{ + unsigned a, b, c, d; + + a = out[0]; + b = out[1]; + c = out[2]; + d = out[3]; + + pmd5_step( F1, a, b, c, d, in[0] + 0xd76aa478, 7 ); + pmd5_step( F1, d, a, b, c, in[1] + 0xe8c7b756, 12 ); + pmd5_step( F1, c, d, a, b, in[2] + 0x242070db, 17 ); + pmd5_step( F1, b, c, d, a, in[3] + 0xc1bdceee, 22 ); + pmd5_step( F1, a, b, c, d, in[4] + 0xf57c0faf, 7 ); + pmd5_step( F1, d, a, b, c, in[5] + 0x4787c62a, 12 ); + pmd5_step( F1, c, d, a, b, in[6] + 0xa8304613, 17 ); + pmd5_step( F1, b, c, d, a, in[7] + 0xfd469501, 22 ); + pmd5_step( F1, a, b, c, d, in[8] + 0x698098d8, 7 ); + pmd5_step( F1, d, a, b, c, in[9] + 0x8b44f7af, 12 ); + pmd5_step( F1, c, d, a, b, in[10] + 0xffff5bb1, 17 ); + pmd5_step( F1, b, c, d, a, in[11] + 0x895cd7be, 22 ); + pmd5_step( F1, a, b, c, d, in[12] + 0x6b901122, 7 ); + pmd5_step( F1, d, a, b, c, in[13] + 0xfd987193, 12 ); + pmd5_step( F1, c, d, a, b, in[14] + 0xa679438e, 17 ); + pmd5_step( F1, b, c, d, a, in[15] + 0x49b40821, 22 ); + + pmd5_step( F2, a, b, c, d, in[1] + 0xf61e2562, 5 ); + pmd5_step( F2, d, a, b, c, in[6] + 0xc040b340, 9 ); + pmd5_step( F2, c, d, a, b, in[11] + 0x265e5a51, 14 ); + pmd5_step( F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20 ); + pmd5_step( F2, a, b, c, d, in[5] + 0xd62f105d, 5 ); + pmd5_step( F2, d, a, b, c, in[10] + 0x02441453, 9 ); + pmd5_step( F2, c, d, a, b, in[15] + 0xd8a1e681, 14 ); + pmd5_step( F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20 ); + pmd5_step( F2, a, b, c, d, in[9] + 0x21e1cde6, 5 ); + pmd5_step( F2, d, a, b, c, in[14] + 0xc33707d6, 9 ); + pmd5_step( F2, c, d, a, b, in[3] + 0xf4d50d87, 14 ); + pmd5_step( F2, b, c, d, a, in[8] + 0x455a14ed, 20 ); + pmd5_step( F2, a, b, c, d, in[13] + 0xa9e3e905, 5 ); + pmd5_step( F2, d, a, b, c, in[2] + 0xfcefa3f8, 9 ); + pmd5_step( F2, c, d, a, b, in[7] + 0x676f02d9, 14 ); + pmd5_step( F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20 ); + + pmd5_step( F3, a, b, c, d, in[5] + 0xfffa3942, 4 ); + pmd5_step( F3, d, a, b, c, in[8] + 0x8771f681, 11 ); + pmd5_step( F3, c, d, a, b, in[11] + 0x6d9d6122, 16 ); + pmd5_step( F3, b, c, d, a, in[14] + 0xfde5380c, 23 ); + pmd5_step( F3, a, b, c, d, in[1] + 0xa4beea44, 4 ); + pmd5_step( F3, d, a, b, c, in[4] + 0x4bdecfa9, 11 ); + pmd5_step( F3, c, d, a, b, in[7] + 0xf6bb4b60, 16 ); + pmd5_step( F3, b, c, d, a, in[10] + 0xbebfbc70, 23 ); + pmd5_step( F3, a, b, c, d, in[13] + 0x289b7ec6, 4 ); + pmd5_step( F3, d, a, b, c, in[0] + 0xeaa127fa, 11 ); + pmd5_step( F3, c, d, a, b, in[3] + 0xd4ef3085, 16 ); + pmd5_step( F3, b, c, d, a, in[6] + 0x04881d05, 23 ); + pmd5_step( F3, a, b, c, d, in[9] + 0xd9d4d039, 4 ); + pmd5_step( F3, d, a, b, c, in[12] + 0xe6db99e5, 11 ); + pmd5_step( F3, c, d, a, b, in[15] + 0x1fa27cf8, 16 ); + pmd5_step( F3, b, c, d, a, in[2] + 0xc4ac5665, 23 ); + + pmd5_step( F4, a, b, c, d, in[0] + 0xf4292244, 6 ); + pmd5_step( F4, d, a, b, c, in[7] + 0x432aff97, 10 ); + pmd5_step( F4, c, d, a, b, in[14] + 0xab9423a7, 15 ); + pmd5_step( F4, b, c, d, a, in[5] + 0xfc93a039, 21 ); + pmd5_step( F4, a, b, c, d, in[12] + 0x655b59c3, 6 ); + pmd5_step( F4, d, a, b, c, in[3] + 0x8f0ccc92, 10 ); + pmd5_step( F4, c, d, a, b, in[10] + 0xffeff47d, 15 ); + pmd5_step( F4, b, c, d, a, in[1] + 0x85845dd1, 21 ); + pmd5_step( F4, a, b, c, d, in[8] + 0x6fa87e4f, 6 ); + pmd5_step( F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10 ); + pmd5_step( F4, c, d, a, b, in[6] + 0xa3014314, 15 ); + pmd5_step( F4, b, c, d, a, in[13] + 0x4e0811a1, 21 ); + pmd5_step( F4, a, b, c, d, in[4] + 0xf7537e82, 6 ); + pmd5_step( F4, d, a, b, c, in[11] + 0xbd3af235, 10 ); + pmd5_step( F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15 ); + pmd5_step( F4, b, c, d, a, in[9] + 0xeb86d391, 21 ); + + out[0] += a; + out[1] += b; + out[2] += c; + out[3] += d; +} + +/* ####################################################################### */ + + +static int +sumFile( const char *name, int len, int whence, long offset ) +{ + int fd, i, cnt, readlen = 0; + unsigned char buf[0x1000]; + + if ((fd = open( name, O_RDONLY )) < 0) { + Debug( "cannot open entropy source %\"s: %m\n", name ); + return -1; + } + lseek( fd, offset, whence ); + while (readlen < len) { + if (!(cnt = read( fd, buf, sizeof(buf) ))) + break; + if (cnt < 0) { + close( fd ); + Debug( "cannot read entropy source %\"s: %m\n", name ); + return -1; + } + readlen += cnt; + if (sizeof(unsigned) == 4) + add_entropy( (unsigned *)buf, (cnt + 3) / 4 ); + else { + unsigned buf2[sizeof(buf) / 4]; + for (i = 0; i < cnt; i += 8) { + buf2[i / 4] = *(unsigned *)(buf + i) & 0xffffffff; + buf2[i / 4 + 1] = *(unsigned *)(buf + i) >> 32; + } + add_entropy( buf2, (cnt + 3) / 4 ); + } + } + close( fd ); + Debug( "read %d bytes from entropy source %\"s\n", readlen, name ); + return readlen; +} + +void +AddTimerEntropy( void ) +{ + struct timeval now; + gettimeofday( &now, 0 ); + add_entropy( (unsigned *)&now, sizeof(now)/sizeof(unsigned) ); +} + +#define BSIZ 0x10000 + +void +AddOtherEntropy( void ) +{ + AddTimerEntropy(); + /* XXX -- setup-specific ... use some common ones */ + sumFile( "/var/log/messages", 0x1000, SEEK_END, -0x1000 ); + sumFile( "/var/log/syslog", 0x1000, SEEK_END, -0x1000 ); + sumFile( "/var/log/debug", 0x1000, SEEK_END, -0x1000 ); + sumFile( "/var/log/kern.log", 0x1000, SEEK_END, -0x1000 ); + sumFile( "/var/log/daemon.log", 0x1000, SEEK_END, -0x1000 ); +/* root hardly ever has an own box ... maybe pick a random mailbox instead? eek ... + sumFile( "/var/spool/mail/root", 0x1000, SEEK_END, -0x1000 ); +*/ +} + +void +AddPreGetEntropy( void ) +{ + static long offset; + int readlen; + + AddTimerEntropy(); + if ((readlen = sumFile( randomFile, BSIZ, SEEK_SET, offset )) == BSIZ) { + offset += readlen; +#if defined(__i386__) || defined(amiga) + if (!strcmp( randomFile, "/dev/mem" )) { + if (offset == 0xa0000) /* skip 640kB-1MB ROM mappings */ + offset = 0x100000; + else if (offset == 0xf00000) /* skip 15-16MB memory hole */ + offset = 0x1000000; + } +#endif + return; + } else if (readlen >= 0 && offset) { + if ((offset = sumFile( randomFile, BSIZ, SEEK_SET, 0 )) == BSIZ) + return; + } + LogError( "Cannot read randomFile %\"s; " + "X cookies may be easily guessable\n", randomFile ); +} +#endif + +/* ONLY 8 or 16 bytes! */ +/* auth MUST be sizeof(unsigned)-aligned! */ +int +GenerateAuthData( char *auth, int len ) +{ +#ifdef HAVE_ARC4RANDOM + int i; + unsigned *rnd = (unsigned *)auth; + if (sizeof(unsigned) == 4) + for (i = 0; i < len; i += 4) + rnd[i / 4] = arc4random(); + else + for (i = 0; i < len; i += 8) + rnd[i / 8] = arc4random() | (arc4random() << 32); + return 1; +#else + int fd; + const char *rd = randomDevice; +# ifdef DEV_RANDOM + if (!*rd) + rd = DEV_RANDOM; +# else + if (*rd) { +# endif + if ((fd = open( rd, O_RDONLY )) >= 0) { + if (read( fd, auth, len ) == len) { + close( fd ); + return 1; + } + close( fd ); + LogError( "Cannot read randomDevice %\"s: %m\n", rd ); + } else + LogError( "Cannot open randomDevice %\"s: %m\n", rd ); +# ifdef DEV_RANDOM + return 0; +# else + } + + if (!getPrngdBytes( auth, len, prngdPort, prngdSocket )) + return 1; + + { + unsigned *rnd = (unsigned *)auth; + unsigned tmp[4] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 }; + AddPreGetEntropy(); + pmd5_hash( tmp, epool ); + add_entropy( tmp, 1 ); + pmd5_hash( tmp, epool + 16 ); + add_entropy( tmp + 2, 1 ); + if (sizeof(unsigned) == 4) + memcpy( auth, tmp, len ); + else { + int i; + for (i = 0; i < len; i += 8) + rnd[i / 8] = tmp[i / 4] | (tmp[i / 4 + 1] << 32); + } + } + return 1; +# endif +#endif +} diff --git a/kdm/backend/greet.h b/kdm/backend/greet.h new file mode 100644 index 000000000..985edc29c --- /dev/null +++ b/kdm/backend/greet.h @@ -0,0 +1,278 @@ +/* + +Copyright 2001-2005 Oswald Buddenhagen <[email protected]> + +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 + * + * interface to xdm's external greeter and config reader + */ + +#ifndef GREET_H +#define GREET_H + +#include <config.h> + +#define DEBUG_CORE 0x01 +#define DEBUG_CONFIG 0x02 +#define DEBUG_GREET 0x04 +#define DEBUG_HLPCON 0x08 +#define DEBUG_WSESS 0x10 +#define DEBUG_WCONFIG 0x20 +#define DEBUG_WGREET 0x40 +#define DEBUG_NOSYSLOG 0x80 +#define DEBUG_AUTH 0x100 +#define DEBUG_VALGRIND 0x400 +#define DEBUG_STRACE 0x800 + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) +# define ATTR_UNUSED __attribute__((unused)) +# define ATTR_NORETURN __attribute__((noreturn)) +# define ATTR_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var))) +#else +# define ATTR_UNUSED +# define ATTR_NORETURN +# define ATTR_PRINTFLIKE(fmt,var) +#endif + +#define as(ar) ((int)(sizeof(ar)/sizeof(ar[0]))) + +#define __stringify(x) #x +#define stringify(x) __stringify(x) + +/* + * Exit codes for fork()ed session process, greeter, and config reader + */ +#define EX_NORMAL 30 /* do whatever seems appropriate */ +#define EX_REMANAGE_DPY 31 /* force remanage; same as EX_NORMAL, but cannot return to reserve mode immediately */ +#define EX_UNMANAGE_DPY 32 /* force deletion */ +#define EX_RESERVER_DPY 33 /* force server termination */ +#define EX_AL_RESERVER_DPY 34 /* reserver; maybe, auto-(re-)login */ +#define EX_OPENFAILED_DPY 35 /* XOpenDisplay failed, retry */ +#define EX_RESERVE 37 /* put in reserve mode */ +#ifdef XDMCP +#define EX_REMOTE 38 /* start -query-ing X-server */ +#define EX_MAX EX_REMOTE +#else +#define EX_MAX EX_RESERVE +#endif + +/* + * Command codes core -> greeter + */ +#define G_Greet 1 /* get login; bidi */ +#define G_ErrorGreet 2 /* print failed auto-login */ +#ifdef XDMCP +#define G_Choose 3 /* run chooser; bidi */ +# define G_Ch_AddHost 301 +# define G_Ch_ChangeHost 302 +# define G_Ch_RemoveHost 303 +# define G_Ch_BadHost 304 +# define G_Ch_Exit 305 +#endif +#define G_SessMan 4 /* start "session manager" */ +#define G_ConfShutdown 5 /* confirm forced shutdown */ +#define G_GreetTimed 6 /* get login; timed login permitted */ + +#ifdef XDMCP +#define G_Ch_Refresh 10 /* XXX change */ +#define G_Ch_RegisterHost 11 /* str name XXX change */ +#define G_Ch_DirectChoice 12 /* str name XXX change */ +#endif + +/* + * Status/command codes greeter -> core + */ +#define G_Ready 0 /* nop */ +#define G_Cancel 1 /* abort login, etc. */ + +#define G_DGreet 2 /* get login */ +#ifdef XDMCP +#define G_DChoose 3 /* run chooser */ +#endif + +#define G_Shutdown 101 /* 5*int, string; async */ +# define SHUT_REBOOT 1 /* how */ +# define SHUT_HALT 2 +# define SHUT_CONSOLE -1 /* pseudo-code */ +# define SHUT_SCHEDULE 0 /* when; config only */ +# define SHUT_TRYNOW 1 +# define SHUT_FORCENOW 2 +# define SHUT_CANCEL 0 /* force */ +# define SHUT_FORCEMY 1 +# define SHUT_FORCE 2 +# define SHUT_ASK 3 +# define TO_INF 0x7fffffff +#define G_SessionExit 102 /* int code; async */ +#define G_GetCfg 103 /* int what; int sts, <variable> */ +#define G_SetupDpy 104 /* ; int <syncer> */ +#define G_ReadDmrc 105 /* str user; int sts - curdmrc */ +#define G_GetDmrc 106 /* str key; str value - curdmrc */ +/*#define G_ResetDmrc 107*/ /* ; async - newdmrc */ +#define G_PutDmrc 108 /* str key, str value; async - newdmrc */ +#define G_Verify 109 /* str type; ..., int V_ret */ +#define G_VerifyRootOK 110 /* str type; ..., int V_ret */ +#define G_List 111 /* int flags; ?*(str,str,[int,]str,str,int), int 0 */ +# define lstRemote 1 +# define lstPassive 2 +# define lstTTY 4 +# define isSelf 1 +# define isTTY 2 +#define G_QueryShutdown 112 /* ; 5*int; string */ +#define G_Activate 113 /* int vt; async */ +#define G_ListBootOpts 114 /* ; int sts, [argv opts, int dflt, int cur] */ +# define BO_OK 0 +# define BO_NOMAN -1 +# define BO_NOENT -2 +# define BO_IO -3 +#define G_Console 116 /* ; async */ +#define G_AutoLogin 117 /* ; async */ + +/* + * Command codes core -> config reader + */ +#define GC_Files 1 /* get file list */ +#define GC_GetConf 2 /* get a config group */ +# define GC_gGlobal 1 /* get global config array */ +#ifdef XDMCP +# define GC_gXaccess 3 /* get Xaccess equivalent */ +#endif +# define GC_gDisplay 4 /* get per-display config array */ + +/* + * Error code core -> greeter + */ +#define GE_Ok 0 +#define GE_NoFkt 1 /* no such function (only for extensions!) */ +#define GE_Error 2 /* internal error, like OOM */ +/* for config reading */ +#define GE_NoEnt 10 /* no such config entry */ +#define GE_BadType 11 /* unknown config entry type */ +/* for dmrc reading */ +#define GE_NoUser 20 /* no such user */ +#define GE_NoFile 21 /* no such file */ +#define GE_Denied 22 /* permission denied */ + +/* + * Log levels. + * Used independently in core, greeter & config reader. + */ +#define DM_DEBUG 0 +#define DM_INFO 1 +#define DM_WARN 2 +#define DM_ERR 3 +#define DM_PANIC 4 + +/* + * Status codes from Verify + */ +/* terminal status codes */ +#define V_OK 0 +#define V_FAIL 10 /* whatever, already reported with V_MSG_* */ +#define V_AUTH 11 /* authentication failed */ +/* non-terminal status codes */ +#define V_MSG_INFO 110 /* info message attached */ +#define V_MSG_ERR 111 /* error message attached (null for generic) */ +#define V_PUT_USER 112 /* user name attached; only with pam & no user send */ +#define V_CHTOK 113 /* password expired; change now */ +#define V_CHTOK_AUTH 114 /* password expired; change now, but authenticate first */ +#define V_PRE_OK 115 /* authentication succeeded, continue with password change */ +/* queries */ +#define V_GET_TEXT 200 /* str prompt, int echo, int ndelay; str return, int tag */ +# define V_IS_SECRET 1 +# define V_IS_USER 2 +# define V_IS_PASSWORD 4 +# define V_IS_OLDPASSWORD 8 +# define V_IS_NEWPASSWORD 16 +#define V_GET_BINARY 201 /* array prompt, int ndelay; array return */ + +/* + * Config/Runtime data keys + */ +#define C_WHO_MASK 0x00ff0000 /* Non-zero for proprietary extensions (see manufacturer table [to be written]) */ +#define C_TYPE_MASK 0x0f000000 /* Type of the value */ +# define C_TYPE_INT 0x00000000 /* Integer */ +# define C_TYPE_STR 0x01000000 /* String */ +# define C_TYPE_ARGV 0x02000000 /* 0-terminated Array of Strings */ +# define C_TYPE_ARR 0x03000000 /* Array (only when XDCMP is enabled) */ +#define C_PRIVATE 0xf0000000 /* Private, don't make it visible to interfaces! */ + +/* display variables */ +#define C_isLocal (C_TYPE_INT | 0x200) +#define C_hasConsole (C_TYPE_INT | 0x201) +#define C_isAuthorized (C_TYPE_INT | 0x202) + +/** + ** for struct display + **/ + +#define d_location 1 +#define dLocal 1 /* server runs on local host */ +#define dForeign 0 /* server runs on remote host */ + +#define d_lifetime 6 +#define dPermanent 4 /* display restarted when session exits */ +#define dReserve 2 /* display not restarted when session exits */ +#define dTransient 0 /* display removed when session exits */ + +#ifdef XDMCP +#define d_origin 8 +#else +#define d_origin 0 /* clever, huh? :) */ +#endif +#define dFromXDMCP 8 /* started with XDMCP */ +#define dFromFile 0 /* started via entry in servers file */ + +#ifdef XDMCP +/** + ** for xdmcp acls + **/ + +/* + * flags in acl entries + */ +#define a_notAllowed 1 /* both direct and indirect */ +#define a_notBroadcast 2 /* only direct */ +#define a_useChooser 2 /* only indirect */ + +/* + * type of host entries + */ +#define HOST_ALIAS 0 +#define HOST_ADDRESS 1 +#define HOST_PATTERN 2 +#define HOST_BROADCAST 3 + +#endif + +#endif /* GREET_H */ diff --git a/kdm/backend/inifile.c b/kdm/backend/inifile.c new file mode 100644 index 000000000..b5426de75 --- /dev/null +++ b/kdm/backend/inifile.c @@ -0,0 +1,255 @@ +/* + +Copyright 2003 Oswald Buddenhagen <[email protected]> + +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 + * + * load, save and manipulate ini-style config files + */ + +#include "dm.h" +#include "dm_error.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> + +char * +iniLoad( const char *fname ) +{ + char *data; + int fd, len; + struct stat st; + + if ((fd = open( fname, O_RDONLY | O_NONBLOCK )) < 0) { + Debug( "cannot open ini-file %\"s: %m", fname ); + return 0; + } + if (fstat( fd, &st ) || !S_ISREG( st.st_mode )) { + LogWarn( "Ini-file %\"s is no regular file\n", fname ); + close( fd ); + return 0; + } + if (st.st_size >= 0x10000) { + LogWarn( "Ini-file %\"s is too big\n", fname ); + close( fd ); + return 0; + } + len = st.st_size; + if (!(data = Malloc( len + 2 ))) { + close( fd ); + return 0; + } + if (read( fd, data, len ) != len) { + Debug( "cannot read ini-file %\"s: %m", fname ); + free( data ); + close( fd ); + return 0; + } + close( fd ); + if (data[len - 1] != '\n') + data[len++] = '\n'; + data[len] = 0; + return data; +} + +int +iniSave( const char *data, const char *fname ) +{ + int fd, cnt, len; + + if ((fd = open( fname, O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, 0600 )) < 0) { + Debug( "cannot create ini-file %\"s: %m", fname ); + return 0; + } + len = strlen( data ); + if ((cnt = write( fd, data, len )) == len) { + close( fd ); + return 1; + } + if (cnt == -1) + Debug( "cannot write ini-file %\"s: %m", fname ); + else + Debug( "cannot write ini-file %\"s: partial write", fname ); + close( fd ); + return 0; +} + +#define apparr(d,s,n) do { memcpy (d, s, n); d += n; } while(0) +#define appbyte(d,b) *d++ = b + +char * +iniEntry( char *data, const char *section, const char *key, const char *value ) +{ + char *p = data, *secinsert = 0, *pastinsert = 0, *cb, *ce, *ndata; + const char *t; + int insect = 0, ll, sl, kl, vl, len, nlen; + + if (p) { + while (*p) { + for (; *p == ' ' || *p == '\t'; p++); + if (*p == '\n') { + p++; + continue; + } + if (*p == '[') { + for (t = section; *++p == *t; t++); + insect = !*t && *p == ']'; + } else if (insect) { + for (t = key; *p == *t; t++, p++); + for (; *p == ' ' || *p == '\t'; p++); + if (!*t && *p == '=') { + for (p++; *p == ' ' || *p == '\t'; p++); + cb = p; + while (*p++ != '\n'); + ce = p; + if (value) { + ll = sl = kl = 0; + len = (ce - data) + strlen( ce ); + goto insert; + } else { + for (ce--; ce != cb && (*(ce - 1) == ' ' || *(ce - 1) == '\t'); ce--); + if (!StrNDup( &p, cb, ce - cb )) + return 0; + return p; + } + } + } + for (; *p != '\n'; p++); + p++; + if (insect) + secinsert = p; + else + pastinsert = p; + } + } + if (!value) + return 0; + len = p - data; + if (secinsert) { + ce = cb = secinsert; + sl = ll = 0; + } else { + sl = strlen( section ) + 3; + if (pastinsert) { + ce = cb = pastinsert; + ll = 1; + } else { + ce = cb = data; + ll = 0; + } + } + kl = strlen( key ) + 1; + insert: + vl = strlen( value ); + nlen = len - (ce - cb) + ll + sl + kl + vl + 1; + if (!(p = ndata = Malloc( nlen + 1 ))) + return data; + apparr( p, data, cb - data ); + if (kl) { + if (sl) { + if (ll) + appbyte( p, '\n' ); + appbyte( p, '[' ); + apparr( p, section, sl - 3 ); + appbyte( p, ']' ); + appbyte( p, '\n' ); + } + apparr( p, key, kl - 1 ); + appbyte( p, '=' ); + } + apparr( p, value, vl ); + appbyte( p, '\n' ); + if (data) { + apparr( p, ce, len - (ce - data) ); + free( data ); + } + appbyte( p, 0 ); + return ndata; +} + +char * +iniMerge( char *data, const char *newdata ) +{ + const char *p, *cb, *ce; + char *section = 0, *key, *value; + + if (!newdata) + return data; + for (p = newdata;;) { + for (; *p == ' ' || *p == '\t'; p++); + if (!*p) + break; + if (*p == '\n') { + p++; + continue; + } + if (*p == '#') { + for (p++; *p != '\n'; p++) + if (!*p) + goto bail; + p++; + continue; + } + if (*p == '[') { + cb = ++p; + for (; *p != ']'; p++) + if (!*p || *p == '\n') /* missing ] */ + goto bail; + if (!ReStrN( §ion, cb, p - cb )) + break; + p++; + } else { + cb = p; + for (; *p != '='; p++) + if (!*p || *p == '\n') /* missing = */ + goto bail; + for (ce = p; ce != cb && (*(ce - 1) == ' ' || *(ce - 1) == '\t'); ce--); + if (!StrNDup( &key, cb, ce - cb )) + break; + for (p++; *p == ' ' || *p == '\t'; p++); + cb = p; + for (; *p && *p != '\n'; p++); + for (ce = p; ce != cb && (*(ce - 1) == ' ' || *(ce - 1) == '\t'); ce--); + if (!StrNDup( &value, cb, ce - cb )) + break; + if (section) + data = iniEntry( data, section, key, value ); + free( value ); + free( key ); + } + } + bail: + if (section) + free( section ); + return data; +} diff --git a/kdm/backend/krb5auth.c b/kdm/backend/krb5auth.c new file mode 100644 index 000000000..16b640a35 --- /dev/null +++ b/kdm/backend/krb5auth.c @@ -0,0 +1,248 @@ +/* + +Copyright 1994, 1998 The Open Group +Copyright 2003 Oswald Buddenhagen <[email protected]> + +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: Stephen Gildea, The Open Group + * + * generate Kerberos Version 5 authorization records + */ + +#include <config.h> + +#ifdef K5AUTH + +#include "dm.h" +#include "dm_auth.h" +#include "dm_error.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#include <krb5/krb5.h> + +static krb5_context ctx; + +/*ARGSUSED*/ +void +Krb5InitAuth( unsigned short name_len ATTR_UNUSED, const char *name ATTR_UNUSED ) +{ + if (krb5_init_context( &ctx )) + LogError( "Error while initializing Krb5 context\n" ); +} + +/* + * Returns malloc'ed string that is the credentials cache name. + * name should be freed by caller. + */ +static char * +Krb5CCacheName( const char *dname ) +{ + char *name; + const char *tmpdir; + int dnl, nl; + + tmpdir = getenv( "TMPDIR" ); + if (!tmpdir) + tmpdir = "/tmp"; + dnl = strlen( dname ); + name = Malloc( strlen( tmpdir ) + dnl + 20 ); + if (!name) + return NULL; + nl = sprintf( name, "FILE:%s/K5C", tmpdir ); + CleanUpFileName( dname, name + nl, dnl + 1 ); + return name; +} + +Xauth * +Krb5GetAuthFor( unsigned short namelen, const char *name, const char *dname ) +{ + Xauth *new; + char *filename; + + if (!(new = (Xauth *)Malloc( sizeof(*new) ))) + return (Xauth *)0; + new->family = FamilyWild; + new->address_length = 0; + new->address = 0; + new->number_length = 0; + new->number = 0; + + if (dname) { + if (!(filename = Krb5CCacheName( dname ))) { + free( (char *)new ); + return (Xauth *)0; + } + new->data = 0; + if (!StrApp( &new->data, "UU:", filename, (char *)0 )) { + free( filename ); + free( (char *)new ); + return (Xauth *)0; + } + free( filename ); + new->data_length = strlen( new->data ); + } else { + new->data = NULL; + new->data_length = 0; + } + + if (!(new->name = (char *)Malloc( namelen ))) { + free( (char *)new->data ); + free( (char *)new ); + return (Xauth *)0; + } + memmove( new->name, name, namelen ); + new->name_length = namelen; + return new; +} + + +Xauth * +Krb5GetAuth( unsigned short namelen, const char *name ) +{ + return Krb5GetAuthFor( namelen, name, NULL ); +} + + +static krb5_error_code +Krb5DisplayCCache( const char *dname, krb5_ccache *ccache_return, char **name ) +{ + char *ccname; + krb5_error_code code; + + if (!(ccname = Krb5CCacheName( dname ))) + return ENOMEM; + Debug( "resolving Kerberos cache %s\n", ccname ); + if ((code = krb5_cc_resolve( ctx, ccname, ccache_return )) || !name) + free( ccname ); + else + *name = ccname; + return code; +} + +char * +Krb5Init( const char *user, const char *passwd, const char *dname ) +{ + krb5_error_code code; + krb5_get_init_creds_opt options; + krb5_principal me; + krb5_creds my_creds; + krb5_ccache ccache; + char *ccname; + + if (!ctx) + return 0; + + if ((code = krb5_parse_name( ctx, user, &me ))) { + LogError( "%s while parsing Krb5 user %\"s\n", + error_message( code ), user ); + return 0; + } + + krb5_get_init_creds_opt_init( &options ); + /*krb5_get_init_creds_opt_set_tkt_life (&options, 60*60*8);*/ /* 8 hours */ + + if ((code = krb5_get_init_creds_password( ctx, &my_creds, + me, /* principal */ + (char * /* for MIT */) passwd, + 0, /* prompter */ + 0, /* prompter ctx */ + 0, /* start time delta */ + 0, /* service */ + &options ))) + { + char *my_name = NULL; + int code2 = krb5_unparse_name( ctx, me, &my_name ); + if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) + LogError( "Password incorrect for Krb5 principal %\"s\n", + code2 ? user : my_name ); + else + LogError( "%s while getting initial Krb5 credentials for %\"s\n", + error_message( code ), code2 ? user : my_name ); + if (my_name) + free( my_name ); + goto err3; + } + + if ((code = Krb5DisplayCCache( dname, &ccache, &ccname ))) { + LogError( "%s while getting Krb5 ccache for %\"s\n", + error_message( code ), dname ); + goto err2; + } + + if ((code = krb5_cc_initialize( ctx, ccache, me ))) { + LogError( "%s while initializing Krb5 cache %\"s\n", + error_message( code ), ccname ); + goto err1; + } + + if ((code = krb5_cc_store_cred( ctx, ccache, &my_creds ))) { + LogError( "%s while storing Krb5 credentials to cache %\"s\n", + error_message( code ), ccname ); + err1: + krb5_cc_close( ctx, ccache ); + free( ccname ); + err2: + krb5_free_cred_contents( ctx, &my_creds ); + err3: + krb5_free_principal( ctx, me ); + return 0; + } + + krb5_cc_close( ctx, ccache ); + krb5_free_cred_contents( ctx, &my_creds ); + krb5_free_principal( ctx, me ); + return ccname; +} + +void +Krb5Destroy( const char *dname ) +{ + krb5_error_code code; + krb5_ccache ccache; + + if (!ctx) + return; + + if ((code = Krb5DisplayCCache( dname, &ccache, 0 ))) + LogError( "%s while getting Krb5 ccache to destroy\n", + error_message( code ) ); + else { + if ((code = krb5_cc_destroy( ctx, ccache ))) { + if (code == KRB5_FCC_NOFILE) + Debug( "no Kerberos ccache file found to destroy\n" ); + else + LogError( "%s while destroying Krb5 credentials cache\n", + error_message( code ) ); + } else + Debug( "kerberos ccache destroyed\n" ); + } +} + +#endif diff --git a/kdm/backend/mitauth.c b/kdm/backend/mitauth.c new file mode 100644 index 000000000..fd18d41df --- /dev/null +++ b/kdm/backend/mitauth.c @@ -0,0 +1,87 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2003 Oswald Buddenhagen <[email protected]> + +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 + * + * generate authorization keys + * for MIT-MAGIC-COOKIE-1 type authorization + */ + +#include "dm.h" +#include "dm_auth.h" + +#define AUTH_DATA_LEN 16 /* bytes of authorization data */ +static char auth_name[256]; + +void +MitInitAuth( unsigned short name_len, const char *name ) +{ + if (name_len > 256) + name_len = 256; + memmove( auth_name, name, name_len ); +} + +Xauth * +MitGetAuth( unsigned short namelen, const char *name ) +{ + Xauth *new; + new = (Xauth *)Malloc( sizeof(Xauth) ); + + if (!new) + return (Xauth *)0; + new->family = FamilyWild; + new->address_length = 0; + new->address = 0; + new->number_length = 0; + new->number = 0; + + new->data = (char *)Malloc( AUTH_DATA_LEN ); + if (!new->data) { + free( (char *)new ); + return (Xauth *)0; + } + new->name = (char *)Malloc( namelen ); + if (!new->name) { + free( (char *)new->data ); + free( (char *)new ); + return (Xauth *)0; + } + memmove( (char *)new->name, name, namelen ); + new->name_length = namelen; + if (!GenerateAuthData( new->data, AUTH_DATA_LEN )) { + free( (char *)new->name ); + free( (char *)new->data ); + free( (char *)new ); + return (Xauth *)0; + } + new->data_length = AUTH_DATA_LEN; + return new; +} diff --git a/kdm/backend/netaddr.c b/kdm/backend/netaddr.c new file mode 100644 index 000000000..349a53528 --- /dev/null +++ b/kdm/backend/netaddr.c @@ -0,0 +1,219 @@ +/* + +Copyright 1991, 1998 The Open Group +Copyright 2002 Sun Microsystems, Inc. All rights reserved. + +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 + * + * netaddr.c - Interpretation of XdmcpNetaddr object. + */ + +#include "dm.h" +#include "dm_socket.h" +#include "dm_error.h" + +/* given an char *, returns the socket protocol family used, + e.g., AF_INET */ + +int +NetaddrFamily( char *netaddrp ) +{ +#ifdef STREAMSCONN + short family = *(short *)netaddrp; + return family; +#else + return ((struct sockaddr *)netaddrp)->sa_family; +#endif +} + + +/* given an char *, returns a pointer to the TCP/UDP port used + and sets *lenp to the length of the address + or 0 if not using TCP or UDP. */ + +char * +NetaddrPort( char *netaddrp, int *lenp ) +{ +#ifdef STREAMSCONN + *lenp = 2; + return netaddrp+2; +#else + switch (NetaddrFamily( netaddrp )) + { + case AF_INET: + *lenp = 2; + return (char *)&(((struct sockaddr_in *)netaddrp)->sin_port); +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: + *lenp = 2; + return (char *)&(((struct sockaddr_in6 *)netaddrp)->sin6_port); +#endif + default: + *lenp = 0; + return NULL; + } +#endif +} + + +/* given an char *, returns a pointer to the network address + and sets *lenp to the length of the address */ + +char * +NetaddrAddress( char *netaddrp, int *lenp ) +{ +#ifdef STREAMSCONN + *lenp = 4; + return netaddrp+4; +#else + switch (NetaddrFamily( netaddrp )) { +#ifdef UNIXCONN + case AF_UNIX: + *lenp = strlen( ((struct sockaddr_un *)netaddrp)->sun_path ); + return (char *)(((struct sockaddr_un *)netaddrp)->sun_path); +#endif +#ifdef TCPCONN + case AF_INET: + *lenp = sizeof(struct in_addr); + return (char *)&(((struct sockaddr_in *)netaddrp)->sin_addr); +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: + { + struct in6_addr *a = &(((struct sockaddr_in6 *)netaddrp)->sin6_addr); + if (IN6_IS_ADDR_V4MAPPED( a )) { + *lenp = sizeof(struct in_addr); + return ((char *)&(a->s6_addr))+12; + } else { + *lenp = sizeof(struct in6_addr); + return (char *)&(a->s6_addr); + } + } +#endif +#endif +#ifdef DNETCONN + case AF_DECnet: + *lenp = sizeof(struct dn_naddr); + return (char *)&(((struct sockaddr_dn *)netaddrp)->sdn_add); +#endif +#ifdef AF_CHAOS + case AF_CHAOS: +#endif + default: + *lenp = 0; + return NULL; + } +#endif /* STREAMSCONN else */ +} + + +/* given an char *, sets *addr to the network address used and + sets *len to the number of bytes in addr. + Returns the X protocol family used, e.g., FamilyInternet */ + +int +ConvertAddr( char *saddr, int *len, char **addr ) +{ + int retval; + + if (len == NULL) + return -1; + *addr = NetaddrAddress( saddr, len ); +#ifdef STREAMSCONN + /* kludge */ + if (NetaddrFamily( saddr ) == 2) + retval = FamilyInternet; +#else + switch (NetaddrFamily( saddr )) { +#ifdef AF_UNSPEC + case AF_UNSPEC: + retval = FamilyLocal; + break; +#endif +#ifdef AF_UNIX +#ifndef __hpux + case AF_UNIX: + retval = FamilyLocal; + break; +#endif +#endif +#ifdef TCPCONN + case AF_INET: + retval = FamilyInternet; + break; +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: + if (*len == sizeof(struct in_addr)) + retval = FamilyInternet; + else + retval = FamilyInternet6; + break; +#endif +#endif +#ifdef DNETCONN + case AF_DECnet: + retval = FamilyDECnet; + break; +#endif +#ifdef AF_CHAOS + case AF_CHAOS: + retval = FamilyChaos; + break; +#endif + default: + retval = -1; + break; + } +#endif /* STREAMSCONN else */ + Debug( "ConvertAddr returning %d for family %d\n", retval, + NetaddrFamily( saddr ) ); + return retval; +} + +#ifdef XDMCP +int +addressEqual( char *a1, int len1, char *a2, int len2 ) +{ + int partlen1, partlen2; + char *part1, *part2; + + if (len1 != len2) + return FALSE; + if (NetaddrFamily( a1 ) != NetaddrFamily( a2 )) + return FALSE; + part1 = NetaddrPort( a1, &partlen1 ); + part2 = NetaddrPort( a2, &partlen2 ); + if (partlen1 != partlen2 || memcmp( part1, part2, partlen1 ) != 0) + return FALSE; + part1 = NetaddrAddress( a1, &partlen1 ); + part2 = NetaddrAddress( a2, &partlen2 ); + if (partlen1 != partlen2 || memcmp( part1, part2, partlen1 ) != 0) + return FALSE; + return TRUE; +} +#endif diff --git a/kdm/backend/policy.c b/kdm/backend/policy.c new file mode 100644 index 000000000..cabee7088 --- /dev/null +++ b/kdm/backend/policy.c @@ -0,0 +1,278 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2001 Oswald Buddenhagen <[email protected]> + +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 + * + * policy.c. Implement site-dependent policy for XDMCP connections + */ + +#include <config.h> + +#ifdef XDMCP + +#include "dm.h" +#include "dm_auth.h" +#include "dm_socket.h" + +static ARRAY8 noAuthentication = { (CARD16)0, (CARD8Ptr) 0 }; + +typedef struct _XdmAuth { + ARRAY8 authentication; + ARRAY8 authorization; +} XdmAuthRec, *XdmAuthPtr; + +static XdmAuthRec auth[] = { +#ifdef HASXDMAUTH +{ {(CARD16)20, (CARD8 *)"XDM-AUTHENTICATION-1"}, + {(CARD16)19, (CARD8 *)"XDM-AUTHORIZATION-1"}, +}, +#endif +{ {(CARD16)0, (CARD8 *)0}, + {(CARD16)0, (CARD8 *)0}, +} +}; + +#define NumAuth as(auth) + +ARRAY8Ptr +ChooseAuthentication( ARRAYofARRAY8Ptr authenticationNames ) +{ + int i, j; + + for (i = 0; i < (int)authenticationNames->length; i++) + for (j = 0; j < NumAuth; j++) + if (XdmcpARRAY8Equal( &authenticationNames->data[i], + &auth[j].authentication )) + return &authenticationNames->data[i]; + return &noAuthentication; +} + +int +CheckAuthentication( + struct protoDisplay *pdpy ATTR_UNUSED, + ARRAY8Ptr displayID ATTR_UNUSED, + ARRAY8Ptr name ATTR_UNUSED, + ARRAY8Ptr data ATTR_UNUSED ) +{ +#ifdef HASXDMAUTH + if (name->length && !memcmp( (char *)name->data, "XDM-AUTHENTICATION-1", 20 )) + return XdmCheckAuthentication( pdpy, displayID, name, data ); +#endif + return TRUE; +} + +int +SelectAuthorizationTypeIndex( ARRAY8Ptr authenticationName, + ARRAYofARRAY8Ptr authorizationNames ) +{ + int i, j; + + for (j = 0; j < NumAuth; j++) + if (XdmcpARRAY8Equal( authenticationName, + &auth[j].authentication )) + break; + if (j < NumAuth) + for (i = 0; i < (int)authorizationNames->length; i++) + if (XdmcpARRAY8Equal( &authorizationNames->data[i], + &auth[j].authorization )) + return i; + for (i = 0; i < (int)authorizationNames->length; i++) + if (ValidAuthorization( authorizationNames->data[i].length, + (char *)authorizationNames->data[i].data )) + return i; + return -1; +} + + +/*#define WILLING_INTERNAL*/ + +#ifdef WILLING_INTERNAL +/* Report the loadavg to chooser. Nice feature ... + * + * Wed Mar 10 1999 -- Steffen Hansen + */ +static void +Willing_msg( char *mbuf ) +{ +#ifdef __linux__ + int fd; + int numcpu; + const char *fail_msg = "Willing to manage"; + FILE *f; + float load[3]; + float mhz = 0.0; + char buf[1024]; + + fd = open( "/proc/loadavg", O_RDONLY ); + if (fd == -1) { + sprintf( mbuf, fail_msg ); + return; + } else if (read( fd, buf, 100 ) < 4) { + close( fd ); + sprintf( mbuf, fail_msg ); + return; + } + close( fd ); + + sscanf( buf, "%f %f %f", &load[0], &load[1], &load[2] ); + sprintf( mbuf, "Available (load: %0.2f, %0.2f, %0.2f)", + load[0], load[1], load[2] ); + + numcpu = 0; + + if (!(f = fopen( "/proc/cpuinfo", "r" ))) + return; + + while (fGets( buf, sizeof(buf), f ) != -1) { + float m; + if (sscanf( buf, "cpu MHz : %f", &m )) { + numcpu++; + mhz = m; + } + } + + fclose( f ); + + if (numcpu) { + if (numcpu > 1) + sprintf( buf, " %d*%0.0f MHz", numcpu, mhz ); + else + sprintf( buf, " %0.0f MHz", mhz ); + + strncat( mbuf, buf, 256 ); + + mbuf[255] = 0; + } +#elif HAVE_GETLOADAVG /* !__linux__ */ +#ifdef __GNUC__ +# warning This code is untested... +#endif + double load[3]; + getloadavg( load, 3 ); + sprintf( mbuf, "Available (load: %0.2f, %0.2f, %0.2f)", load[0], + load[1], load[2] ); +#else /* !__linux__ && !GETLOADAVG */ + strcpy( mbuf, "Willing to manage" ); +#endif +} +#endif + +/*ARGSUSED*/ +int +Willing( ARRAY8Ptr addr, CARD16 connectionType, + ARRAY8Ptr authenticationName ATTR_UNUSED, + ARRAY8Ptr status, xdmOpCode type ) +{ + int ret; + char statusBuf[256]; + static time_t lastscan; + + if (autoRescan && lastscan + 15 < now) { + lastscan = now; + ScanAccessDatabase( FALSE ); + } + ret = AcceptableDisplayAddress( addr, connectionType, type ); + if (!ret) + sprintf( statusBuf, "Display not authorized to connect" ); + else { + if (*willing) { + FILE *fd; + int len, ok = 0; + if ((fd = popen( willing, "r" ))) { + for (;;) { + if ((len = fGets( statusBuf, sizeof(statusBuf), fd )) != -1) { + if (len) { + ok = 1; + break; + } + } + if (feof( fd ) || errno != EINTR) + break; + } + pclose( fd ); + } + if (!ok) + sprintf( statusBuf, "Willing, but %.*s failed", + sizeof(statusBuf) - 21, willing ); + } else +#ifdef WILLING_INTERNAL + Willing_msg( statusBuf ); +#else + strcpy( statusBuf, "Willing to manage" ); +#endif + } + status->length = strlen( statusBuf ); + status->data = (CARD8Ptr) Malloc( status->length ); + if (!status->data) + status->length = 0; + else + memmove( status->data, statusBuf, status->length ); + return ret; +} + +/*ARGSUSED*/ +ARRAY8Ptr +Accept( struct sockaddr *from ATTR_UNUSED, int fromlen ATTR_UNUSED, + CARD16 displayNumber ATTR_UNUSED ) +{ + return 0; +} + +/*ARGSUSED*/ +int +SelectConnectionTypeIndex( ARRAY16Ptr connectionTypes, + ARRAYofARRAY8Ptr connectionAddresses ATTR_UNUSED ) +{ + int i; + + /* + * Select one supported connection type + */ + + for (i = 0; i < connectionTypes->length; i++) { + switch (connectionTypes->data[i]) { + case FamilyLocal: +#if defined(TCPCONN) + case FamilyInternet: +# if defined(IPv6) && defined(AF_INET6) + case FamilyInternet6: +# endif /* IPv6 */ +#endif /* TCPCONN */ +#if defined(DNETCONN) + case FamilyDECnet: +#endif /* DNETCONN */ + return i; + } + } /* for */ + return -1; +} + +#endif /* XDMCP */ diff --git a/kdm/backend/printf.c b/kdm/backend/printf.c new file mode 100644 index 000000000..d7220642b --- /dev/null +++ b/kdm/backend/printf.c @@ -0,0 +1,872 @@ +/* + +Copyright 2001,2002,2004 Oswald Buddenhagen <[email protected]> + +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 + * + * printf.c - working horse of error.c + */ + +/* + * NOTE: this file is meant to be included, not linked, + * so it can be used in the helper programs without much voodoo. + */ + +/* ########## printf core implementation with some extensions ########## */ +/* + * How to use the extensions: + * - put ' or " in the flags field to quote a string with this char and + * escape special characters (only available, if PRINT_QUOTES is defined) + * - put \\ in the flags field to quote special characters and leading and + * trailing spaces (only available, if PRINT_QUOTES is defined) + * - arrays (only available, if PRINT_ARRAYS is defined) + * - the array modifier [ comes after the maximal field width specifier + * - the array length can be specified literally, with the '*' modifier + * (in which case an argument is expected) or will be automatically + * determined (stop values are -1 for ints and 0 for strings) + * - these modifiers expect their argument to be an in-line string quoted + * with an arbitrary character: + * - (, ) -> array pre-/suf-fix; default "" + * - <, > -> element pre-/suf-fix; default "" + * - | -> element separator; default " " + * - these modifiers expect no argument: + * - : -> print '<number of elements>: ' before an array + * - , -> short for |',' + * - { -> short for ('{')' }'<' '|'' + * - the pointer to the array is the last argument to the format + * - the %m conversion from syslog() is supported + */ + +/************************************************************** + * Partially stolen from OpenSSH's OpenBSD compat directory. + * (C) Patrick Powell, Brandon Long, Thomas Roessler, + * Michael Elkins, Ben Lindstrom + **************************************************************/ + +#include <ctype.h> +#include <string.h> +#include <stdarg.h> + +/* format flags - Bits */ +#define DP_F_MINUS (1 << 0) +#define DP_F_PLUS (1 << 1) +#define DP_F_SPACE (1 << 2) +#define DP_F_NUM (1 << 3) +#define DP_F_ZERO (1 << 4) +#define DP_F_UPCASE (1 << 5) +#define DP_F_UNSIGNED (1 << 6) +#define DP_F_SQUOTE (1 << 7) +#define DP_F_DQUOTE (1 << 8) +#define DP_F_BACKSL (1 << 9) +#define DP_F_ARRAY (1 << 10) +#define DP_F_COLON (1 << 11) + +/* Conversion Flags */ +#define DP_C_INT 0 +#define DP_C_BYTE 1 +#define DP_C_SHORT 2 +#define DP_C_LONG 3 +#define DP_C_STR 10 + +typedef void (*OutCh)( void *bp, char c ); + + +static void +fmtint( OutCh dopr_outch, void *bp, + long value, int base, int min, int max, int flags ) +{ + const char *ctab; + unsigned long uvalue; + int signvalue = 0; + int place = 0; + int spadlen = 0; /* amount to space pad */ + int zpadlen = 0; /* amount to zero pad */ + char convert[20]; + + if (max < 0) + max = 0; + + uvalue = value; + + if (!(flags & DP_F_UNSIGNED)) { + if (value < 0) { + signvalue = '-'; + uvalue = -value; + } else if (flags & DP_F_PLUS) /* Do a sign (+/i) */ + signvalue = '+'; + else if (flags & DP_F_SPACE) + signvalue = ' '; + } + + ctab = (flags & DP_F_UPCASE) ? "0123456789ABCDEF" : "0123456789abcdef"; + do { + convert[place++] = ctab[uvalue % (unsigned)base]; + uvalue = uvalue / (unsigned)base; + } while (uvalue); + + zpadlen = max - place; + spadlen = min - (max > place ? max : place) - + (signvalue ? 1 : 0) - ((flags & DP_F_NUM) ? 2 : 0); + if (zpadlen < 0) + zpadlen = 0; + if (spadlen < 0) + spadlen = 0; + if (flags & DP_F_ZERO) { + zpadlen = zpadlen > spadlen ? zpadlen : spadlen; + spadlen = 0; + } + if (flags & DP_F_MINUS) + spadlen = -spadlen; /* Left Justifty */ + + + /* Spaces */ + while (spadlen > 0) { + dopr_outch( bp, ' ' ); + --spadlen; + } + + /* Sign */ + if (signvalue) + dopr_outch( bp, signvalue ); + + /* Prefix */ + if (flags & DP_F_NUM) { + dopr_outch( bp, '0' ); + dopr_outch( bp, 'x' ); + } + + /* Zeros */ + if (zpadlen > 0) + while (zpadlen > 0) { + dopr_outch( bp, '0' ); + --zpadlen; + } + + /* Digits */ + while (place > 0) + dopr_outch( bp, convert[--place] ); + + /* Left Justified spaces */ + while (spadlen < 0) { + dopr_outch( bp, ' ' ); + ++spadlen; + } +} + +typedef struct { + const char *str; + size_t len; +} str_t; + +static void +putstr( OutCh dopr_outch, void *bp, str_t *st ) +{ + size_t pt; + + for (pt = 0; pt < st->len; pt++) + dopr_outch( bp, st->str[pt] ); +} + +static str_t _null_parents = { "(null)", 6 }; +#ifdef PRINT_ARRAYS +static str_t _null_dparents = { "((null))", 8 }; +#endif +#if defined(PRINT_QUOTES) || defined(PRINT_ARRAYS) +static str_t _null_caps = { "NULL", 4 }; +#endif + +static void +fmtstr( OutCh dopr_outch, void *bp, + const char *value, int flags, int min, int max ) +{ + int padlen, strln, curcol; +#ifdef PRINT_QUOTES + int lastcol; +#endif + char ch; + + if (!value) { +#ifdef PRINT_QUOTES + if (flags & (DP_F_SQUOTE | DP_F_DQUOTE)) + putstr( dopr_outch, bp, &_null_caps ); + else +#endif + putstr( dopr_outch, bp, &_null_parents ); + return; + } + + for (strln = 0; (unsigned)strln < (unsigned)max && value[strln]; strln++); + padlen = min - strln; + if (padlen < 0) + padlen = 0; + if (flags & DP_F_MINUS) + padlen = -padlen; /* Left Justify */ + + for (; padlen > 0; padlen--) + dopr_outch( bp, ' ' ); +#ifdef PRINT_QUOTES +# if 0 /* gcc's flow analyzer is not the smartest ... */ + lastcol = 0; +# endif + if (flags & DP_F_SQUOTE) + dopr_outch( bp, '\'' ); + else if (flags & DP_F_DQUOTE) + dopr_outch( bp, '"'); + else if (flags & DP_F_BACKSL) + for (lastcol = strln; lastcol && value[lastcol - 1] == ' '; lastcol--); +#endif + for (curcol = 0; curcol < strln; curcol++) { + ch = value[curcol]; +#ifdef PRINT_QUOTES + if (flags & (DP_F_SQUOTE | DP_F_DQUOTE | DP_F_BACKSL)) { + switch (ch) { + case '\r': ch = 'r'; break; + case '\n': ch = 'n'; break; + case '\t': ch = 't'; break; + case '\a': ch = 'a'; break; + case '\b': ch = 'b'; break; + case '\v': ch = 'v'; break; + case '\f': ch = 'f'; break; + default: + if (ch < 32 || + ((unsigned char)ch >= 0x7f && (unsigned char)ch < 0xa0)) + { + dopr_outch( bp, '\\' ); + fmtint( dopr_outch, bp, (unsigned char)ch, 8, 3, 3, DP_F_ZERO ); + continue; + } else { + if ((ch == '\'' && (flags & DP_F_SQUOTE)) || + (ch == '"' && (flags & DP_F_DQUOTE) ) || + (ch == ' ' && (flags & DP_F_BACKSL) && + (!curcol || curcol >= lastcol)) || + ch == '\\') + dopr_outch( bp, '\\' ); + dopr_outch( bp, ch ); + continue; + } + } + dopr_outch( bp, '\\' ); + } +#endif + dopr_outch( bp, ch ); + } +#ifdef PRINT_QUOTES + if (flags & DP_F_SQUOTE) + dopr_outch( bp, '\'' ); + else if (flags & DP_F_DQUOTE) + dopr_outch( bp, '"' ); +#endif + for (; padlen < 0; padlen++) + dopr_outch( bp, ' ' ); +} + +static void +DoPr( OutCh dopr_outch, void *bp, const char *format, va_list args ) +{ + const char *strvalue; +#ifdef PRINT_ARRAYS + str_t arpr, arsf, arepr, aresf, aresp, *arp; + void *arptr; +#endif + unsigned long value; + int radix, min, max, flags, cflags, errn; +#ifdef PRINT_ARRAYS + int arlen; + unsigned aridx; + char sch; +#endif + char ch; +#define NCHR if (!(ch = *format++)) return + +#if 0 /* gcc's flow analyzer is not the smartest ... */ +# ifdef PRINT_ARRAYS + arlen = 0; +# endif + radix = 0; +#endif + errn = errno; + for (;;) { + for (;;) { + NCHR; + if (ch == '%') + break; + dopr_outch (bp, ch); + } + flags = cflags = min = 0; + max = -1; + for (;;) { + NCHR; + switch (ch) { + case '#': flags |= DP_F_NUM; continue; + case '-': flags |= DP_F_MINUS; continue; + case '+': flags |= DP_F_PLUS; continue; + case ' ': flags |= DP_F_SPACE; continue; + case '0': flags |= DP_F_ZERO; continue; +#ifdef PRINT_QUOTES + case '"': flags |= DP_F_DQUOTE; continue; + case '\'': flags |= DP_F_SQUOTE; continue; + case '\\': flags |= DP_F_BACKSL; continue; +#endif + } + break; + } + for (;;) { + if (isdigit( (unsigned char)ch )) { + min = 10 * min + (ch - '0'); + NCHR; + continue; + } else if (ch == '*') { + min = va_arg( args, int ); + NCHR; + } + break; + } + if (ch == '.') { + max = 0; + for (;;) { + NCHR; + if (isdigit( (unsigned char)ch )) { + max = 10 * max + (ch - '0'); + continue; + } else if (ch == '*') { + max = va_arg( args, int ); + NCHR; + } + break; + } + } +#ifdef PRINT_ARRAYS + if (ch == '[') { + flags |= DP_F_ARRAY; + arlen = -1; + arpr.len = arsf.len = arepr.len = aresf.len = 0; + aresp.len = 1, aresp.str = " "; + for (;;) { + NCHR; + if (isdigit( (unsigned char)ch )) { + arlen = 0; + for (;;) { + arlen += (ch - '0'); + NCHR; + if (!isdigit( (unsigned char)ch )) + break; + arlen *= 10; + } + } + switch (ch) { + case ':': flags |= DP_F_COLON; continue; + case '*': arlen = va_arg( args, int ); continue; + case '(': arp = &arpr; goto rar; + case ')': arp = &arsf; goto rar; + case '<': arp = &arepr; goto rar; + case '>': arp = &aresf; goto rar; + case '|': arp = &aresp; + rar: + NCHR; + sch = ch; + arp->str = format; + do { + NCHR; + } while (ch != sch); + arp->len = format - arp->str - 1; + continue; + case ',': + aresp.len = 1, aresp.str = ","; + continue; + case '{': + aresp.len = 0, arpr.len = arepr.len = 1, arsf.len = 2; + arpr.str = "{", arepr.str = " ", arsf.str = " }"; + continue; + } + break; + } + } +#endif + for (;;) { + switch (ch) { + case 'h': + cflags = DP_C_SHORT; + NCHR; + if (ch == 'h') { + cflags = DP_C_BYTE; + NCHR; + } + continue; + case 'l': + cflags = DP_C_LONG; + NCHR; + continue; + } + break; + } + switch (ch) { + case '%': + dopr_outch( bp, ch ); + break; + case 'm': + fmtstr( dopr_outch, bp, strerror( errn ), flags, min, max ); + break; + case 'c': + dopr_outch( bp, va_arg( args, int ) ); + break; + case 's': +#ifdef PRINT_ARRAYS + cflags = DP_C_STR; + goto printit; +#else + strvalue = va_arg( args, char * ); + fmtstr( dopr_outch, bp, strvalue, flags, min, max ); + break; +#endif + case 'u': + flags |= DP_F_UNSIGNED; + case 'd': + case 'i': + radix = 10; + goto printit; + case 'X': + flags |= DP_F_UPCASE; + case 'x': + flags |= DP_F_UNSIGNED; + radix = 16; + printit: +#ifdef PRINT_ARRAYS + if (flags & DP_F_ARRAY) { + if (!(arptr = va_arg( args, void * ))) + putstr( dopr_outch, bp, + arpr.len ? &_null_caps : &_null_dparents ); + else { + if (arlen == -1) { + arlen = 0; + switch (cflags) { + case DP_C_STR: while (((char **)arptr)[arlen]) arlen++; break; + case DP_C_BYTE: while (((unsigned char *)arptr)[arlen] != (unsigned char)-1) arlen++; break; + case DP_C_SHORT: while (((unsigned short int *)arptr)[arlen] != (unsigned short int)-1) arlen++; break; + case DP_C_LONG: while (((unsigned long int *)arptr)[arlen] != (unsigned long int)-1) arlen++; break; + default: while (((unsigned int *)arptr)[arlen] != (unsigned int)-1) arlen++; break; + } + } + if (flags & DP_F_COLON) { + fmtint( dopr_outch, bp, (long)arlen, 10, 0, -1, DP_F_UNSIGNED ); + dopr_outch( bp, ':' ); + dopr_outch( bp, ' ' ); + } + putstr( dopr_outch, bp, &arpr ); + for (aridx = 0; aridx < (unsigned)arlen; aridx++) { + if (aridx) + putstr( dopr_outch, bp, &aresp ); + putstr( dopr_outch, bp, &arepr ); + if (cflags == DP_C_STR) { + strvalue = ((char **)arptr)[aridx]; + fmtstr( dopr_outch, bp, strvalue, flags, min, max ); + } else { + if (flags & DP_F_UNSIGNED) { + switch (cflags) { + case DP_C_BYTE: value = ((unsigned char *)arptr)[aridx]; break; + case DP_C_SHORT: value = ((unsigned short int *)arptr)[aridx]; break; + case DP_C_LONG: value = ((unsigned long int *)arptr)[aridx]; break; + default: value = ((unsigned int *)arptr)[aridx]; break; + } + } else { + switch (cflags) { + case DP_C_BYTE: value = ((signed char *)arptr)[aridx]; break; + case DP_C_SHORT: value = ((short int *)arptr)[aridx]; break; + case DP_C_LONG: value = ((long int *)arptr)[aridx]; break; + default: value = ((int *)arptr)[aridx]; break; + } + } + fmtint( dopr_outch, bp, value, radix, min, max, flags ); + } + putstr( dopr_outch, bp, &aresf ); + } + putstr( dopr_outch, bp, &arsf ); + } + } else { + if (cflags == DP_C_STR) { + strvalue = va_arg( args, char * ); + fmtstr( dopr_outch, bp, strvalue, flags, min, max ); + } else { +#endif + if (flags & DP_F_UNSIGNED) { + switch (cflags) { + case DP_C_LONG: value = va_arg( args, unsigned long int ); break; + default: value = va_arg( args, unsigned int ); break; + } + } else { + switch (cflags) { + case DP_C_LONG: value = va_arg( args, long int ); break; + default: value = va_arg( args, int ); break; + } + } + fmtint( dopr_outch, bp, value, radix, min, max, flags ); +#ifdef PRINT_ARRAYS + } + } +#endif + break; + case 'p': + value = (long)va_arg( args, void * ); + fmtint( dopr_outch, bp, value, 16, sizeof(long) * 2 + 2, + max, flags | DP_F_UNSIGNED | DP_F_ZERO | DP_F_NUM ); + break; + } + } +} + +/* ########## end of printf core implementation ########## */ + + +/* + * Logging function for xdm and helper programs. + */ +#ifndef NO_LOGGER + +#include <stdio.h> +#include <time.h> + +#ifdef USE_SYSLOG +# include <syslog.h> +# ifdef LOG_NAME +# define InitLog() openlog(LOG_NAME, LOG_PID, LOG_DAEMON) +# else +# define InitLog() openlog(prog, LOG_PID, LOG_DAEMON) +# endif +static int lognums[] = { LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERR, LOG_CRIT }; +#else +# define InitLog() while(0) +#endif + +static const char *lognams[] = { "debug", "info", "warning", "error", "panic" }; + +static void +logTime( char *dbuf ) +{ + time_t tim; + (void)time( &tim ); + strftime( dbuf, 20, "%b %e %H:%M:%S", localtime( &tim ) ); +} + +#if defined(LOG_DEBUG_MASK) || defined(USE_SYSLOG) +STATIC int debugLevel; +#endif + +#define OOMSTR "Out of memory. Expect problems.\n" + +STATIC void +LogOutOfMem( void ) +{ + static time_t last; + time_t tnow; + + time( &tnow ); + if (last + 100 > tnow) { /* don't log bursts */ + last = tnow; + return; + } + last = tnow; +#ifdef USE_SYSLOG + if (!(debugLevel & DEBUG_NOSYSLOG)) + syslog( LOG_CRIT, OOMSTR ); + else +#endif + { + int el; + char dbuf[24], sbuf[128]; + logTime( dbuf ); + el = sprintf( sbuf, "%s " +#ifdef LOG_NAME + LOG_NAME "[%ld]: " OOMSTR, dbuf, +#else + "%s[%ld]: " OOMSTR, dbuf, prog, +#endif + (long)getpid() ); + write( 2, sbuf, el ); + } +} + +typedef struct { + char *buf; + int clen, blen, type; + char lmbuf[128]; +} OCLBuf; + +static void +OutChLFlush( OCLBuf *oclbp ) +{ + if (oclbp->clen) { +#ifdef USE_SYSLOG + if (!(debugLevel & DEBUG_NOSYSLOG)) + syslog( lognums[oclbp->type], "%.*s", oclbp->clen, oclbp->buf ); + else +#endif + { + oclbp->buf[oclbp->clen] = '\n'; + write( 2, oclbp->buf, oclbp->clen + 1 ); + } + oclbp->clen = 0; + } +} + +static void +OutChL( void *bp, char c ) +{ + OCLBuf *oclbp = (OCLBuf *)bp; + char *nbuf; + int nlen; + + if (c == '\n') + OutChLFlush( oclbp ); + else { + if (oclbp->clen >= oclbp->blen - 1) { + if (oclbp->buf == oclbp->lmbuf) { + OutChLFlush( oclbp ); + oclbp->buf = 0; + oclbp->blen = 0; + } + nlen = oclbp->blen * 3 / 2 + 128; + nbuf = Realloc( oclbp->buf, nlen ); + if (nbuf) { + oclbp->buf = nbuf; + oclbp->blen = nlen; + } else { + OutChLFlush( oclbp ); + oclbp->buf = oclbp->lmbuf; + oclbp->blen = sizeof(oclbp->lmbuf); + } + } +#ifdef USE_SYSLOG + if (!oclbp->clen && (debugLevel & DEBUG_NOSYSLOG)) { +#else + if (!oclbp->clen) { +#endif + char dbuf[24]; + logTime( dbuf ); + oclbp->clen = sprintf( oclbp->buf, "%s " +#ifdef LOG_NAME + LOG_NAME "[%ld] %s: ", dbuf, +#else + "%s[%ld] %s: ", dbuf, prog, +#endif + (long)getpid(), lognams[oclbp->type] ); + } + oclbp->buf[oclbp->clen++] = c; + } +} + +static void +Logger( int type, const char *fmt, va_list args ) +{ + OCLBuf oclb; + + oclb.buf = 0; + oclb.blen = oclb.clen = 0; + oclb.type = type; + DoPr( OutChL, &oclb, fmt, args ); + /* no flush, every message is supposed to be \n-terminated */ + if (oclb.buf && oclb.buf != oclb.lmbuf) + free( oclb.buf ); +} + +#ifdef LOG_DEBUG_MASK +STATIC void +Debug( const char *fmt, ... ) +{ + if (debugLevel & LOG_DEBUG_MASK) { + va_list args; + int olderrno = errno; + va_start( args, fmt ); + Logger( DM_DEBUG, fmt, args ); + va_end( args ); + errno = olderrno; + } +} +#endif + +#ifndef LOG_NO_INFO +STATIC void +LogInfo( const char *fmt, ... ) +{ + va_list args; + + va_start( args, fmt ); + Logger( DM_INFO, fmt, args ); + va_end( args ); +} +#endif + +#ifndef LOG_NO_WARN +STATIC void +LogWarn( const char *fmt, ... ) +{ + va_list args; + + va_start( args, fmt ); + Logger( DM_WARN, fmt, args ); + va_end( args ); +} +#endif + +#ifndef LOG_NO_ERROR +STATIC void +LogError( const char *fmt, ... ) +{ + va_list args; + + va_start( args, fmt ); + Logger( DM_ERR, fmt, args ); + va_end( args ); +} +#endif + +#ifdef LOG_PANIC_EXIT +STATIC void +LogPanic( const char *fmt, ... ) +{ + va_list args; + + va_start( args, fmt ); + Logger( DM_PANIC, fmt, args ); + va_end( args ); + exit( LOG_PANIC_EXIT ); +} +#endif + +#endif /* NO_LOGGER */ + +#ifdef NEED_FDPRINTF + +typedef struct { + char *buf; + int clen, blen, tlen; +} OCFBuf; + +static void +OutCh_OCF( void *bp, char c ) +{ + OCFBuf *ocfbp = (OCFBuf *)bp; + char *nbuf; + int nlen; + + ocfbp->tlen++; + if (ocfbp->clen >= ocfbp->blen) { + if (ocfbp->blen < 0) + return; + nlen = ocfbp->blen * 3 / 2 + 100; + nbuf = Realloc( ocfbp->buf, nlen ); + if (!nbuf) { + free( ocfbp->buf ); + ocfbp->blen = -1; + ocfbp->buf = 0; + ocfbp->clen = 0; + return; + } + ocfbp->blen = nlen; + ocfbp->buf = nbuf; + } + ocfbp->buf[ocfbp->clen++] = c; +} + +STATIC int +FdPrintf( int fd, const char *fmt, ... ) +{ + va_list args; + OCFBuf ocfb = { 0, 0, 0, -1 }; + + va_start( args, fmt ); + DoPr( OutCh_OCF, &ocfb, fmt, args ); + va_end( args ); + if (ocfb.buf) { + Debug( "FdPrintf %\".*s to %d\n", ocfb.clen, ocfb.buf, fd ); + (void)write( fd, ocfb.buf, ocfb.clen ); + free( ocfb.buf ); + } + return ocfb.tlen; +} + +#endif /* NEED_FDPRINTF */ + +#ifdef NEED_ASPRINTF + +typedef struct { + char *buf; + int clen, blen, tlen; +} OCABuf; + +static void +OutCh_OCA( void *bp, char c ) +{ + OCABuf *ocabp = (OCABuf *)bp; + char *nbuf; + int nlen; + + ocabp->tlen++; + if (ocabp->clen >= ocabp->blen) { + if (ocabp->blen < 0) + return; + nlen = ocabp->blen * 3 / 2 + 100; + nbuf = Realloc( ocabp->buf, nlen ); + if (!nbuf) { + free( ocabp->buf ); + ocabp->blen = -1; + ocabp->buf = 0; + ocabp->clen = 0; + return; + } + ocabp->blen = nlen; + ocabp->buf = nbuf; + } + ocabp->buf[ocabp->clen++] = c; +} + +STATIC int +VASPrintf( char **strp, const char *fmt, va_list args ) +{ + OCABuf ocab = { 0, 0, 0, -1 }; + + DoPr( OutCh_OCA, &ocab, fmt, args ); + OutCh_OCA( &ocab, 0 ); + *strp = Realloc( ocab.buf, ocab.clen ); + if (!*strp) + *strp = ocab.buf; + return ocab.tlen; +} + +STATIC int +ASPrintf( char **strp, const char *fmt, ... ) +{ + va_list args; + int len; + + va_start( args, fmt ); + len = VASPrintf( strp, fmt, args ); + va_end( args ); + return len; +} + +#endif /* NEED_ASPRINTF */ diff --git a/kdm/backend/process.c b/kdm/backend/process.c new file mode 100644 index 000000000..30dca096d --- /dev/null +++ b/kdm/backend/process.c @@ -0,0 +1,761 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2001-2004 Oswald Buddenhagen <[email protected]> + +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 + * + * subdaemon and external process management and communication + */ + +#include "dm.h" +#include "dm_error.h" + +#include <ctype.h> +#include <stdio.h> +#include <stdarg.h> +#include <signal.h> +#include <unistd.h> +#ifdef _POSIX_PRIORITY_SCHEDULING +# include <sched.h> +#endif + +extern char **environ; + + +SIGFUNC Signal( int sig, SIGFUNC handler ) +{ +#ifndef __EMX__ + struct sigaction sigact, osigact; + sigact.sa_handler = handler; + sigemptyset( &sigact.sa_mask ); +# ifdef SA_RESTART + sigact.sa_flags = SA_RESTART; +# else + sigact.sa_flags = 0; +# endif + sigaction( sig, &sigact, &osigact ); + return osigact.sa_handler; +#else + return signal( sig, handler ); +#endif +} + + +void +TerminateProcess( int pid, int sig ) +{ + kill( pid, sig ); +#ifdef SIGCONT + kill( pid, SIGCONT ); +#endif +} + + +static FD_TYPE CloseMask; +static int max = -1; + +void +RegisterCloseOnFork( int fd ) +{ + FD_SET( fd, &CloseMask ); + if (fd > max) + max = fd; +} + +void +ClearCloseOnFork( int fd ) +{ + FD_CLR( fd, &CloseMask ); +} + +void +CloseNClearCloseOnFork( int fd ) +{ + close( fd ); + FD_CLR( fd, &CloseMask ); +} + +static void +CloseOnFork( void ) +{ + int fd; + + for (fd = 0; fd <= max; fd++) + if (FD_ISSET( fd, &CloseMask )) + close( fd ); + FD_ZERO( &CloseMask ); + max = -1; +} + +int +Fork() +{ + int pid; + + sigset_t ss, oss; + sigfillset( &ss ); + sigprocmask( SIG_SETMASK, &ss, &oss ); + + if (!(pid = fork())) { +#ifdef SIGCHLD + (void)Signal( SIGCHLD, SIG_DFL ); +#endif + (void)Signal( SIGTERM, SIG_DFL ); + (void)Signal( SIGINT, SIG_IGN ); /* for -nodaemon */ + (void)Signal( SIGPIPE, SIG_DFL ); + (void)Signal( SIGALRM, SIG_DFL ); + (void)Signal( SIGHUP, SIG_DFL ); + sigemptyset( &ss ); + sigprocmask( SIG_SETMASK, &ss, NULL ); + CloseOnFork(); + return 0; + } + + sigprocmask( SIG_SETMASK, &oss, 0 ); + + return pid; +} + +int +Wait4( int pid ) +{ + waitType result; + + while (waitpid( pid, &result, 0 ) < 0) + if (errno != EINTR) { + Debug( "Wait4(%d) failed: %m\n", pid ); + return 0; + } + return waitVal( result ); +} + + +void +execute( char **argv, char **env ) +{ + Debug( "execute: %[s ; %[s\n", argv, env ); + execve( argv[0], argv, env ); + /* + * In case this is a shell script which hasn't been + * made executable (or this is a SYSV box), do + * a reasonable thing + */ + if (errno != ENOENT) { + char **newargv; + FILE *f; + int nu; + char program[1024]; + + /* + * emulate BSD kernel behaviour -- read + * the first line; check if it starts + * with "#!", in which case it uses + * the rest of the line as the name of + * program to run. Else use "/bin/sh". + */ + if (!(f = fopen( argv[0], "r" ))) + return; + if (!fGets( program, sizeof(program), f )) { + fclose( f ); + return; + } + fclose( f ); + if (!strncmp( program, "#!", 2 )) + newargv = parseArgs( 0, program + 2 ); + else + newargv = addStrArr( 0, "/bin/sh", 7 ); + if (!newargv) + return; + nu = arrLen( newargv ); + if (!(argv = xCopyStrArr( nu, argv ))) + return; + memcpy( argv, newargv, sizeof(char *) * nu ); + Debug( "shell script execution: %[s\n", argv ); + execve( argv[0], argv, env ); + } +} + +int +runAndWait( char **args, char **env ) +{ + int pid, ret; + + switch (pid = Fork()) { + case 0: + execute( args, env ); + LogError( "Can't execute %\"s: %m\n", args[0] ); + exit( 127 ); + case -1: + LogError( "Can't fork to execute %\"s: %m\n", args[0] ); + return 1; + } + ret = Wait4( pid ); + return waitVal( ret ); +} + +FILE * +pOpen( char **what, char m, int *pid ) +{ + int dp[2]; + + if (pipe( dp )) + return 0; + switch ((*pid = Fork())) { + case 0: + if (m == 'r') + dup2( dp[1], 1 ); + else + dup2( dp[0], 0 ); + close( dp[0] ); + close( dp[1] ); + execute( what, environ ); + LogError( "Can't execute %\"s: %m\n", what[0] ); + exit( 127 ); + case -1: + close( dp[0] ); + close( dp[1] ); + LogError( "Can't fork to execute %\"s: %m\n", what[0] ); + return 0; + } + if (m == 'r') { + close( dp[1] ); + return fdopen( dp[0], "r" ); + } else { + close( dp[0] ); + return fdopen( dp[1], "w" ); + } +} + +int +pClose( FILE *f, int pid ) +{ + fclose( f ); + return Wait4( pid ); +} + +char * +locate( const char *exe ) +{ + int len; + char *path, *pathe, *name, *thenam, nambuf[PATH_MAX+1]; + + if (!(path = getenv( "PATH" ))) { + LogError( "Can't execute %'s: $PATH not set.\n", exe ); + return 0; + } + len = strlen( exe ); + name = nambuf + PATH_MAX - len; + memcpy( name, exe, len + 1 ); + *--name = '/'; + do { + if (!(pathe = strchr( path, ':' ))) + pathe = path + strlen( path ); + len = pathe - path; + if (len && !(len == 1 && *path == '.')) { + thenam = name - len; + if (thenam >= nambuf) { + memcpy( thenam, path, len ); + if (!access( thenam, X_OK )) { + StrDup( &name, thenam ); + return name; + } + } + } + path = pathe; + } while (*path++ != '\0'); + LogError( "Can't execute %'s: not in $PATH.\n", exe ); + return 0; +} + + +static GTalk *curtalk; + +void +GSet( GTalk *tlk ) +{ + curtalk = tlk; +} + +int +GFork( GPipe *pajp, const char *pname, char *cname, + GPipe *ogp, char *cgname ) +{ + int opipe[2], ipipe[2], ogpipe[2], igpipe[2], pid; + + if (pipe( opipe )) + goto badp1; + if (pipe( ipipe )) + goto badp2; + if (ogp) { + if (pipe( ogpipe )) + goto badp3; + if (pipe( igpipe )) { + close( ogpipe[0] ); + close( ogpipe[1] ); + badp3: + close( ipipe[0] ); + close( ipipe[1] ); + badp2: + close( opipe[0] ); + close( opipe[1] ); + badp1: + LogError( "Cannot start %s, pipe() failed", cname ); + if (cname) + free( cname ); + return -1; + } + } + RegisterCloseOnFork( opipe[1] ); + RegisterCloseOnFork( ipipe[0] ); + if (ogp) { + RegisterCloseOnFork( ogpipe[1] ); + RegisterCloseOnFork( igpipe[0] ); + } + switch (pid = Fork()) { + case -1: + close( opipe[0] ); + close( ipipe[1] ); + CloseNClearCloseOnFork( opipe[1] ); + CloseNClearCloseOnFork( ipipe[0] ); + if (ogp) { + close( ogpipe[0] ); + close( igpipe[1] ); + CloseNClearCloseOnFork( ogpipe[1] ); + CloseNClearCloseOnFork( igpipe[0] ); + } + LogError( "Cannot start %s, fork() failed\n", cname ); + if (cname) + free( cname ); + return -1; + case 0: + pajp->wfd = ipipe[1]; + RegisterCloseOnFork( ipipe[1] ); + pajp->rfd = opipe[0]; + RegisterCloseOnFork( opipe[0] ); + pajp->who = (char *)pname; + if (ogp) { + ogp->wfd = igpipe[1]; + RegisterCloseOnFork( igpipe[1] ); + ogp->rfd = ogpipe[0]; + RegisterCloseOnFork( ogpipe[0] ); + ogp->who = (char *)pname; + } + break; + default: + close( opipe[0] ); + close( ipipe[1] ); + pajp->rfd = ipipe[0]; + pajp->wfd = opipe[1]; + pajp->who = cname; + if (ogp) { + close( ogpipe[0] ); + close( igpipe[1] ); + ogp->rfd = igpipe[0]; + ogp->wfd = ogpipe[1]; + ogp->who = cgname; + } + break; + } + return pid; +} + +int +GOpen( GProc *proc, char **argv, const char *what, char **env, char *cname, + GPipe *gp ) +{ + char **margv; + int pip[2]; + char coninfo[32]; + +/* ### GSet (proc->pipe); */ + if (proc->pid) { + LogError( "%s already running\n", cname ); + if (cname) + free( cname ); + return -1; + } + if (!(margv = xCopyStrArr( 1, argv ))) { + if (cname) + free( cname ); + return -1; + } + if (!StrApp( margv, progpath, what, (char *)0 )) { + free( margv ); + if (cname) + free( cname ); + return -1; + } + if (pipe( pip )) { + LogError( "Cannot start %s, pipe() failed\n", cname ); + if (cname) + free( cname ); + goto fail; + } + if (gp) { + ClearCloseOnFork( gp->rfd ); + ClearCloseOnFork( gp->wfd ); + } + proc->pid = GFork( &proc->pipe, 0, cname, 0, 0 ); + if (proc->pid) { + close( pip[1] ); + if (gp) { + RegisterCloseOnFork( gp->rfd ); + RegisterCloseOnFork( gp->wfd ); + } + } + switch (proc->pid) { + case -1: + fail1: + close( pip[0] ); + fail: + free( margv[0] ); + free( margv ); + return -1; + case 0: + (void)Signal( SIGPIPE, SIG_IGN ); + close( pip[0] ); + fcntl( pip[1], F_SETFD, FD_CLOEXEC ); + if (gp) + sprintf( coninfo, "CONINFO=%d %d %d %d", + proc->pipe.rfd, proc->pipe.wfd, gp->rfd, gp->wfd ); + else + sprintf( coninfo, "CONINFO=%d %d", + proc->pipe.rfd, proc->pipe.wfd ); + env = putEnv( coninfo, env ); + if (debugLevel & DEBUG_VALGRIND) { + char **nmargv = xCopyStrArr( 1, margv ); + nmargv[0] = locate( "valgrind" ); + execute( nmargv, env ); + } else if (debugLevel & DEBUG_STRACE) { + char **nmargv = xCopyStrArr( 1, margv ); + nmargv[0] = locate( "strace" ); + execute( nmargv, env ); + } else + execute( margv, env ); + write( pip[1], "", 1 ); + exit( 1 ); + default: + (void)Signal( SIGPIPE, SIG_IGN ); + if (Reader( pip[0], coninfo, 1 )) { + Wait4( proc->pid ); + LogError( "Cannot execute %\"s (%s)\n", margv[0], cname ); + GClosen (&proc->pipe); + goto fail1; + } + close( pip[0] ); + Debug( "started %s (%\"s), pid %d\n", cname, margv[0], proc->pid ); + free( margv[0] ); + free( margv ); + GSendInt( debugLevel ); + return 0; + } +} + +static void +iGClosen( GPipe *pajp ) +{ + CloseNClearCloseOnFork( pajp->rfd ); + CloseNClearCloseOnFork( pajp->wfd ); + pajp->rfd = pajp->wfd = -1; +} + +void +GClosen (GPipe *pajp) +{ + iGClosen( pajp ); + if (pajp->who) + free( pajp->who ); + pajp->who = 0; +} + +int +GClose (GProc *proc, GPipe *gp, int force) +{ + int ret; + + if (!proc->pid) { + Debug( "whoops, GClose while helper not running\n" ); + return 0; + } + iGClosen( &proc->pipe ); + if (gp) + GClosen (gp); + if (force) + TerminateProcess( proc->pid, SIGTERM ); + ret = Wait4( proc->pid ); + proc->pid = 0; + if (WaitSig( ret ) ? WaitSig( ret ) != SIGTERM : + (WaitCode( ret ) < EX_NORMAL || WaitCode( ret ) > EX_MAX)) + LogError( "Abnormal termination of %s, code %d, signal %d\n", + proc->pipe.who, WaitCode( ret ), WaitSig( ret ) ); + Debug( "closed %s\n", proc->pipe.who ); + if (proc->pipe.who) + free( proc->pipe.who ); + proc->pipe.who = 0; + return ret; +} + +static void ATTR_NORETURN +GErr( void ) +{ + Longjmp( curtalk->errjmp, 1 ); +} + +static void +GRead( void *buf, int len ) +{ + if (Reader( curtalk->pipe->rfd, buf, len ) != len) { + LogError( "Cannot read from %s\n", curtalk->pipe->who ); + GErr(); + } +} + +static void +GWrite( const void *buf, int len ) +{ + if (Writer( curtalk->pipe->wfd, buf, len ) != len) { + LogError( "Cannot write to %s\n", curtalk->pipe->who ); + GErr(); + } +#ifdef _POSIX_PRIORITY_SCHEDULING + if ((debugLevel & DEBUG_HLPCON)) + sched_yield(); +#endif +} + +void +GSendInt( int val ) +{ + GDebug( "sending int %d (%#x) to %s\n", val, val, curtalk->pipe->who ); + GWrite( &val, sizeof(val) ); +} + +int +GRecvInt() +{ + int val; + + GDebug( "receiving int from %s ...\n", curtalk->pipe->who ); + GRead( &val, sizeof(val) ); + GDebug( " -> %d (%#x)\n", val, val ); + return val; +} + +int +GRecvCmd( int *cmd ) +{ + GDebug( "receiving command from %s ...\n", curtalk->pipe->who ); + if (Reader( curtalk->pipe->rfd, cmd, sizeof(*cmd) ) == sizeof(*cmd)) { + GDebug( " -> %d\n", *cmd ); + return 1; + } + GDebug( " -> no data\n" ); + return 0; +} + +void +GSendArr( int len, const char *data ) +{ + GDebug( "sending array[%d] %02[*{hhx to %s\n", + len, len, data, curtalk->pipe->who ); + GWrite( &len, sizeof(len) ); + GWrite( data, len ); +} + +static char * +iGRecvArr( int *rlen ) +{ + int len; + char *buf; + + GRead( &len, sizeof(len) ); + *rlen = len; + GDebug( " -> %d bytes\n", len ); + if (!len) + return (char *)0; + if (!(buf = Malloc( len ))) + GErr(); + GRead( buf, len ); + return buf; +} + +char * +GRecvArr( int *rlen ) +{ + char *buf; + + GDebug( "receiving array from %s ...\n", curtalk->pipe->who ); + buf = iGRecvArr( rlen ); + GDebug( " -> %02[*{hhx\n", *rlen, buf ); + return buf; +} + +static int +iGRecvArrBuf( char *buf ) +{ + int len; + + GRead( &len, sizeof(len) ); + GDebug( " -> %d bytes\n", len ); + if (len) + GRead( buf, len ); + return len; +} + +int +GRecvArrBuf( char *buf ) +{ + int len; + + GDebug( "receiving already allocated array from %s ...\n", + curtalk->pipe->who ); + len = iGRecvArrBuf( buf ); + GDebug( " -> %02[*{hhx\n", len, buf ); + return len; +} + +int +GRecvStrBuf( char *buf ) +{ + int len; + + GDebug( "receiving already allocated string from %s ...\n", + curtalk->pipe->who ); + len = iGRecvArrBuf( buf ); + GDebug( " -> %\".*s\n", len, buf ); + return len; +} + +void +GSendStr( const char *buf ) +{ + int len; + + GDebug( "sending string %\"s to %s\n", buf, curtalk->pipe->who ); + if (buf) { + len = strlen( buf ) + 1; + GWrite( &len, sizeof(len) ); + GWrite( buf, len ); + } else + GWrite( &buf, sizeof(int) ); +} + +void +GSendNStr( const char *buf, int len ) +{ + int tlen = len + 1; + GDebug( "sending string %\".*s to %s\n", len, buf, curtalk->pipe->who ); + GWrite( &tlen, sizeof(tlen) ); + GWrite( buf, len ); + GWrite( "", 1 ); +} + +void +GSendStrN( const char *buf, int len ) +{ + if (buf) + GSendNStr( buf, StrNLen( buf, len ) ); + else + GSendStr( buf ); +} + +char * +GRecvStr() +{ + int len; + char *buf; + + GDebug( "receiving string from %s ...\n", curtalk->pipe->who ); + buf = iGRecvArr( &len ); + GDebug( " -> %\".*s\n", len, buf ); + return buf; +} + +static void +iGSendStrArr( int num, char **data ) +{ + char **cdata; + + GWrite( &num, sizeof(num) ); + for (cdata = data; --num >= 0; cdata++) + GSendStr( *cdata ); +} + +/* +void +GSendStrArr (int num, char **data) +{ + GDebug( "sending string array[%d] to %s\n", num, curtalk->pipe->who ); + iGSendStrArr( num, data ); +} +*/ + +char ** +GRecvStrArr( int *rnum ) +{ + int num; + char **argv, **cargv; + + GDebug( "receiving string array from %s ...\n", curtalk->pipe->who ); + GRead( &num, sizeof(num) ); + GDebug( " -> %d strings\n", num ); + *rnum = num; + if (!num) + return (char **)0; + if (!(argv = Malloc( num * sizeof(char *) ))) + GErr(); + for (cargv = argv; --num >= 0; cargv++) + *cargv = GRecvStr(); + return argv; +} + +void +GSendArgv( char **argv ) +{ + int num; + + if (argv) { + for (num = 0; argv[num]; num++); + GDebug( "sending argv[%d] to %s ...\n", num, curtalk->pipe->who ); + iGSendStrArr( num + 1, argv ); + } else { + GDebug( "sending NULL argv to %s\n", curtalk->pipe->who ); + GWrite( &argv, sizeof(int) ); + } +} + +char ** +GRecvArgv() +{ + int num; + + return GRecvStrArr( &num ); +} + diff --git a/kdm/backend/protodpy.c b/kdm/backend/protodpy.c new file mode 100644 index 000000000..08c38fbd1 --- /dev/null +++ b/kdm/backend/protodpy.c @@ -0,0 +1,141 @@ +/* + +Copyright 1988, 1998 The Open Group + +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 + * + * manage a collection of proto-displays. These are displays for + * which sessionID's have been generated, but no session has been + * started. + */ + +#include <config.h> + +#ifdef XDMCP + +#include "dm.h" +#include "dm_error.h" + +static struct protoDisplay *protoDisplays; + +struct protoDisplay * +FindProtoDisplay( + XdmcpNetaddr address, + int addrlen, + CARD16 displayNumber ) +{ + struct protoDisplay *pdpy; + + Debug( "FindProtoDisplay\n" ); + for (pdpy = protoDisplays; pdpy; pdpy=pdpy->next) { + if (pdpy->displayNumber == displayNumber && + addressEqual( address, addrlen, pdpy->address, pdpy->addrlen )) + { + return pdpy; + } + } + return (struct protoDisplay *)0; +} + +static void +TimeoutProtoDisplays (void) +{ + struct protoDisplay *pdpy, *next; + + for (pdpy = protoDisplays; pdpy; pdpy = next) { + next = pdpy->next; + if (pdpy->date < (unsigned long)(now - PROTO_TIMEOUT)) + DisposeProtoDisplay( pdpy ); + } +} + +struct protoDisplay * +NewProtoDisplay( XdmcpNetaddr address, int addrlen, CARD16 displayNumber, + CARD16 connectionType, ARRAY8Ptr connectionAddress, + CARD32 sessionID ) +{ + struct protoDisplay *pdpy; + + Debug( "NewProtoDisplay\n" ); + TimeoutProtoDisplays (); + pdpy = (struct protoDisplay *)Malloc( sizeof(*pdpy) ); + if (!pdpy) + return NULL; + pdpy->address = (XdmcpNetaddr)Malloc( addrlen ); + if (!pdpy->address) { + free( (char *)pdpy ); + return NULL; + } + pdpy->addrlen = addrlen; + memmove( pdpy->address, address, addrlen ); + pdpy->displayNumber = displayNumber; + pdpy->connectionType = connectionType; + pdpy->date = now; + if (!XdmcpCopyARRAY8( connectionAddress, &pdpy->connectionAddress )) { + free( (char *)pdpy->address ); + free( (char *)pdpy ); + return NULL; + } + pdpy->sessionID = sessionID; + pdpy->fileAuthorization = (Xauth *)NULL; + pdpy->xdmcpAuthorization = (Xauth *)NULL; + pdpy->next = protoDisplays; + protoDisplays = pdpy; + return pdpy; +} + +void +DisposeProtoDisplay( pdpy ) + struct protoDisplay *pdpy; +{ + struct protoDisplay *p, *prev; + + prev = 0; + for (p = protoDisplays; p; p=p->next) { + if (p == pdpy) + break; + prev = p; + } + if (!p) + return; + if (prev) + prev->next = pdpy->next; + else + protoDisplays = pdpy->next; + bzero( &pdpy->key, sizeof(pdpy->key) ); + if (pdpy->fileAuthorization) + XauDisposeAuth( pdpy->fileAuthorization ); + if (pdpy->xdmcpAuthorization) + XauDisposeAuth( pdpy->xdmcpAuthorization ); + XdmcpDisposeARRAY8( &pdpy->connectionAddress ); + free( (char *)pdpy->address ); + free( (char *)pdpy ); +} + +#endif /* XDMCP */ diff --git a/kdm/backend/reset.c b/kdm/backend/reset.c new file mode 100644 index 000000000..2c2100870 --- /dev/null +++ b/kdm/backend/reset.c @@ -0,0 +1,111 @@ +/* + +Copyright 1988, 1998 The Open Group + +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 + * + * pseudoReset -- pretend to reset the server by killing all clients + * with windows. It will reset the server most of the time, unless + * a client remains connected with no windows. + */ + +#include "dm.h" +#include "dm_error.h" + +#include <X11/Xlib.h> + +#include <signal.h> + +/*ARGSUSED*/ +static int +ignoreErrors( Display *dspl ATTR_UNUSED, XErrorEvent *event ATTR_UNUSED ) +{ + Debug( "ignoring error\n" ); + return 0; +} + +/* + * this is mostly bogus -- but quite useful. I wish the protocol + * had some way of enumerating and identifying clients, that way + * this code wouldn't have to be this kludgy. + */ + +static void +killWindows( Window window ) +{ + Window root, parent, *children; + unsigned int child, nchildren = 0; + + while (XQueryTree( dpy, window, &root, &parent, &children, &nchildren ) + && nchildren > 0) + { + for (child = 0; child < nchildren; child++) { + Debug( "XKillClient %p\n", children[child] ); + XKillClient( dpy, children[child] ); + } + XFree( (char *)children ); + } +} + +static Jmp_buf resetJmp; + +/* ARGSUSED */ +static void +abortReset( int n ATTR_UNUSED ) +{ + Longjmp( resetJmp, 1 ); +} + +/* + * this display connection better not have any windows... + */ + +void +pseudoReset() +{ + int screen; + + if (Setjmp( resetJmp )) { + LogError( "pseudoReset timeout\n" ); + } else { + (void)Signal( SIGALRM, abortReset ); + (void)alarm( 30 ); + XSetErrorHandler( ignoreErrors ); + for (screen = 0; screen < ScreenCount (dpy); screen++) { + Debug( "pseudoReset screen %d\n", screen ); + killWindows( RootWindow( dpy, screen ) ); + } + Debug( "before XSync\n" ); + XSync( dpy, False ); + (void)alarm( 0 ); + } + Signal( SIGALRM, SIG_DFL ); + XSetErrorHandler( (XErrorHandler)0 ); + Debug( "pseudoReset done\n" ); +} diff --git a/kdm/backend/resource.c b/kdm/backend/resource.c new file mode 100644 index 000000000..f17db78ec --- /dev/null +++ b/kdm/backend/resource.c @@ -0,0 +1,486 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2000-2005 Oswald Buddenhagen <[email protected]> + +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 + * + * obtain configuration data + */ + +#include "dm.h" +#include "dm_error.h" + +#include <sys/stat.h> + + +static char **originalArgv; + +static GProc getter; +GTalk cnftalk; + +static void +OpenGetter() +{ + GSet( &cnftalk ); + if (!getter.pid) { + if (GOpen( &getter, + originalArgv, "_config", 0, strdup( "config reader" ), + 0 )) + LogPanic( "Cannot run config reader\n" ); + Debug( "getter now ready\n" ); + } +} + +void +CloseGetter() +{ + if (getter.pid) { + GSet( &cnftalk ); + (void)GClose (&getter, 0, 0); + Debug( "getter now closed\n" ); + } +} + +/* + * ref-counted, unique-instance strings + */ +static RcStr *strs; + +/* + * make a ref-counted string of the argument. the new string will + * have a ref-count of 1. the passed string pointer is no longer valid. + */ +RcStr * +newStr( char *str ) +{ + RcStr *cs; + + for (cs = strs; cs; cs = cs->next) + if (!strcmp( str, cs->str )) { + free( str ); + cs->cnt++; + return cs; + } + if (!(cs = Malloc( sizeof(*cs) ))) + return 0; + cs->cnt = 1; + cs->str = str; + cs->next = strs; + strs = cs; + return cs; +} + +/* + * decrement ref-count and delete string when count drops to 0. + */ +void +delStr( RcStr *str ) +{ + RcStr **cs; + + if (!str || --str->cnt) + return; + for (cs = &strs; *cs; cs = &((*cs)->next)) + if (str == *cs) { + *cs = (*cs)->next; + free( (*cs)->str ); + free( *cs ); + break; + } +} + + +typedef struct CfgFile { + RcStr *name; + int depidx; + long deptime; +} CfgFile; + +static int numCfgFiles; +static CfgFile *cfgFiles; + +static int cfgMapT[] = { + GC_gGlobal, + GC_gDisplay, +#ifdef XDMCP + GC_gXaccess, +#endif +}; +static int cfgMap[as(cfgMapT)]; + +static int +GetDeps() +{ + int ncf, i, dep, ret; + CfgFile *cf; + + OpenGetter(); + GSendInt( GC_Files ); + ncf = GRecvInt(); + if (!(cf = Malloc( ncf * sizeof(*cf) ))) { + CloseGetter(); + return 0; + } + for (i = 0; i < ncf; i++) { + cf[i].name = newStr( GRecvStr() ); + if ((dep = cf[i].depidx = GRecvInt()) != -1) + cf[i].deptime = mTime( cf[dep].name->str ); + } + if (cfgFiles) { + for (i = 0; i < numCfgFiles; i++) + delStr( cfgFiles[i].name ); + free( cfgFiles ); + } + ret = 1; + cfgFiles = cf; + numCfgFiles = ncf; + for (i = 0; i < as(cfgMapT); i++) { + GSendInt( cfgMapT[i] ); + if ((cfgMap[i] = GRecvInt()) < 0) { + LogError( "Config reader does not support config cathegory %#x\n", + cfgMapT[i] ); + ret = 0; + } + } + GSendInt( -1 ); + return ret; +} + +static int +checkDep( int idx ) +{ + int dep; + + if ((dep = cfgFiles[idx].depidx) == -1) + return 0; + if (checkDep( dep )) + return 1; + return mTime( cfgFiles[dep].name->str ) != cfgFiles[idx].deptime; +} + +static int +needsReScan( int what, CfgDep *dep ) +{ + int widx, idx; + long mt; + + for (widx = 0; cfgMapT[widx] != what; widx++); + idx = cfgMap[widx]; + if (checkDep( idx )) { + if (!GetDeps()) + return -1; + idx = cfgMap[widx]; + } + mt = mTime( cfgFiles[idx].name->str ); + if (dep->name != cfgFiles[idx].name) { + if (dep->name) + delStr( dep->name ); + dep->name = cfgFiles[idx].name; + dep->name->cnt++; + dep->time = mt; + return 1; + } else if (dep->time != mt) { + dep->time = mt; + return 1; + } else + return 0; +} + +int +startConfig( int what, CfgDep *dep, int force ) +{ + int ret; + + if ((ret = needsReScan( what, dep )) < 0 || (!ret && !force)) + return ret; + OpenGetter(); + GSendInt( GC_GetConf ); + GSendInt( what ); + GSendStr( dep->name->str ); + return 1; +} + +static void +LoadResources( CfgArr *conf ) +{ + char **vptr, **pptr, *cptr; + long *iptr, i, id, nu, j, nptr, nint, nchr; + + if (conf->data) + free( conf->data ); + conf->numCfgEnt = GRecvInt(); + nptr = GRecvInt(); + nint = GRecvInt(); + nchr = GRecvInt(); + if (!(conf->data = Malloc( conf->numCfgEnt * + (sizeof(long) + + sizeof(char *)) + + nptr * sizeof(char *) + + nint * sizeof(long) + + nchr ))) + { + CloseGetter(); + return; + } + vptr = (char **)conf->data; + pptr = vptr + conf->numCfgEnt; + conf->idx = (long *)(pptr + nptr); + iptr = conf->idx + conf->numCfgEnt; + cptr = (char *)(iptr + nint); + for (i = 0; i < conf->numCfgEnt; i++) { + id = GRecvInt(); + conf->idx[i] = id; + switch (id & C_TYPE_MASK) { + case C_TYPE_INT: + vptr[i] = (char *)((unsigned long)GRecvInt()); + break; + case C_TYPE_STR: + vptr[i] = cptr; + cptr += GRecvStrBuf( cptr ); + break; + case C_TYPE_ARGV: + nu = GRecvInt(); + vptr[i] = (char *)pptr; + for (j = 0; j < nu; j++) { + *pptr++ = cptr; + cptr += GRecvStrBuf( cptr ); + } + *pptr++ = (char *)0; + break; + default: + LogError( "Config reader supplied unknown data type in id %#x\n", + id ); + break; + } + } +} + +static void +ApplyResource( int id, char **src, char **dst ) +{ + switch (id & C_TYPE_MASK) { + case C_TYPE_INT: + *(int *)dst = *(long *)src; + break; + case C_TYPE_STR: + case C_TYPE_ARGV: + *dst = *src; + break; + } +} + + +#define boffset(f) XtOffsetOf(struct display, f) + +/* no global variables exported currently +struct globEnts { + int id; + char **off; +} globEnt[] = { +}; + */ + +/* no per-display variables exported currently +struct dpyEnts { + int id; + int off; +} dpyEnt[] = { +}; + */ + +CfgArr cfg; + +char ** +FindCfgEnt( struct display *d, int id ) +{ + int i; + +/* no global variables exported currently + for (i = 0; i < as(globEnt); i++) + if (globEnt[i].id == id) + return globEnt[i].off; + */ + for (i = 0; i < cfg.numCfgEnt; i++) + if (cfg.idx[i] == id) + return ((char **)cfg.data) + i; + if (d) { +/* no per-display variables exported currently + for (i = 0; i < as(dpyEnt); i++) + if (dpyEnt[i].id == id) + return (char **)(((char *)d) + dpyEnt[i].off); + */ + for (i = 0; i < d->cfg.numCfgEnt; i++) + if (d->cfg.idx[i] == id) + return ((char **)d->cfg.data) + i; + } + Debug( "unknown config entry %#x requested\n", id ); + return (char **)0; +} + + +CONF_CORE_GLOBAL_DEFS + +struct globVals { + int id; + char **off; +} globVal[] = { +CONF_CORE_GLOBALS +}; + +int +LoadDMResources( int force ) +{ + int i, ret; + char **ent; + + if (Setjmp( cnftalk.errjmp )) + return -1; /* may memleak, but we probably have to abort anyway */ + if ((ret = startConfig( GC_gGlobal, &cfg.dep, force )) <= 0) + return ret; + LoadResources( &cfg ); +/* Debug( "manager resources: %[*x\n", + cfg.numCfgEnt, ((char **)cfg.data) + cfg.numCfgEnt );*/ + ret = 1; + for (i = 0; i < as(globVal); i++) { + if (!(ent = FindCfgEnt( 0, globVal[i].id ))) + ret = -1; + else + ApplyResource( globVal[i].id, ent, globVal[i].off ); + } + if (ret < 0) + LogError( "Internal error: config reader supplied incomplete data\n" ); + return ret; +} + + +struct dpyVals { + int id; + int off; +} dpyVal[] = { +CONF_CORE_LOCALS +}; + +int +LoadDisplayResources( struct display *d ) +{ + int i, ret; + char **ent; + + if (Setjmp( cnftalk.errjmp )) + return -1; /* may memleak */ + if ((ret = startConfig( GC_gDisplay, &d->cfg.dep, FALSE )) <= 0) + return ret; + GSendStr( d->name ); + GSendStr( d->class2 ); + LoadResources( &d->cfg ); +/* Debug( "display(%s, %s) resources: %[*x\n", d->name, d->class2, + d->cfg.numCfgEnt, ((char **)d->cfg.data) + d->cfg.numCfgEnt );*/ + ret = 1; + for (i = 0; i < as(dpyVal); i++) { + if (!(ent = FindCfgEnt( d, dpyVal[i].id ))) + ret = -1; + else + ApplyResource( dpyVal[i].id, ent, + (char **)(((char *)d) + dpyVal[i].off) ); + } + if (ret < 0) + LogError( "Internal error: config reader supplied incomplete data\n" ); + return ret; +} + +int +InitResources( char **argv ) +{ + originalArgv = argv; + cnftalk.pipe = &getter.pipe; + if (Setjmp( cnftalk.errjmp )) + return 0; /* may memleak */ + return GetDeps(); +} + +static void +addServers( char **srv, int bType ) +{ + char *name, *class2; + const char *dtx, *cls; + struct display *d; + + for (; *srv; srv++) { + if ((cls = strchr( *srv, '_' ))) { + if (!StrNDup( &name, *srv, cls - *srv )) + return; + if (!StrDup( &class2, cls )) { + free( name ); + return; + } + } else { + if (!StrDup( &name, *srv )) + return; + class2 = 0; + } + if ((d = FindDisplayByName( name ))) { + if (d->class2) + free( d->class2 ); + dtx = "existing"; + } else { + if (!(d = NewDisplay( name ))) { + free( name ); + if (class2) + free( class2 ); + return; + } + dtx = "new"; + } + d->stillThere = 1; + d->class2 = class2; + d->displayType = (*name == ':' ? dLocal : dForeign) | bType; + if ((bType & d_lifetime) == dReserve) { + if (d->status == notRunning) + d->status = reserve; + } else { + if (d->status == reserve) + d->status = notRunning; + } + Debug( "found %s %s%s display: %s %s\n", dtx, + ((d->displayType & d_location) == dLocal) ? "local" : "foreign", + ((d->displayType & d_lifetime) == dReserve) ? " reserve" : "", + d->name, d->class2 ); + free( name ); + } +} + +void +ScanServers( void ) +{ + Debug( "ScanServers\n" ); + addServers( staticServers, dFromFile | dPermanent ); + addServers( reserveServers, dFromFile | dReserve ); +} + diff --git a/kdm/backend/rpcauth.c b/kdm/backend/rpcauth.c new file mode 100644 index 000000000..1186f72c2 --- /dev/null +++ b/kdm/backend/rpcauth.c @@ -0,0 +1,89 @@ +/* + +Copyright 1988, 1998 The Open Group + +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 + * + * generate SecureRPC authorization records + */ + +#include <config.h> + +#ifdef SECURE_RPC + +#include "dm.h" +#include "dm_auth.h" +#include "dm_error.h" + +#include <rpc/rpc.h> +#include <rpc/key_prot.h> + +/*ARGSUSED*/ +void +SecureRPCInitAuth( unsigned short name_len ATTR_UNUSED, + const char *name ATTR_UNUSED ) +{ +} + +Xauth * +SecureRPCGetAuth( unsigned short namelen, const char *name ) +{ + Xauth *new; + char key[MAXNETNAMELEN+1]; + + new = (Xauth *)Malloc( sizeof(*new) ); + if (!new) + return (Xauth *)0; + new->family = FamilyWild; + new->address_length = 0; + new->address = 0; + new->number_length = 0; + new->number = 0; + + getnetname( key ); + Debug( "system netname %s\n", key ); + new->data_length = strlen( key ); + new->data = (char *)Malloc( new->data_length ); + if (!new->data) { + free( (char *)new ); + return (Xauth *)0; + } + new->name = (char *)Malloc( namelen ); + if (!new->name) { + free( (char *)new->data ); + free( (char *)new ); + return (Xauth *)0; + } + memmove( new->name, name, namelen ); + new->name_length = namelen; + memmove( new->data, key, new->data_length ); + return new; +} + +#endif diff --git a/kdm/backend/server.c b/kdm/backend/server.c new file mode 100644 index 000000000..129c8bfe1 --- /dev/null +++ b/kdm/backend/server.c @@ -0,0 +1,360 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2001,2003,2005 Oswald Buddenhagen <[email protected]> + +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 + * + * server.c - manage the X server + */ + +#include "dm.h" +#include "dm_error.h" +#include "dm_socket.h" + +#include <X11/Xlib.h> + +#include <stdio.h> +#include <signal.h> + + +struct display *startingServer; +time_t serverTimeout = TO_INF; + +char ** +PrepServerArgv( struct display *d, const char *args ) +{ + char **argv; +#ifdef HAVE_VTS + char vtstr[8]; +#endif + + if (!(argv = parseArgs( 0, d->serverCmd )) || + !(argv = parseArgs( argv, args )) || + !(argv = addStrArr( argv, d->name, -1 ))) + exit( 47 ); +#ifdef HAVE_VTS + if (d->serverVT && + !(argv = addStrArr( argv, vtstr, + sprintf( vtstr, "vt%d", d->serverVT ) ))) + exit( 47 ); +#endif + return argv; +} + +static void +StartServerOnce( void ) +{ + struct display *d = startingServer; + char **argv; + int pid; + + Debug( "StartServerOnce for %s, try %d\n", d->name, ++d->startTries ); + d->serverStatus = starting; + switch (pid = Fork()) { + case 0: + argv = PrepServerArgv( d, d->serverArgsLocal ); + if (d->authFile) { + if (!(argv = addStrArr( argv, "-auth", 5 )) || + !(argv = addStrArr( argv, d->authFile, -1 ))) + exit( 47 ); + } + Debug( "exec %\"[s\n", argv ); + /* + * give the server SIGUSR1 ignored, + * it will notice that and send SIGUSR1 + * when ready + */ + (void)Signal( SIGUSR1, SIG_IGN ); + (void)execv( argv[0], argv ); + LogError( "X server %\"s cannot be executed\n", argv[0] ); + exit( 47 ); + case -1: + LogError( "X server fork failed\n" ); + StartServerFailed(); + break; + default: + Debug( "X server forked, pid %d\n", pid ); + d->serverPid = pid; + serverTimeout = d->serverTimeout + now; + break; + } +} + +void +StartServer( struct display *d ) +{ + startingServer = d; + d->startTries = 0; + StartServerOnce(); +} + +void +AbortStartServer( struct display *d ) +{ + if (startingServer == d) + { + if (d->serverStatus != ignore) + { + d->serverStatus = ignore; + serverTimeout = TO_INF; + Debug( "aborting X server start\n" ); + } + startingServer = 0; + } +} + +void +StartServerSuccess() +{ + struct display *d = startingServer; + d->serverStatus = ignore; + serverTimeout = TO_INF; + Debug( "X server ready, starting session\n" ); + StartDisplayP2( d ); +} + +void +StartServerFailed() +{ + struct display *d = startingServer; + if (!d->serverAttempts || d->startTries < d->serverAttempts) { + d->serverStatus = pausing; + serverTimeout = d->openDelay + now; + } else { + d->serverStatus = ignore; + serverTimeout = TO_INF; + startingServer = 0; + LogError( "X server for display %s can't be started," + " session disabled\n", d->name ); + StopDisplay( d ); + } +} + +void +StartServerTimeout() +{ + struct display *d = startingServer; + switch (d->serverStatus) { + case ignore: + case awaiting: + break; /* cannot happen */ + case starting: + LogError( "X server startup timeout, terminating\n" ); + kill( d->serverPid, d->termSignal ); + d->serverStatus = d->termSignal == SIGKILL ? killed : terminated; + serverTimeout = d->serverTimeout + now; + break; + case terminated: + LogInfo( "X server termination timeout, killing\n" ); + kill( d->serverPid, SIGKILL ); + d->serverStatus = killed; + serverTimeout = 10 + now; + break; + case killed: + LogInfo( "X server is stuck in D state; leaving it alone\n" ); + StartServerFailed(); + break; + case pausing: + StartServerOnce(); + break; + } +} + + +Display *dpy; + +/* + * this code is complicated by some TCP failings. On + * many systems, the connect will occasionally hang forever, + * this trouble is avoided by setting up a timeout to Longjmp + * out of the connect (possibly leaving piles of garbage around + * inside Xlib) and give up, terminating the server. + */ + +static Jmp_buf openAbort; + +/* ARGSUSED */ +static void +abortOpen( int n ATTR_UNUSED ) +{ + Longjmp( openAbort, 1 ); +} + +#ifdef XDMCP + +#ifdef STREAMSCONN +# include <tiuser.h> +#endif + +static void +GetRemoteAddress( struct display *d, int fd ) +{ + char buf[512]; + int len = sizeof(buf); +#ifdef STREAMSCONN + struct netbuf netb; +#endif + + XdmcpDisposeARRAY8( &d->peer ); +#ifdef STREAMSCONN + netb.maxlen = sizeof(buf); + netb.buf = buf; + t_getname( fd, &netb, REMOTENAME ); + len = 8; + /* lucky for us, t_getname returns something that looks like a sockaddr */ +#else + getpeername( fd, (struct sockaddr *)buf, (void *)&len ); +#endif + if (len && XdmcpAllocARRAY8( &d->peer, len )) + memmove( (char *)d->peer.data, buf, len ); + Debug( "got remote address %s %d\n", d->name, d->peer.length ); +} + +#endif /* XDMCP */ + +static int +openErrorHandler( Display *dspl ATTR_UNUSED ) +{ + LogError( "IO Error in XOpenDisplay\n" ); + exit( EX_OPENFAILED_DPY ); + /*NOTREACHED*/ + return (0); +} + +void +WaitForServer( struct display *d ) +{ + volatile int i; + /* static int i; */ + + i = 0; + do { + (void)Signal( SIGALRM, abortOpen ); + (void)alarm( (unsigned)d->openTimeout ); + if (!Setjmp( openAbort )) { + Debug( "before XOpenDisplay(%s)\n", d->name ); + errno = 0; + (void)XSetIOErrorHandler( openErrorHandler ); + dpy = XOpenDisplay( d->name ); +#ifdef STREAMSCONN + { + /* For some reason, the next XOpenDisplay we do is + going to fail, so we might as well get that out + of the way. There is something broken here. */ + Display *bogusDpy = XOpenDisplay( d->name ); + Debug( "bogus XOpenDisplay %s\n", + bogusDpy ? "succeeded" : "failed" ); + if (bogusDpy) XCloseDisplay( bogusDpy ); /* just in case */ + } +#endif + (void)alarm( (unsigned)0 ); + (void)Signal( SIGALRM, SIG_DFL ); + (void)XSetIOErrorHandler( (int (*)( Display * )) 0 ); + Debug( "after XOpenDisplay(%s)\n", d->name ); + if (dpy) { +#ifdef XDMCP + if ((d->displayType & d_location) == dForeign) + GetRemoteAddress( d, ConnectionNumber( dpy ) ); +#endif + RegisterCloseOnFork( ConnectionNumber( dpy ) ); + return; + } + Debug( "OpenDisplay(%s) attempt %d failed: %m\n", d->name, i + 1 ); + sleep( (unsigned)d->openDelay ); + } else { + LogError( "Hung in XOpenDisplay(%s), aborting\n", d->name ); + (void)Signal( SIGALRM, SIG_DFL ); + break; + } + } while (++i < d->openRepeat); + LogError( "Cannot connect to %s, giving up\n", d->name ); + exit( EX_OPENFAILED_DPY ); +} + + +void +ResetServer( struct display *d ) +{ + if (dpy && (d->displayType & d_origin) != dFromXDMCP) + pseudoReset(); +} + + +static Jmp_buf pingTime; + +static void +PingLost( void ) +{ + Longjmp( pingTime, 1 ); +} + +/* ARGSUSED */ +static int +PingLostIOErr( Display *dspl ATTR_UNUSED ) +{ + PingLost(); + return 0; +} + +/* ARGSUSED */ +static void +PingLostSig( int n ATTR_UNUSED ) +{ + PingLost(); +} + +int +PingServer( struct display *d ) +{ + int (*oldError)( Display * ); + void (*oldSig)( int ); + int oldAlarm; + + oldError = XSetIOErrorHandler( PingLostIOErr ); + oldAlarm = alarm( 0 ); + oldSig = Signal( SIGALRM, PingLostSig ); + (void)alarm( d->pingTimeout * 60 ); + if (!Setjmp( pingTime )) { + Debug( "ping X server\n" ); + XSync( dpy, 0 ); + } else { + Debug( "X server dead\n" ); + (void)alarm( 0 ); + (void)Signal( SIGALRM, SIG_DFL ); + XSetIOErrorHandler( oldError ); + return 0; + } + (void)alarm( 0 ); + (void)Signal( SIGALRM, oldSig ); + (void)alarm( oldAlarm ); + Debug( "X server alive\n" ); + XSetIOErrorHandler( oldError ); + return 1; +} diff --git a/kdm/backend/session.c b/kdm/backend/session.c new file mode 100644 index 000000000..72da0a820 --- /dev/null +++ b/kdm/backend/session.c @@ -0,0 +1,792 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2000-2004 Oswald Buddenhagen <[email protected]> + +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 + * + * subdaemon event loop, etc. + */ + +#include "dm.h" +#include "dm_error.h" + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/cursorfont.h> + +#include <stdio.h> +#include <ctype.h> +#include <signal.h> + +struct display *td; +const char *td_setup = "auto"; + +static void DeleteXloginResources( void ); +static void LoadXloginResources( void ); +static void SetupDisplay( const char *arg ); + + +static Jmp_buf pingTime; + +/* ARGSUSED */ +static void +catchAlrm( int n ATTR_UNUSED ) +{ + Longjmp( pingTime, 1 ); +} + +static Jmp_buf tenaciousClient; + +/* ARGSUSED */ +static void +waitAbort( int n ATTR_UNUSED ) +{ + Longjmp( tenaciousClient, 1 ); +} + +static void +AbortClient( int pid ) +{ + int sig = SIGTERM; + volatile int i; + int retId; + + for (i = 0; i < 4; i++) { + if (kill( -pid, sig ) == -1) { + switch (errno) { + case EPERM: + LogError( "Can't kill client\n" ); + case EINVAL: + case ESRCH: + return; + } + } + if (!Setjmp( tenaciousClient )) { + (void)Signal( SIGALRM, waitAbort ); + (void)alarm( (unsigned)10 ); + retId = wait( (waitType *)0 ); + (void)alarm( (unsigned)0 ); + (void)Signal( SIGALRM, SIG_DFL ); + if (retId == pid) + break; + } else + (void)Signal( SIGALRM, SIG_DFL ); + sig = SIGKILL; + } +} + + +static char * +conv_auto( int what, const char *prompt ATTR_UNUSED ) +{ + switch (what) { + case GCONV_USER: + return curuser; + case GCONV_PASS: + case GCONV_PASS_ND: + return curpass; + default: + LogError( "Unknown authentication data type requested for autologin.\n" ); + return 0; + } +} + +static void +DoAutoLogon( void ) +{ + ReStr( &curuser, td->autoUser ); + ReStr( &curpass, td->autoPass ); + ReStr( &curtype, "classic" ); + cursource = PWSRC_AUTOLOGIN; +} + +static int +AutoLogon( Time_t tdiff ) +{ + Debug( "autoLogon, tdiff = %d, rLogin = %d, goodexit = %d, nuser = %s\n", + tdiff, td->hstent->rLogin, td->hstent->goodExit, td->hstent->nuser ); + if (td->hstent->rLogin == 2 || + (td->hstent->rLogin == 1 && + tdiff <= 0 && !td->hstent->goodExit && !td->hstent->lock)) + { + curuser = td->hstent->nuser; + td->hstent->nuser = 0; + curpass = td->hstent->npass; + td->hstent->npass = 0; + newdmrc = td->hstent->nargs; + td->hstent->nargs = 0; + ReStr( &curtype, "classic" ); + cursource = (td->hstent->rLogin == 1) ? PWSRC_RELOGIN : PWSRC_MANUAL; + return 1; + } else if (*td->autoUser && !td->autoDelay && + ((tdiff > 0 && ((td->displayType & d_lifetime) == dTransient || + !td->hstent->lastExit)) || + td->autoAgain)) + { + unsigned int lmask; + Window dummy1, dummy2; + int dummy3, dummy4, dummy5, dummy6; + XQueryPointer( dpy, DefaultRootWindow( dpy ), + &dummy1, &dummy2, &dummy3, &dummy4, &dummy5, &dummy6, + &lmask ); + if (lmask & ShiftMask) + return 0; + DoAutoLogon(); + return 1; + } + return 0; +} + + +static const struct { + int vcode, echo, ndelay; +} grqs[] = { + { V_GET_TEXT, TRUE, FALSE }, + { V_GET_TEXT, FALSE, FALSE }, + { V_GET_TEXT, TRUE, FALSE }, + { V_GET_TEXT, FALSE, FALSE }, + { V_GET_TEXT, FALSE, TRUE }, + { V_GET_BINARY, 0, 0 } +}; + +char * +conv_interact( int what, const char *prompt ) +{ + char *ret; + int tag; + + GSendInt( grqs[what].vcode ); + if (what == GCONV_BINARY) { + unsigned const char *up = (unsigned const char *)prompt; + int len = up[3] | (up[2] << 8) | (up[1] << 16) | (up[0] << 24); + GSendArr( len, prompt ); + GSendInt( FALSE ); /* ndelay */ + return GRecvArr( &len ); + } else { + GSendStr( prompt ); + GSendInt( grqs[what].echo ); + GSendInt( grqs[what].ndelay ); + ret = GRecvStr(); + if (ret) { + tag = GRecvInt(); + switch (what) { + case GCONV_USER: + /* assert(tag & V_IS_USER); */ + if (curuser) + free( curuser ); + curuser = ret; + break; + case GCONV_PASS: + case GCONV_PASS_ND: + /* assert(tag & V_IS_PASSWORD); */ + if (curpass) + free( curpass ); + curpass = ret; + break; + default: + if (tag & V_IS_USER) + ReStr( &curuser, ret ); + else if (tag & V_IS_PASSWORD) + ReStr( &curpass, ret ); + else if (tag & V_IS_NEWPASSWORD) + ReStr( &newpass, ret ); + else if (tag & V_IS_OLDPASSWORD) + ReStr( &ret, curpass ); + } + } + return ret; + } +} + +static int greeter; +GProc grtproc; +GTalk grttalk; + +GTalk mstrtalk; /* make static; see dm.c */ + +int +CtrlGreeterWait( int wreply ) +{ + int i, cmd, type, rootok; + char *name, *pass, **avptr; +#ifdef XDMCP + ARRAY8Ptr aptr; +#endif + + if (Setjmp( mstrtalk.errjmp )) { + CloseGreeter( TRUE ); + SessionExit( EX_UNMANAGE_DPY ); + } + + while (GRecvCmd( &cmd )) { + switch (cmd) + { + case G_Ready: + Debug( "G_Ready\n" ); + return 0; + case G_GetCfg: + /*Debug ("G_GetCfg\n");*/ + type = GRecvInt(); + /*Debug (" index %#x\n", type);*/ + if (type == C_isLocal) + i = (td->displayType & d_location) == dLocal; + else if (type == C_hasConsole) +#ifdef HAVE_VTS + i = *consoleTTYs != 0; +#else + i = td->console != 0; +#endif + else if (type == C_isAuthorized) + i = td->authorizations != 0; + else + goto normal; + GSendInt( GE_Ok ); + /*Debug (" -> bool %d\n", i);*/ + GSendInt( i ); + break; + normal: + if (!(avptr = FindCfgEnt( td, type ))) { + /*Debug (" -> not found\n");*/ + GSendInt( GE_NoEnt ); + break; + } + switch (type & C_TYPE_MASK) { + default: + /*Debug (" -> unknown type\n");*/ + GSendInt( GE_BadType ); + break; + case C_TYPE_INT: + case C_TYPE_STR: + case C_TYPE_ARGV: +#ifdef XDMCP + case C_TYPE_ARR: +#endif + GSendInt( GE_Ok ); + switch (type & C_TYPE_MASK) { + case C_TYPE_INT: + /*Debug (" -> int %#x (%d)\n", *(int *)avptr, *(int *)avptr);*/ + GSendInt( *(long *)avptr ); + break; + case C_TYPE_STR: + /*Debug (" -> string %\"s\n", *avptr);*/ + GSendStr( *avptr ); + break; + case C_TYPE_ARGV: + /*Debug (" -> sending argv %\"[{s\n", *(char ***)avptr);*/ + GSendArgv( *(char ***)avptr ); + break; +#ifdef XDMCP + case C_TYPE_ARR: + aptr = *(ARRAY8Ptr *)avptr; + /*Debug (" -> sending array %02[*:hhx\n", + aptr->length, aptr->data);*/ + GSendArr( aptr->length, (char *)aptr->data ); + break; +#endif + } + break; + } + break; + case G_ReadDmrc: + Debug( "G_ReadDmrc\n" ); + name = GRecvStr(); + Debug( " user %\"s\n", name ); + if (StrCmp( dmrcuser, name )) { + if (curdmrc) { free( curdmrc ); curdmrc = 0; } + if (dmrcuser) + free( dmrcuser ); + dmrcuser = name; + i = ReadDmrc(); + Debug( " -> status %d\n", i ); + GSendInt( i ); + Debug( " => %\"s\n", curdmrc ); + } else { + if (name) + free( name ); + Debug( " -> status " stringify( GE_Ok ) "\n" ); + GSendInt( GE_Ok ); + Debug( " => keeping old\n" ); + } + break; + case G_GetDmrc: + Debug( "G_GetDmrc\n" ); + name = GRecvStr(); + Debug( " key %\"s\n", name ); + pass = iniEntry( curdmrc, "Desktop", name, 0 ); + Debug( " -> %\"s\n", pass ); + GSendStr( pass ); + if (pass) + free( pass ); + free( name ); + break; +/* case G_ResetDmrc: + Debug ("G_ResetDmrc\n"); + if (newdmrc) { free (newdmrc); newdmrc = 0; } + break; */ + case G_PutDmrc: + Debug( "G_PutDmrc\n" ); + name = GRecvStr(); + Debug( " key %\"s\n", name ); + pass = GRecvStr(); + Debug( " value %\"s\n", pass ); + newdmrc = iniEntry( newdmrc, "Desktop", name, pass ); + free( pass ); + free( name ); + break; + case G_VerifyRootOK: + Debug( "G_VerifyRootOK\n" ); + rootok = TRUE; + goto doverify; + case G_Verify: + Debug( "G_Verify\n" ); + rootok = FALSE; + doverify: + if (curuser) { free( curuser ); curuser = 0; } + if (curpass) { free( curpass ); curpass = 0; } + if (curtype) free( curtype ); + curtype = GRecvStr(); + Debug( " type %\"s\n", curtype ); + cursource = PWSRC_MANUAL; + if (Verify( conv_interact, rootok )) { + Debug( " -> return success\n" ); + GSendInt( V_OK ); + } else + Debug( " -> failure returned\n" ); + break; + case G_AutoLogin: + Debug( "G_AutoLogin\n" ); + DoAutoLogon(); + if (Verify( conv_auto, FALSE )) { + Debug( " -> return success\n" ); + GSendInt( V_OK ); + } else + Debug( " -> failure returned\n" ); + break; + case G_SetupDpy: + Debug( "G_SetupDpy\n" ); + SetupDisplay( 0 ); + td_setup = 0; + GSendInt( 0 ); + break; + default: + return cmd; + } + if (!wreply) + return -1; + } + Debug( "lost connection to greeter\n" ); + return -2; +} + +void +OpenGreeter() +{ + char *name, **env; + static Time_t lastStart; + int cmd; + Cursor xcursor; + + GSet( &grttalk ); + if (greeter) + return; + if (time( 0 ) < lastStart + 10) /* XXX should use some readiness indicator instead */ + SessionExit( EX_UNMANAGE_DPY ); + greeter = 1; + ASPrintf( &name, "greeter for display %s", td->name ); + Debug( "starting %s\n", name ); + + /* Hourglass cursor */ + if ((xcursor = XCreateFontCursor( dpy, XC_watch ))) { + XDefineCursor( dpy, DefaultRootWindow( dpy ), xcursor ); + XFreeCursor( dpy, xcursor ); + } + XFlush( dpy ); + + /* Load system default Resources (if any) */ + LoadXloginResources(); + + grttalk.pipe = &grtproc.pipe; + env = systemEnv( (char *)0 ); + if (GOpen( &grtproc, (char **)0, "_greet", env, name, &td->gpipe )) + SessionExit( EX_UNMANAGE_DPY ); + freeStrArr( env ); + if ((cmd = CtrlGreeterWait( TRUE ))) { + if (cmd != -2) + LogError( "Received unknown or unexpected command %d from greeter\n", cmd ); + CloseGreeter( TRUE ); + SessionExit( EX_UNMANAGE_DPY ); + } + Debug( "%s ready\n", name ); + time( &lastStart ); +} + +int +CloseGreeter( int force ) +{ + int ret; + + if (!greeter) + return EX_NORMAL; + greeter = 0; + ret = GClose (&grtproc, 0, force); + Debug( "greeter for %s stopped\n", td->name ); + if (WaitCode( ret ) > EX_NORMAL && WaitCode( ret ) <= EX_MAX) { + Debug( "greeter-initiated session exit, code %d\n", WaitCode( ret ) ); + SessionExit( WaitCode( ret ) ); + } + return ret; +} + +void +PrepErrorGreet() +{ + if (!greeter) { + OpenGreeter(); + GSendInt( G_ErrorGreet ); + GSendStr( curuser ); + } +} + +static Jmp_buf idleTOJmp; + +/* ARGSUSED */ +static void +IdleTOJmp( int n ATTR_UNUSED ) +{ + Longjmp( idleTOJmp, 1 ); +} + + +static Jmp_buf abortSession; + +/* ARGSUSED */ +static void +catchTerm( int n ATTR_UNUSED ) +{ + Signal( SIGTERM, SIG_IGN ); + Longjmp( abortSession, EX_AL_RESERVER_DPY ); +} + +/* + * We need our own error handlers because we can't be sure what exit code Xlib + * will use, and our Xlib does exit(1) which matches EX_REMANAGE_DPY, which + * can cause a race condition leaving the display wedged. We need to use + * EX_RESERVER_DPY for IO errors, to ensure that the manager waits for the + * server to terminate. For other X errors, we should give up. + */ + +/*ARGSUSED*/ +static int +IOErrorHandler( Display *dspl ATTR_UNUSED ) +{ + LogError( "Fatal X server IO error: %m\n" ); + /* The only X interaction during the session are pings, and those + have an own IOErrorHandler -> not EX_AL_RESERVER_DPY */ + Longjmp( abortSession, EX_RESERVER_DPY ); + /*NOTREACHED*/ + return 0; +} + +/*ARGSUSED*/ +static int +ErrorHandler( Display *dspl ATTR_UNUSED, XErrorEvent *event ) +{ + LogError( "X error\n" ); + if (event->error_code == BadImplementation) + Longjmp( abortSession, EX_UNMANAGE_DPY ); + return 0; +} + +void +ManageSession( struct display *d ) +{ + int ex, cmd; + volatile int clientPid = 0; + volatile Time_t tdiff = 0; + + td = d; + Debug( "ManageSession %s\n", d->name ); + if ((ex = Setjmp( abortSession ))) { + CloseGreeter( TRUE ); + if (clientPid) + AbortClient( clientPid ); + SessionExit( ex ); + /* NOTREACHED */ + } + (void)XSetIOErrorHandler( IOErrorHandler ); + (void)XSetErrorHandler( ErrorHandler ); + (void)Signal( SIGTERM, catchTerm ); + + (void)Signal( SIGHUP, SIG_IGN ); + + if (Setjmp( grttalk.errjmp )) + Longjmp( abortSession, EX_RESERVER_DPY ); /* EX_RETRY_ONCE */ + +#ifdef XDMCP + if (d->useChooser) + DoChoose(); + /* NOTREACHED */ +#endif + + if (d->hstent->sdRec.how) { + OpenGreeter(); + GSendInt( G_ConfShutdown ); + GSendInt( d->hstent->sdRec.how ); + GSendInt( d->hstent->sdRec.uid ); + GSendStr( d->hstent->sdRec.osname ); + if ((cmd = CtrlGreeterWait( TRUE )) != G_Ready) { + LogError( "Received unknown command %d from greeter\n", cmd ); + CloseGreeter( TRUE ); + } + goto regreet; + } + + tdiff = time( 0 ) - td->hstent->lastExit - td->openDelay; + if (AutoLogon( tdiff )) { + if (!Verify( conv_auto, FALSE )) + goto gcont; + if (greeter) + GSendInt( V_OK ); + } else { + regreet: + OpenGreeter(); + if (Setjmp( idleTOJmp )) { + CloseGreeter( TRUE ); + SessionExit( EX_NORMAL ); + } + Signal( SIGALRM, IdleTOJmp ); + alarm( td->idleTimeout ); +#ifdef XDMCP + if (((d->displayType & d_location) == dLocal) && + d->loginMode >= LOGIN_DEFAULT_REMOTE) + goto choose; +#endif + for (;;) { + Debug( "ManageSession, greeting, tdiff = %d\n", tdiff ); + GSendInt( (*td->autoUser && td->autoDelay && + (tdiff > 0 || td->autoAgain)) ? + G_GreetTimed : G_Greet ); + gcont: + cmd = CtrlGreeterWait( TRUE ); +#ifdef XDMCP + recmd: + if (cmd == G_DChoose) { + choose: + cmd = DoChoose(); + goto recmd; + } + if (cmd == G_DGreet) + continue; +#endif + alarm( 0 ); + if (cmd == G_Ready) + break; + if (cmd == -2) + CloseGreeter( FALSE ); + else { + LogError( "Received unknown command %d from greeter\n", cmd ); + CloseGreeter( TRUE ); + } + goto regreet; + } + } + + if (CloseGreeter( FALSE ) != EX_NORMAL) + goto regreet; + + DeleteXloginResources(); + + if (td_setup) + SetupDisplay( td_setup ); + + if (!(clientPid = StartClient())) { + LogError( "Client start failed\n" ); + SessionExit( EX_NORMAL ); /* XXX maybe EX_REMANAGE_DPY? -- enable in dm.c! */ + } + Debug( "client Started\n" ); + + /* + * Wait for session to end, + */ + for (;;) { + if (!Setjmp( pingTime )) { + (void)Signal( SIGALRM, catchAlrm ); + (void)alarm( d->pingInterval * 60 ); /* may be 0 */ + (void)Wait4( clientPid ); + (void)alarm( 0 ); + break; + } else { + (void)alarm( 0 ); + if (!PingServer( d )) + catchTerm( SIGTERM ); + } + } + /* + * Sometimes the Xsession somehow manages to exit before + * a server crash is noticed - so we sleep a bit and wait + * for being killed. + */ + if (!PingServer( d )) { + Debug( "X server dead upon session exit.\n" ); + if ((d->displayType & d_location) == dLocal) + sleep( 10 ); + SessionExit( EX_AL_RESERVER_DPY ); + } + SessionExit( EX_NORMAL ); /* XXX maybe EX_REMANAGE_DPY? -- enable in dm.c! */ +} + +static int xResLoaded; + +void +LoadXloginResources() +{ + char **args; + char **env; + + if (!xResLoaded && td->resources[0] && access( td->resources, 4 ) == 0) { + env = systemEnv( (char *)0 ); + if ((args = parseArgs( (char **)0, td->xrdb )) && + (args = addStrArr( args, td->resources, -1 ))) + { + Debug( "loading resource file: %s\n", td->resources ); + (void)runAndWait( args, env ); + freeStrArr( args ); + } + freeStrArr( env ); + xResLoaded = TRUE; + } +} + +void +SetupDisplay( const char *arg ) +{ + char **env; + + env = systemEnv( (char *)0 ); + (void)source( env, td->setup, arg ); + freeStrArr( env ); +} + +void +DeleteXloginResources() +{ + int i; + Atom prop; + + if (!xResLoaded) + return; + xResLoaded = FALSE; + prop = XInternAtom( dpy, "SCREEN_RESOURCES", True ); + XDeleteProperty( dpy, RootWindow( dpy, 0 ), XA_RESOURCE_MANAGER ); + if (prop) + for (i = ScreenCount(dpy); --i >= 0; ) + XDeleteProperty( dpy, RootWindow( dpy, i ), prop ); + XSync( dpy, 0 ); +} + + +int +source( char **env, const char *file, const char *arg ) +{ + char **args; + int ret; + + if (file && file[0]) { + Debug( "source %s\n", file ); + if (!(args = parseArgs( (char **)0, file ))) + return waitCompose( 0,0,3 ); + if (arg && !(args = addStrArr( args, arg, -1 ))) + return waitCompose( 0,0,3 ); + ret = runAndWait( args, env ); + freeStrArr( args ); + return ret; + } + return 0; +} + +char ** +inheritEnv( char **env, const char **what ) +{ + char *value; + + for (; *what; ++what) + if ((value = getenv( *what ))) + env = setEnv( env, *what, value ); + return env; +} + +char ** +baseEnv( const char *user ) +{ + char **env; + + env = 0; + +#ifdef _AIX + /* we need the tags SYSENVIRON: and USRENVIRON: in the call to setpenv() */ + env = setEnv( env, "SYSENVIRON:", 0 ); +#endif + + if (user) { + env = setEnv( env, "USER", user ); +#ifdef _AIX + env = setEnv( env, "LOGIN", user ); +#endif + env = setEnv( env, "LOGNAME", user ); + } + +#ifdef _AIX + env = setEnv( env, "USRENVIRON:", 0 ); +#endif + + env = inheritEnv( env, (const char **)exportList ); + + env = setEnv( env, "DISPLAY", + memcmp( td->name, "localhost:", 10 ) ? + td->name : td->name + 9 ); + + if (td->ctrl.path) + env = setEnv( env, "DM_CONTROL", fifoDir ); + + return env; +} + +char ** +systemEnv( const char *user ) +{ + char **env; + + env = baseEnv( user ); + if (td->authFile) + env = setEnv( env, "XAUTHORITY", td->authFile ); + env = setEnv( env, "PATH", td->systemPath ); + env = setEnv( env, "SHELL", td->systemShell ); + return env; +} diff --git a/kdm/backend/sessreg.c b/kdm/backend/sessreg.c new file mode 100644 index 000000000..03a46e992 --- /dev/null +++ b/kdm/backend/sessreg.c @@ -0,0 +1,307 @@ +/* + +Copyright 1990, 1998 The Open Group +Copyright 2005 Oswald Buddenhagen <[email protected]> + +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 OPEN GROUP 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 The Open Group 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 Open Group. + +*/ + +/* + + Author: Keith Packard, MIT X Consortium + Lastlog support and dynamic utmp entry allocation + by Andreas Stolcke <[email protected]> + +*/ + +#define _FILE_OFFSET_BITS 64 +#include "dm.h" +#include "dm_error.h" + +#if defined(__svr4__) || defined(__Lynx__) || defined(__QNX__) || defined(__APPLE__) || defined(_SEQUENT_) /*|| defined(USE_PAM)*/ +# define NO_LASTLOG +#endif + +#ifndef NO_LASTLOG +# ifdef HAVE_LASTLOG_H +# include <lastlog.h> +# endif +# ifndef LLOG_FILE +# ifdef _PATH_LASTLOGX +# define LLOG_FILE _PATH_LASTLOGX +# elif defined(_PATH_LASTLOG) +# define LLOG_FILE _PATH_LASTLOG +# else +# define LLOG_FILE "/usr/adm/lastlog" +# endif +# endif +#endif + +#if !defined(__svr4__) && !defined(__QNX__) +# define SESSREG_HOST +#endif + +#ifdef BSD +# if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) +/* *BSD doesn't like a ':0' type entry in utmp */ +# define NO_UTMP +# endif +#endif + +#ifdef BSD_UTMP +# ifndef TTYS_FILE +# define TTYS_FILE "/etc/ttys" +# endif +#endif + +#ifdef _AIX +# define UTL_PFX "xdm/" +# define UTL_OFF strlen(UTL_PFX) +#else +# define UTL_OFF 0 +#endif + +#ifndef BSD_UTMP +static unsigned +crc32s( const unsigned char *str ) +{ + int b; + unsigned crc = 0xffffffff, by; + + for (; *str; str++) { + by = (crc & 255) ^ *str; + for (b = 0; b < 8; b++) + by = (by >> 1) ^ (-(by & 1) & 0xedb88320); + crc = (crc >> 8) ^ by; + } + return crc; +} +#endif + +void +sessreg( struct display *d, int pid, const char *user, int uid ) +{ + const char *dot, *colon; + int left, clen; +#ifdef BSD_UTMP + FILE *ttys; + int utmp, slot, freeslot; + STRUCTUTMP entry; +#else + unsigned crc, i; +#endif + int wtmp, c; +#ifndef NO_LASTLOG + int llog; + struct LASTLOG ll; +#endif + STRUCTUTMP ut_ent; + + if (!d->useSessReg) + return; + + bzero( &ut_ent, sizeof(ut_ent) ); + + if (pid) { + strncpy( ut_ent.ut_user, user, sizeof(ut_ent.ut_user) ); +#ifndef BSD_UTMP + ut_ent.ut_pid = pid; + ut_ent.ut_type = USER_PROCESS; + } else { + ut_ent.ut_type = DEAD_PROCESS; +#endif + } + ut_ent.ut_time = time( 0 ); + + colon = strchr( d->name, ':' ); + clen = strlen( colon ); + if (clen > (int)(sizeof(ut_ent.ut_line) - UTL_OFF) - 2) + return; /* uhm, well ... */ + if (colon == d->name) { +#ifndef BSD_UTMP + strncpy( ut_ent.ut_id, d->name, sizeof(ut_ent.ut_id) ); +#endif + left = 0; + } else { +#ifdef SESSREG_HOST +# ifndef BSD_UTMP + if (pid) +# endif + { + if (colon - d->name > (int)sizeof(ut_ent.ut_host)) { + ut_ent.ut_host[0] = '~'; + memcpy( ut_ent.ut_host + 1, + colon - (sizeof(ut_ent.ut_host) - 1), + sizeof(ut_ent.ut_host) - 1 ); + } else + memcpy( ut_ent.ut_host, d->name, colon - d->name ); + } +#endif +#ifndef BSD_UTMP + crc = crc32s( d->name ); + ut_ent.ut_id[0] = crc % 26 + 'A'; + crc /= 26; + for (i = 1; i < sizeof(ut_ent.ut_id); i++) { + c = crc % 62; + crc /= 62; + ut_ent.ut_id[i] = c < 26 ? c + 'A' : + c < 52 ? c - 26 + 'a' : c - 52 + '0'; + } +#endif + left = sizeof(ut_ent.ut_line) - UTL_OFF - clen; + if (colon - d->name <= left) { + clen += colon - d->name; + colon = d->name; + left = 0; + } else { + dot = strchr( d->name, '.' ); + if (dot && dot - d->name < left) { + memcpy( ut_ent.ut_line + UTL_OFF, d->name, left - 1 ); + ut_ent.ut_line[UTL_OFF + left - 1] = '~'; + } else { + memcpy( ut_ent.ut_line + UTL_OFF, d->name, left/2 - 1 ); + ut_ent.ut_line[UTL_OFF + left/2 - 1] = '~'; + if (dot) { + memcpy( ut_ent.ut_line + UTL_OFF + left/2, + dot - (left - left/2 - 1), + left - left/2 - 1 ); + ut_ent.ut_line[UTL_OFF + left - 1] = '~'; + } else + memcpy( ut_ent.ut_line + UTL_OFF + left/2, + colon - (left - left/2), left - left/2 ); + } + } + } +#ifdef UTL_PFX + memcpy( ut_ent.ut_line, UTL_PFX, UTL_OFF ); +#endif + memcpy( ut_ent.ut_line + UTL_OFF + left, colon, clen ); + +#ifndef NO_UTMP +# ifdef BSD_UTMP + if ((utmp = open( UTMP_FILE, O_RDWR )) < 0) + Debug( "cannot open utmp file " UTMP_FILE ": %m\n" ); + else { + + slot = 1; + if (pid) { + if (!(ttys = fopen( TTYS_FILE, "r" ))) + LogWarn( "Cannot open tty file " TTYS_FILE ": %m\n" ); + else { + int column0 = 1; + while ((c = getc( ttys )) != EOF) + if (c == '\n') { + slot++; + column0 = 1; + } else + column0 = 0; + if (!column0) + slot++; + fclose( ttys ); + } + } + freeslot = -1; + lseek( utmp, slot * sizeof(entry), SEEK_SET ); + while (read( utmp, (char *)&entry, sizeof(entry) ) == sizeof(entry)) { + if (!strncmp( entry.ut_line, ut_ent.ut_line, + sizeof(entry.ut_line) )) +# ifdef SESSREG_HOST + if (!strncmp( entry.ut_host, ut_ent.ut_host, + sizeof(entry.ut_host) )) +# endif + goto found; + if (freeslot < 0 && *entry.ut_user == '\0') + freeslot = slot; + slot++; + } + if (!pid) { + Debug( "utmp entry for display %s vanished\n", d->name ); + goto skip; + } + if (freeslot >= 0) + slot = freeslot; + found: + +# ifdef SESSREG_HOST + if (!pid) + bzero( ut_ent.ut_host, sizeof(ut_ent.ut_host) ); +# endif + lseek( utmp, slot * sizeof(ut_ent), SEEK_SET ); + if (write( utmp, (char *)&ut_ent, sizeof(ut_ent) ) != sizeof(ut_ent)) + LogError( "Cannot write utmp file " UTMP_FILE ": %m\n" ); + skip: + close( utmp ); + } +# else + UTMPNAME( UTMP_FILE ); + SETUTENT(); + PUTUTLINE( &ut_ent ); + ENDUTENT(); +# endif +#endif + + if ((wtmp = open( WTMP_FILE, O_WRONLY|O_APPEND )) < 0) + Debug( "cannot open wtmp file " WTMP_FILE ": %m\n" ); + else { + if (write( wtmp, (char *)&ut_ent, sizeof(ut_ent) ) != sizeof(ut_ent)) + LogError( "Cannot write wtmp file " WTMP_FILE ": %m\n" ); + close( wtmp ); + } + +#ifndef NO_LASTLOG + if (pid) { + bzero( (char *)&ll, sizeof(ll) ); + ll.ll_time = ut_ent.ut_time; + memcpy( ll.ll_line, ut_ent.ut_line, sizeof(ll.ll_line) ); + memcpy( ll.ll_host, ut_ent.ut_host, sizeof(ll.ll_host) ); +# ifdef HAVE_UTMPX + updlastlogx( LLOG_FILE, uid, &ll ); +# else + if ((llog = open( LLOG_FILE, O_RDWR )) < 0) + Debug( "cannot open lastlog file " LLOG_FILE ": %m\n" ); + else { + lseek( llog, (off_t)uid * sizeof(ll), SEEK_SET ); + if (write( llog, (char *)&ll, sizeof(ll) ) != sizeof(ll)) + LogError( "Cannot write llog file " WTMP_FILE ": %m\n" ); + close( llog ); + } +# endif + } +#else + (void)uid; +#endif + +#ifdef UTL_PFX + { + char tmp[sizeof("/dev/") + sizeof(ut_ent.ut_line)]; + mkdir( "/dev/" UTL_PFX, 0755 ); + chmod( "/dev/" UTL_PFX, 0755 ); + sprintf( tmp, "/dev/%.*s", sizeof(ut_ent.ut_line), ut_ent.ut_line ); + if (pid) + close( creat( tmp, 0644 ) ); + else + unlink( tmp ); + } +#endif +} diff --git a/kdm/backend/socket.c b/kdm/backend/socket.c new file mode 100644 index 000000000..677a3d32f --- /dev/null +++ b/kdm/backend/socket.c @@ -0,0 +1,418 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2002 Sun Microsystems, Inc. All rights reserved. +Copyright 2002,2004 Oswald Buddenhagen <[email protected]> + +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 + * + * socket.c - Support for BSD sockets + */ + +#include "dm.h" + +#if defined(XDMCP) && !defined(STREAMSCONN) + +#include "dm_error.h" +#include "dm_socket.h" + +#include <netdb.h> +#include <arpa/inet.h> + +static int c_request_port; + +static int +CreateListeningSocket( struct sockaddr *sock_addr, int salen ) +{ + int fd; +#if defined(IPv6) && defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) + int on = 0; +#endif + const char *addrstring = "unknown"; +#if defined(IPv6) && defined(AF_INET6) + char addrbuf[INET6_ADDRSTRLEN]; +#endif + + if (!request_port) + return -1; + + if (debugLevel & DEBUG_CORE) { +#if defined(IPv6) && defined(AF_INET6) + void *ipaddr; + if (sock_addr->sa_family == AF_INET6) + ipaddr = & ((struct sockaddr_in6 *)sock_addr)->sin6_addr; + else + ipaddr = & ((struct sockaddr_in *)sock_addr)->sin_addr; + addrstring = + inet_ntop( sock_addr->sa_family, ipaddr, addrbuf, sizeof(addrbuf) ); + +#else + addrstring = inet_ntoa( ((struct sockaddr_in *)sock_addr)->sin_addr ); +#endif + + Debug( "creating socket to listen on port %d of address %s\n", + request_port, addrstring ); + } + + if ((fd = socket( sock_addr->sa_family, SOCK_DGRAM, 0 )) == -1) { + LogError( "XDMCP socket creation failed, errno %d\n", errno ); + return -1; + } +#if defined(IPv6) && defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) + setsockopt( fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on) ); +#endif + + if (bind( fd, sock_addr, salen ) == -1) { + LogError( "error %d binding socket address %d\n", errno, request_port ); + close( fd ); + return -1; + } + + RegisterCloseOnFork( fd ); + RegisterInput( fd ); + return fd; +} + +struct socklist { + struct socklist *next; + struct socklist *mcastgroups; + struct sockaddr *addr; + int salen; + int addrlen; + int fd; + int ref; /* referenced bit - see UpdateListenSockets */ +}; + +static struct socklist *listensocks; + +static void +DestroyListeningSocket( struct socklist *s ) +{ + struct socklist *g, *n; + + if (s->fd >= 0) { + CloseNClearCloseOnFork( s->fd ); + UnregisterInput( s->fd ); + s->fd = -1; + } + if (s->addr) { + free( s->addr ); + s->addr = NULL; + } + for (g = s->mcastgroups; g; g = n) { + n = g->next; + if (g->addr) + free( g->addr ); + free( g ); + } + s->mcastgroups = NULL; +} + +static struct socklist* +FindInList( struct socklist *list, ARRAY8Ptr addr ) +{ + struct socklist *s; + + for (s = list; s; s = s->next) { + if (s->addrlen == addr->length) { + char *addrdata; + + switch (s->addr->sa_family) { + case AF_INET: + addrdata = (char *) + &(((struct sockaddr_in *)s->addr)->sin_addr.s_addr); + break; +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: + addrdata = (char *) + &(((struct sockaddr_in6 *)s->addr)->sin6_addr.s6_addr); + break; +#endif + default: + /* Unrecognized address family */ + continue; + } + if (!memcmp( addrdata, addr->data, addr->length )) + return s; + } + } + return NULL; +} + +static struct socklist * +CreateSocklistEntry( ARRAY8Ptr addr ) +{ + struct socklist *s; + + if (!(s = Calloc( 1, sizeof(struct socklist) ))) + return NULL; + + if (addr->length == 4) { /* IPv4 */ + struct sockaddr_in *sin4; + sin4 = Calloc( 1, sizeof(struct sockaddr_in) ); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + sin4->sin_len = sizeof(struct sockaddr_in); +#endif + s->addr = (struct sockaddr *)sin4; + s->salen = sizeof(struct sockaddr_in); + s->addrlen = sizeof(struct in_addr); + sin4->sin_family = AF_INET; + sin4->sin_port = htons( (short)request_port ); + memcpy( &sin4->sin_addr, addr->data, addr->length ); + } +#if defined(IPv6) && defined(AF_INET6) + else if (addr->length == 16) { /* IPv6 */ + struct sockaddr_in6 *sin6; + sin6 = Calloc( 1, sizeof(struct sockaddr_in6) ); +#ifdef SIN6_LEN + sin6->sin6_len = sizeof(struct sockaddr_in6); +#endif + s->addr = (struct sockaddr *)sin6; + s->salen = sizeof(struct sockaddr_in6); + s->addrlen = sizeof(struct in6_addr); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = htons( (short)request_port ); + memcpy( &sin6->sin6_addr, addr->data, addr->length ); + } +#endif + else { + /* Unknown address type */ + free( s ); + s = NULL; + } + return s; +} + +static void +UpdateListener( ARRAY8Ptr addr, void **closure ) +{ + struct socklist *s; + + *closure = NULL; + + if (addr == NULL) { + ARRAY8 tmpaddr; + struct in_addr in; +#if defined(IPv6) && defined(AF_INET6) + struct in6_addr in6 = in6addr_any; + tmpaddr.length = sizeof(in6); + tmpaddr.data = (CARD8Ptr) &in6; + UpdateListener( &tmpaddr, closure ); + if (*closure) + return; +#endif + in.s_addr = htonl( INADDR_ANY ); + tmpaddr.length = sizeof(in); + tmpaddr.data = (CARD8Ptr) ∈ + UpdateListener( &tmpaddr, closure ); + return; + } + + if (c_request_port == request_port && + (s = FindInList( listensocks, addr ))) + { + *closure = (void *)s; + s->ref = 1; + return; + } + + if (!(s = CreateSocklistEntry( addr ))) + return; + + if ((s->fd = CreateListeningSocket( s->addr, s->salen )) < 0) { + free( s->addr ); + free( s ); + return; + } + s->ref = 1; + s->next = listensocks; + listensocks = s; + *closure = (void *)s; +} + +#define JOIN_MCAST_GROUP 0 +#define LEAVE_MCAST_GROUP 1 + +static void +ChangeMcastMembership( struct socklist *s, struct socklist *g, int op ) +{ + int sockopt; + + switch (s->addr->sa_family) + { + case AF_INET: + { + struct ip_mreq mreq; + memcpy( &mreq.imr_multiaddr, + &((struct sockaddr_in *)g->addr)->sin_addr, + sizeof(struct in_addr) ); + memcpy( &mreq.imr_interface, + &((struct sockaddr_in *)s->addr)->sin_addr, + sizeof(struct in_addr) ); + if (op == JOIN_MCAST_GROUP) + sockopt = IP_ADD_MEMBERSHIP; + else + sockopt = IP_DROP_MEMBERSHIP; + if (setsockopt( s->fd, IPPROTO_IP, sockopt, + &mreq, sizeof(mreq) ) < 0) { + LogError( "XDMCP socket multicast %s to %s failed, errno %d\n", + (op == JOIN_MCAST_GROUP) ? "join" : "drop", + inet_ntoa( ((struct sockaddr_in *)g->addr)->sin_addr ), + errno ); + } else if (debugLevel & DEBUG_CORE) { + Debug( "XDMCP socket multicast %s to %s succeeded\n", + (op == JOIN_MCAST_GROUP) ? "join" : "drop", + inet_ntoa( ((struct sockaddr_in *)g->addr)->sin_addr ) ); + } + return; + } +#if defined(IPv6) && defined(AF_INET6) +# ifndef IPV6_JOIN_GROUP +# define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP +# endif +# ifndef IPV6_LEAVE_GROUP +# define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP +# endif + case AF_INET6: + { + struct ipv6_mreq mreq6; + memcpy( &mreq6.ipv6mr_multiaddr, + &((struct sockaddr_in6 *)g->addr)->sin6_addr, + sizeof(struct in6_addr) ); + mreq6.ipv6mr_interface = 0; /* TODO: fix this */ + if (op == JOIN_MCAST_GROUP) + sockopt = IPV6_JOIN_GROUP; + else + sockopt = IPV6_LEAVE_GROUP; + if (setsockopt( s->fd, IPPROTO_IPV6, sockopt, + &mreq6, sizeof(mreq6) ) < 0) + { + int saveerr = errno; + char addrbuf[INET6_ADDRSTRLEN]; + + inet_ntop( s->addr->sa_family, + &((struct sockaddr_in6 *)g->addr)->sin6_addr, + addrbuf, sizeof(addrbuf) ); + + LogError( "XDMCP socket multicast %s to %s failed, errno %d\n", + (op == JOIN_MCAST_GROUP) ? "join" : "drop", addrbuf, + saveerr ); + } else if (debugLevel & DEBUG_CORE) { + char addrbuf[INET6_ADDRSTRLEN]; + + inet_ntop( s->addr->sa_family, + &((struct sockaddr_in6 *)g->addr)->sin6_addr, + addrbuf, sizeof(addrbuf) ); + + Debug( "XDMCP socket multicast %s to %s succeeded\n", + (op == JOIN_MCAST_GROUP) ? "join" : "drop", addrbuf ); + } + return; + } +#endif + } +} + +static void +UpdateMcastGroup( ARRAY8Ptr addr, void **closure ) +{ + struct socklist *s = (struct socklist *)*closure; + struct socklist *g; + + if (!s) + return; + + /* Already in the group, mark & continue */ + if ((g = FindInList( s->mcastgroups, addr ))) { + g->ref = 1; + return; + } + + /* Need to join the group */ + if (!(g = CreateSocklistEntry( addr ))) + return; + + ChangeMcastMembership( s, g, JOIN_MCAST_GROUP ); + free( g ); +} + +/* Open or close listening sockets to match the current settings read in + from the access database. */ +void +UpdateListenSockets( void ) +{ + struct socklist *s, *g, **ls, **lg; + void *tmpPtr = NULL; + + /* Clear Ref bits - any not marked by UpdateCallback will be closed */ + for (s = listensocks; s; s = s->next) { + s->ref = 0; + for (g = s->mcastgroups; g; g = g->next) + g->ref = 0; + } + ForEachListenAddr( UpdateListener, UpdateMcastGroup, &tmpPtr ); + c_request_port = request_port; + for (ls = &listensocks; (s = *ls); ) + if (!s->ref) { + DestroyListeningSocket( s ); + *ls = s->next; + free( s ); + } else { + ls = &s->next; + for (lg = &s->mcastgroups; (g = *lg); ) + if (!g->ref) { + ChangeMcastMembership( s, g, LEAVE_MCAST_GROUP ); + *lg = g->next; + free( g ); + } else + lg = &g->next; + } +} + +int +AnyListenSockets( void ) +{ + return listensocks != NULL; +} + +int +ProcessListenSockets( FD_TYPE *reads ) +{ + struct socklist *s; + int ret = 0; + + for (s = listensocks; s; s = s->next) + if (FD_ISSET( s->fd, reads )) { + ProcessRequestSocket( s->fd ); + ret = 1; + } + return ret; +} + +#endif /* !STREAMSCONN && XDMCP */ diff --git a/kdm/backend/streams.c b/kdm/backend/streams.c new file mode 100644 index 000000000..f60fb955e --- /dev/null +++ b/kdm/backend/streams.c @@ -0,0 +1,127 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2002,2004 Oswald Buddenhagen <[email protected]> + +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 + * + * streams.c - Support for STREAMS + */ + +#include "dm.h" + +#if defined(XDMCP) && defined(STREAMSCONN) + +#include "dm_error.h" + +#include <fcntl.h> +#include <tiuser.h> +#include <netconfig.h> +#include <netdir.h> + +static int xdmcpFd = -1, c_request_port; + +void +UpdateListenSockets( void ) +{ + struct t_bind bind_addr; + struct netconfig *nconf; + struct nd_hostserv service; + struct nd_addrlist *servaddrs; + char bindbuf[15]; + int it; + + if (c_request_port == request_port) + return; + c_request_port = request_port; + + if (xdmcpFd != -1) { + CloseNClearCloseOnFork( xdmcpFd ); + UnregisterInput( xdmcpFd ); + xdmcpFd = -1; + } + + if (!request_port) + return; + + Debug( "creating UDP stream %d\n", request_port ); + + nconf = getnetconfigent( "udp" ); + if (!nconf) { + t_error( "getnetconfigent udp" ); + return; + } + + xdmcpFd = t_open( nconf->nc_device, O_RDWR, NULL ); + if (xdmcpFd == -1) { + LogError( "XDMCP stream creation failed\n" ); + t_error( "CreateWellKnownSockets(xdmcpFd): t_open failed" ); + return; + } + + service.h_host = HOST_SELF; + sprintf( bindbuf, "%d", request_port ); + service.h_serv = bindbuf; + netdir_getbyname( nconf, &service, &servaddrs ); + freenetconfigent( nconf ); + + bind_addr.qlen = 5; + bind_addr.addr.buf = servaddrs->n_addrs[0].buf; + bind_addr.addr.len = servaddrs->n_addrs[0].len; + bind_addr.addr.maxlen = servaddrs->n_addrs[0].len; + it = t_bind( xdmcpFd, &bind_addr, &bind_addr ); + netdir_free( (char *)servaddrs, ND_ADDRLIST ); + if (it < 0) { + LogError( "Error binding UDP port %d\n", request_port ); + t_error( "CreateWellKnownSockets(xdmcpFd): t_bind failed" ); + t_close( xdmcpFd ); + xdmcpFd = -1; + return; + } + RegisterCloseOnFork( xdmcpFd ); + RegisterInput( xdmcpFd ); +} + +int +AnyListenSockets( void ) +{ + return xdmcpFd != -1; +} + +int +ProcessRequestSockets( FD_TYPE *reads ) +{ + if (xdmcpFd >= 0 && FD_ISSET( xdmcpFd, reads )) { + ProcessRequestSocket( xdmcpFd ); + return 1; + } + return 0; +} + +#endif /* STREAMSCONN && XDMCP */ diff --git a/kdm/backend/util.c b/kdm/backend/util.c new file mode 100644 index 000000000..a5358cd65 --- /dev/null +++ b/kdm/backend/util.c @@ -0,0 +1,621 @@ +/* + +Copyright 1989, 1998 The Open Group +Copyright 2000-2005 Oswald Buddenhagen <[email protected]> + +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_error.h" + +#include <string.h> +#include <unistd.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> + +#if 0 /*def USG; this was hpux once upon a time */ +# define NEED_UTSNAME +#endif + +#ifdef NEED_UTSNAME +# include <sys/utsname.h> +#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 *b, *n; + + 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; +} + +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 +} + diff --git a/kdm/backend/xdmauth.c b/kdm/backend/xdmauth.c new file mode 100644 index 000000000..86257c651 --- /dev/null +++ b/kdm/backend/xdmauth.c @@ -0,0 +1,267 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2001,2003 Oswald Buddenhagen <[email protected]> + +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 + * + * generate authorization data for XDM-AUTHORIZATION-1 as per XDMCP spec + */ + +#include <config.h> + +#ifdef HASXDMAUTH + +#include "dm.h" +#include "dm_auth.h" +#include "dm_error.h" + +static char auth_name[256]; +static int auth_name_len; + +void +XdmInitAuth( unsigned short name_len, const char *name ) +{ + if (name_len > 256) + name_len = 256; + auth_name_len = name_len; + memmove( auth_name, name, name_len ); +} + +/* + * Generate authorization for XDM-AUTHORIZATION-1 + * + * When being used with XDMCP, 8 bytes are generated for the session key + * (sigma), as the random number (rho) is already shared between xdm and + * the server. Otherwise, we'll prepend a random number to pass in the file + * between xdm and the server (16 bytes total) + */ + +static Xauth * +XdmGetAuthHelper( unsigned short namelen, const char *name, int includeRho ) +{ + Xauth *new; + + if (!(new = (Xauth *)Malloc( sizeof(Xauth) ))) + return (Xauth *)0; + new->family = FamilyWild; + new->address_length = 0; + new->address = 0; + new->number_length = 0; + new->number = 0; + if (includeRho) + new->data_length = 16; + else + new->data_length = 8; + + new->data = (char *)Malloc( new->data_length ); + if (!new->data) { + free( (char *)new ); + return (Xauth *)0; + } + new->name = (char *)Malloc( namelen ); + if (!new->name) { + free( (char *)new->data ); + free( (char *)new ); + return (Xauth *)0; + } + memmove( (char *)new->name, name, namelen ); + new->name_length = namelen; + if (!GenerateAuthData( (char *)new->data, new->data_length )) { + free( (char *)new->name ); + free( (char *)new->data ); + free( (char *)new ); + return (Xauth *)0; + } + /* + * set the first byte of the session key to zero as it + * is a DES key and only uses 56 bits + */ + ((char *)new->data)[new->data_length - 8] = '\0'; + Debug( "local server auth %02[*hhx\n", new->data_length, new->data ); + return new; +} + +Xauth * +XdmGetAuth( unsigned short namelen, const char *name ) +{ + return XdmGetAuthHelper( namelen, name, TRUE ); +} + +#ifdef XDMCP + +void +XdmGetXdmcpAuth( struct protoDisplay *pdpy, + unsigned short authorizationNameLen, + const char *authorizationName ) +{ + Xauth *fileauth, *xdmcpauth; + + if (pdpy->fileAuthorization && pdpy->xdmcpAuthorization) + return; + xdmcpauth = XdmGetAuthHelper( authorizationNameLen, authorizationName, + FALSE ); + if (!xdmcpauth) + return; + fileauth = (Xauth *)Malloc( sizeof(Xauth) ); + if (!fileauth) { + XauDisposeAuth( xdmcpauth ); + return; + } + /* build the file auth from the XDMCP auth */ + *fileauth = *xdmcpauth; + fileauth->name = Malloc( xdmcpauth->name_length ); + fileauth->data = Malloc( 16 ); + fileauth->data_length = 16; + if (!fileauth->name || !fileauth->data) { + XauDisposeAuth( xdmcpauth ); + if (fileauth->name) + free( (char *)fileauth->name ); + if (fileauth->data) + free( (char *)fileauth->data ); + free( (char *)fileauth ); + return; + } + /* + * for the file authorization, prepend the random number (rho) + * which is simply the number we've been passing back and + * forth via XDMCP + */ + memmove( fileauth->name, xdmcpauth->name, xdmcpauth->name_length ); + memmove( fileauth->data, pdpy->authenticationData.data, 8 ); + memmove( fileauth->data + 8, xdmcpauth->data, 8 ); + Debug( "accept packet auth %02[*hhx\nauth file auth %02[*hhx\n", + xdmcpauth->data_length, xdmcpauth->data, + fileauth->data_length, fileauth->data ); + /* encrypt the session key for its trip back to the server */ + XdmcpWrap( (unsigned char *)xdmcpauth->data, (unsigned char *)&pdpy->key, + (unsigned char *)xdmcpauth->data, 8 ); + pdpy->fileAuthorization = fileauth; + pdpy->xdmcpAuthorization = xdmcpauth; +} + +#define atox(c) ('0' <= c && c <= '9' ? c - '0' : \ + 'a' <= c && c <= 'f' ? c - 'a' + 10 : \ + 'A' <= c && c <= 'F' ? c - 'A' + 10 : -1) + +static int +HexToBinary( char *key ) +{ + char *out, *in; + int top, bottom; + + in = key + 2; + out= key; + while (in[0] && in[1]) { + top = atox( in[0] ); + if (top == -1) + return 0; + bottom = atox( in[1] ); + if (bottom == -1) + return 0; + *out++ = (top << 4) | bottom; + in += 2; + } + if (in[0]) + return 0; + *out++ = '\0'; + return 1; +} + +/* + * Search the Keys file for the entry matching this display. This + * routine accepts either plain ascii strings for keys, or hex-encoded numbers + */ + +static int +XdmGetKey( struct protoDisplay *pdpy, ARRAY8Ptr displayID ) +{ + FILE *keys; + char line[1024], id[1024], key[1024]; + int keylen; + + Debug( "lookup key for %.*s\n", displayID->length, displayID->data ); + keys = fopen( keyFile, "r" ); + if (!keys) + return FALSE; + while (fgets( line, sizeof(line), keys )) { + if (line[0] == '#' || sscanf( line, "%s %s", id, key ) != 2) + continue; + bzero( line, sizeof(line) ); + Debug( "key entry for %\"s %d bytes\n", id, strlen( key ) ); + if (strlen( id ) == displayID->length && + !strncmp( id, (char *)displayID->data, displayID->length )) + { + if (!strncmp( key, "0x", 2 ) || !strncmp( key, "0X", 2 )) + if (!HexToBinary( key )) + break; + keylen = strlen( key ); + while (keylen < 7) + key[keylen++] = '\0'; + pdpy->key.data[0] = '\0'; + memmove( pdpy->key.data + 1, key, 7 ); + bzero( key, sizeof(key) ); + fclose( keys ); + return TRUE; + } + } + bzero( line, sizeof(line) ); + bzero( key, sizeof(key) ); + fclose( keys ); + return FALSE; +} + +/*ARGSUSED*/ +int +XdmCheckAuthentication( struct protoDisplay *pdpy, + ARRAY8Ptr displayID, + ARRAY8Ptr authenticationName ATTR_UNUSED, + ARRAY8Ptr authenticationData ) +{ + XdmAuthKeyPtr incoming; + + if (!XdmGetKey( pdpy, displayID )) + return FALSE; + if (authenticationData->length != 8) + return FALSE; + XdmcpUnwrap( authenticationData->data, (unsigned char *)&pdpy->key, + authenticationData->data, 8 ); + Debug( "request packet auth %02[*hhx\n", + authenticationData->length, authenticationData->data ); + if (!XdmcpCopyARRAY8( authenticationData, &pdpy->authenticationData )) + return FALSE; + incoming = (XdmAuthKeyPtr)authenticationData->data; + XdmcpIncrementKey( incoming ); + XdmcpWrap( authenticationData->data, (unsigned char *)&pdpy->key, + authenticationData->data, 8 ); + return TRUE; +} + +#endif /* XDMCP */ +#endif /* HASXDMAUTH (covering the entire file) */ diff --git a/kdm/backend/xdmcp.c b/kdm/backend/xdmcp.c new file mode 100644 index 000000000..2925a6bbc --- /dev/null +++ b/kdm/backend/xdmcp.c @@ -0,0 +1,1160 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2002 Sun Microsystems, Inc. All rights reserved. +Copyright 2001-2004 Oswald Buddenhagen <[email protected]> + +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 + * + * xdmcp.c - Support for XDMCP + */ + +#include <config.h> + +#ifdef XDMCP + +#include "dm.h" +#include "dm_error.h" +#include "dm_auth.h" +#include "dm_socket.h" + +#include <sys/types.h> +#include <ctype.h> + +#include <netdb.h> +#if defined(IPv6) && defined(AF_INET6) +# include <arpa/inet.h> +#endif + +/* + * Forward reference + */ +static void broadcast_respond( struct sockaddr *from, int fromlen, int length, int fd ); +static void forward_respond (struct sockaddr *from, int fromlen, int length, int fd); +static void manage( struct sockaddr *from, int fromlen, int length, int fd ); +static void query_respond( struct sockaddr *from, int fromlen, int length, int fd ); +static void request_respond( struct sockaddr *from, int fromlen, int length, int fd ); +static void send_accept( struct sockaddr *to, int tolen, CARD32 sessionID, ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData, ARRAY8Ptr authorizationName, ARRAY8Ptr authorizationData, int fd ); +static void send_alive( struct sockaddr *from, int fromlen, int length, int fd ); +static void send_decline( struct sockaddr *to, int tolen, ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData, ARRAY8Ptr status, int fd ); +static void send_failed( struct sockaddr *from, int fromlen, const char *name, CARD32 sessionID, const char *reason, int fd ); +static void send_refuse( struct sockaddr *from, int fromlen, CARD32 sessionID, int fd ); +static void send_unwilling( struct sockaddr *from, int fromlen, ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd ); +static void send_willing( struct sockaddr *from, int fromlen, ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd ); + + +static XdmcpBuffer buffer; + +static void +sendForward( CARD16 connectionType, ARRAY8Ptr address, char *closure ) +{ +#ifdef AF_INET + struct sockaddr_in in_addr; +#endif +#if defined(IPv6) && defined(AF_INET6) + struct sockaddr_in6 in6_addr; +#endif +#ifdef AF_DECnet +#endif + struct sockaddr *addr; + int addrlen; + + switch (connectionType) { +#ifdef AF_INET + case FamilyInternet: + addr = (struct sockaddr *)&in_addr; + bzero( (char *)&in_addr, sizeof(in_addr) ); +# ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + in_addr.sin_len = sizeof(in_addr); +# endif + in_addr.sin_family = AF_INET; + in_addr.sin_port = htons( (short)XDM_UDP_PORT ); + if (address->length != 4) + return; + memmove( (char *)&in_addr.sin_addr, address->data, address->length ); + addrlen = sizeof(struct sockaddr_in); + break; +#endif +#if defined(IPv6) && defined(AF_INET6) + case FamilyInternet6: + addr = (struct sockaddr *)&in6_addr; + bzero( (char *)&in6_addr, sizeof(in6_addr) ); +# ifdef SIN6_LEN + in6_addr.sin6_len = sizeof(in6_addr); +# endif + in6_addr.sin6_family = AF_INET6; + in6_addr.sin6_port = htons( (short)XDM_UDP_PORT ); + if (address->length != 16) + return; + memmove( (char *)&in6_addr.sin6_addr, address->data, address->length ); + addrlen = sizeof(struct sockaddr_in6); + break; +#endif +#ifdef AF_DECnet + case FamilyDECnet: +#endif + default: + return; + } + XdmcpFlush( (int)closure, &buffer, (XdmcpNetaddr)addr, addrlen ); + return; +} + +static void +ClientAddress( struct sockaddr *from, + ARRAY8Ptr addr, /* return */ + ARRAY8Ptr port, /* return */ + CARD16 *type ) /* return */ +{ + int length, family; + char *data; + + data = NetaddrPort( (XdmcpNetaddr)from, &length ); + XdmcpAllocARRAY8( port, length ); + memmove( port->data, data, length ); + port->length = length; + + family = ConvertAddr( (XdmcpNetaddr)from, &length, &data ); + XdmcpAllocARRAY8( addr, length ); + memmove( addr->data, data, length ); + addr->length = length; + + *type = family; +} + +static void +all_query_respond( struct sockaddr *from, int fromlen, + ARRAYofARRAY8Ptr authenticationNames, + xdmOpCode type, int fd ) +{ + ARRAY8Ptr authenticationName; + ARRAY8 status; + ARRAY8 addr; + CARD16 connectionType; + int family; + int length; + + family = ConvertAddr( (XdmcpNetaddr)from, &length, &(addr.data) ); + addr.length = length; /* convert int to short */ + Debug( "all_query_respond: conntype=%d, addr=%02[*:hhx\n", + family, addr.length, addr.data ); + if (family < 0) + return; + connectionType = family; + + if (type == INDIRECT_QUERY) + RememberIndirectClient( &addr, connectionType ); + else + ForgetIndirectClient( &addr, connectionType ); + + authenticationName = ChooseAuthentication( authenticationNames ); + if (Willing( &addr, connectionType, authenticationName, &status, type )) + send_willing( from, fromlen, authenticationName, &status, fd ); + else + if (type == QUERY) + send_unwilling( from, fromlen, authenticationName, &status, fd ); + XdmcpDisposeARRAY8( &status ); +} + +static void +indirect_respond( struct sockaddr *from, int fromlen, int length, int fd ) +{ + ARRAYofARRAY8 queryAuthenticationNames; + ARRAY8 clientAddress; + ARRAY8 clientPort; + CARD16 connectionType; + int expectedLen; + int i; + XdmcpHeader header; + int localHostAsWell; + + Debug( "<indirect> respond %d\n", length ); + if (!XdmcpReadARRAYofARRAY8( &buffer, &queryAuthenticationNames )) + return; + expectedLen = 1; + for (i = 0; i < (int)queryAuthenticationNames.length; i++) + expectedLen += 2 + queryAuthenticationNames.data[i].length; + if (length == expectedLen) { + ClientAddress( from, &clientAddress, &clientPort, &connectionType ); + /* + * set up the forward query packet + */ + header.version = XDM_PROTOCOL_VERSION; + header.opcode = (CARD16)FORWARD_QUERY; + header.length = 0; + header.length += 2 + clientAddress.length; + header.length += 2 + clientPort.length; + header.length += 1; + for (i = 0; i < (int)queryAuthenticationNames.length; i++) + header.length += 2 + queryAuthenticationNames.data[i].length; + XdmcpWriteHeader( &buffer, &header ); + XdmcpWriteARRAY8( &buffer, &clientAddress ); + XdmcpWriteARRAY8( &buffer, &clientPort ); + XdmcpWriteARRAYofARRAY8( &buffer, &queryAuthenticationNames ); + + localHostAsWell = + ForEachMatchingIndirectHost( &clientAddress, connectionType, + sendForward, (char *)fd ); + + XdmcpDisposeARRAY8( &clientAddress ); + XdmcpDisposeARRAY8( &clientPort ); + if (localHostAsWell) + all_query_respond( from, fromlen, &queryAuthenticationNames, + INDIRECT_QUERY, fd ); + } else + Debug( "<indirect> length error got %d expect %d\n", + length, expectedLen ); + XdmcpDisposeARRAYofARRAY8( &queryAuthenticationNames ); +} + +void +ProcessRequestSocket( int fd ) +{ + XdmcpHeader header; +#if defined(IPv6) && defined(AF_INET6) + struct sockaddr_storage addr; +#else + struct sockaddr addr; +#endif + int addrlen = sizeof(addr); + + Debug( "ProcessRequestSocket\n" ); + bzero( (char *)&addr, sizeof(addr) ); + if (!XdmcpFill( fd, &buffer, (XdmcpNetaddr)&addr, &addrlen )) { + Debug( "XdmcpFill failed\n" ); + return; + } + if (!XdmcpReadHeader( &buffer, &header )) { + Debug( "XdmcpReadHeader failed\n" ); + return; + } + if (header.version != XDM_PROTOCOL_VERSION) { + Debug( "XDMCP header version read was %d, expected %d\n", + header.version, XDM_PROTOCOL_VERSION ); + return; + } + Debug( "header: %d %d %d\n", header.version, header.opcode, header.length ); + switch (header.opcode) { + case BROADCAST_QUERY: + broadcast_respond( (struct sockaddr *)&addr, addrlen, header.length, fd ); + break; + case QUERY: + query_respond( (struct sockaddr *)&addr, addrlen, header.length, fd ); + break; + case INDIRECT_QUERY: + indirect_respond( (struct sockaddr *)&addr, addrlen, header.length, fd ); + break; + case FORWARD_QUERY: + forward_respond ((struct sockaddr *)&addr, addrlen, header.length, fd); + break; + case REQUEST: + request_respond( (struct sockaddr *)&addr, addrlen, header.length, fd ); + break; + case MANAGE: + manage( (struct sockaddr *)&addr, addrlen, header.length, fd ); + break; + case KEEPALIVE: + send_alive( (struct sockaddr *)&addr, addrlen, header.length, fd ); + break; + } +} + +/* + * respond to a request on the UDP socket. + */ + +static void +direct_query_respond( struct sockaddr *from, int fromlen, + int length, xdmOpCode type, int fd ) +{ + ARRAYofARRAY8 queryAuthenticationNames; + int expectedLen; + int i; + + if (!XdmcpReadARRAYofARRAY8( &buffer, &queryAuthenticationNames )) + return; + expectedLen = 1; + for (i = 0; i < (int)queryAuthenticationNames.length; i++) + expectedLen += 2 + queryAuthenticationNames.data[i].length; + if (length == expectedLen) + all_query_respond( from, fromlen, &queryAuthenticationNames, type, fd ); + XdmcpDisposeARRAYofARRAY8( &queryAuthenticationNames ); +} + +static void +query_respond( struct sockaddr *from, int fromlen, int length, int fd ) +{ + Debug( "<query> respond %d\n", length ); + direct_query_respond( from, fromlen, length, QUERY, fd ); +} + +static void +broadcast_respond( struct sockaddr *from, int fromlen, int length, int fd ) +{ + direct_query_respond( from, fromlen, length, BROADCAST_QUERY, fd ); +} + +/* computes an X display name */ + +static char * +NetworkAddressToName( CARD16 connectionType, ARRAY8Ptr connectionAddress, + struct sockaddr *originalAddress, CARD16 displayNumber ) +{ + switch (connectionType) { + case FamilyInternet: +#if defined(IPv6) && defined(AF_INET6) + case FamilyInternet6: +#endif + { + CARD8 *data; + struct hostent *hostent; + char *hostname = NULL; + char *name; + const char *localhost; + int multiHomed = 0; + int type; +#if defined(IPv6) && defined(AF_INET6) + struct addrinfo *ai = NULL, *nai, hints; + char dotted[INET6_ADDRSTRLEN]; + + if (connectionType == FamilyInternet6) + type = AF_INET6; + else +#endif + type = AF_INET; + + data = connectionAddress->data; + hostent = gethostbyaddr( (char *)data, + connectionAddress->length, type ); + if (hostent) { + if (sourceAddress) { +#if defined(IPv6) && defined(AF_INET6) + bzero( &hints, sizeof(hints) ); + hints.ai_flags = AI_CANONNAME; + if (!getaddrinfo( hostent->h_name, NULL, &hints, &ai )) { + hostname = ai->ai_canonname; + for (nai = ai->ai_next; nai; nai = nai->ai_next) + if (ai->ai_protocol == nai->ai_protocol && + memcmp( ai->ai_addr, nai->ai_addr, + ai->ai_addrlen )) + multiHomed = 1; + } +#else + hostent = gethostbyname( hostent->h_name ); + if (hostent && hostent->h_addrtype == AF_INET) { + multiHomed = hostent->h_addr_list[1] != NULL; + hostname = hostent->h_name; + } +#endif + } else + hostname = hostent->h_name; + } + + localhost = localHostname(); + + /* + * protect against bogus host names + */ + if (hostname && *hostname && *hostname != '.' && !multiHomed) { + if (!strcmp( localhost, hostname )) + ASPrintf( &name, "localhost:%d", displayNumber ); + else { + if (removeDomainname) { + char *localDot, *remoteDot; + + /* check for a common domain name. This + * could reduce names by recognising common + * super-domain names as well, but I don't think + * this is as useful, and will confuse more + * people + */ + if ((localDot = strchr( localhost, '.' )) && + (remoteDot = strchr( hostname, '.' ))) + { + /* smash the name in place; it won't + * be needed later. + */ + if (!strcmp( localDot+1, remoteDot+1 )) + *remoteDot = '\0'; + } + } + + ASPrintf( &name, "%s:%d", hostname, displayNumber ); + } + } else { +#if defined(IPv6) && defined(AF_INET6) + if (multiHomed) { + if (connectionType == FamilyInternet) { + data = (CARD8 *) + &((struct sockaddr_in *)originalAddress)->sin_addr; + } else { + data = (CARD8 *) + &((struct sockaddr_in6 *)originalAddress)->sin6_addr; + } + } + inet_ntop( type, data, dotted, sizeof(dotted) ); + ASPrintf( &name, "%s:%d", dotted, displayNumber ); +#else + if (multiHomed) + data = (CARD8 *) + &((struct sockaddr_in *)originalAddress)->sin_addr; + ASPrintf( &name, "%[4|'.'hhu:%d", data, displayNumber ); +#endif + } +#if defined(IPv6) && defined(AF_INET6) + if (ai) + freeaddrinfo( ai ); +#endif + return name; + } +#ifdef DNET + case FamilyDECnet: + return NULL; +#endif /* DNET */ + default: + return NULL; + } +} + +/*ARGSUSED*/ +static void +forward_respond ( struct sockaddr *from, int fromlen ATTR_UNUSED, + int length, int fd) +{ + ARRAY8 clientAddress; + ARRAY8 clientPort; + ARRAYofARRAY8 authenticationNames; + struct sockaddr *client; + int clientlen; + int expectedLen; + int i; + + Debug( "<forward> respond %d\n", length ); + clientAddress.length = 0; + clientAddress.data = 0; + clientPort.length = 0; + clientPort.data = 0; + authenticationNames.length = 0; + authenticationNames.data = 0; + if (XdmcpReadARRAY8( &buffer, &clientAddress ) && + XdmcpReadARRAY8( &buffer, &clientPort ) && + XdmcpReadARRAYofARRAY8( &buffer, &authenticationNames )) + { + expectedLen = 0; + expectedLen += 2 + clientAddress.length; + expectedLen += 2 + clientPort.length; + expectedLen += 1; /* authenticationNames */ + for (i = 0; i < (int)authenticationNames.length; i++) + expectedLen += 2 + authenticationNames.data[i].length; + if (length == expectedLen) { + int j; + + j = 0; + for (i = 0; i < (int)clientPort.length; i++) + j = j * 256 + clientPort.data[i]; + Debug( "<forward> client address (port %d) %[*hhu\n", j, + clientAddress.length, clientAddress.data ); + switch (from->sa_family) { +#ifdef AF_INET + case AF_INET: + { + struct sockaddr_in in_addr; + + if (clientAddress.length != 4 || clientPort.length != 2) + goto badAddress; + bzero( (char *)&in_addr, sizeof(in_addr) ); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + in_addr.sin_len = sizeof(in_addr); +#endif + in_addr.sin_family = AF_INET; + memmove( &in_addr.sin_addr, clientAddress.data, 4 ); + memmove( (char *)&in_addr.sin_port, clientPort.data, 2 ); + client = (struct sockaddr *)&in_addr; + clientlen = sizeof(in_addr); + all_query_respond( client, clientlen, &authenticationNames, + FORWARD_QUERY, fd ); + } + break; +#endif +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: + { + struct sockaddr_in6 in6_addr; + + if (clientAddress.length != 16 || clientPort.length != 2) + goto badAddress; + bzero( (char *)&in6_addr, sizeof(in6_addr) ); +#ifdef SIN6_LEN + in6_addr.sin6_len = sizeof(in6_addr); +#endif + in6_addr.sin6_family = AF_INET6; + memmove( &in6_addr,clientAddress.data,clientAddress.length ); + memmove( (char *)&in6_addr.sin6_port, clientPort.data, 2 ); + client = (struct sockaddr *)&in6_addr; + clientlen = sizeof(in6_addr); + all_query_respond( client, clientlen, &authenticationNames, + FORWARD_QUERY, fd ); + } + break; +#endif +#ifdef AF_UNIX + case AF_UNIX: + { + struct sockaddr_un un_addr; + + if (clientAddress.length >= sizeof(un_addr.sun_path)) + goto badAddress; + bzero( (char *)&un_addr, sizeof(un_addr) ); + un_addr.sun_family = AF_UNIX; + memmove( un_addr.sun_path, clientAddress.data, clientAddress.length ); + un_addr.sun_path[clientAddress.length] = '\0'; + client = (struct sockaddr *)&un_addr; +#if defined(HAVE_STRUCT_SOCKADDR_IN_SIN_LEN) && !defined(__Lynx__) && defined(UNIXCONN) + un_addr.sun_len = strlen( un_addr.sun_path ); + clientlen = SUN_LEN( &un_addr ); +#else + clientlen = sizeof(un_addr); +#endif + all_query_respond( client, clientlen, &authenticationNames, + FORWARD_QUERY, fd ); + } + break; +#endif +#ifdef AF_CHAOS + case AF_CHAOS: + goto badAddress; +#endif +#ifdef AF_DECnet + case AF_DECnet: + goto badAddress; +#endif + } + } else + Debug( "<forward> length error got %d expect %d\n", length, expectedLen ); + } + badAddress: + XdmcpDisposeARRAY8( &clientAddress ); + XdmcpDisposeARRAY8( &clientPort ); + XdmcpDisposeARRAYofARRAY8( &authenticationNames ); +} + +static ARRAY8 Hostname; + +static void +send_willing( struct sockaddr *from, int fromlen, + ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd ) +{ + XdmcpHeader header; + + Debug( "send <willing> %.*s %.*s\n", authenticationName->length, + authenticationName->data, + status->length, + status->data ); + header.version = XDM_PROTOCOL_VERSION; + header.opcode = (CARD16)WILLING; + header.length = + 6 + authenticationName->length + Hostname.length + status->length; + XdmcpWriteHeader( &buffer, &header ); + XdmcpWriteARRAY8( &buffer, authenticationName ); + XdmcpWriteARRAY8( &buffer, &Hostname ); + XdmcpWriteARRAY8( &buffer, status ); + XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen ); +} + +static void +send_unwilling( struct sockaddr *from, int fromlen, + ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd ) +{ + XdmcpHeader header; + + Debug( "send <unwilling> %.*s %.*s\n", authenticationName->length, + authenticationName->data, + status->length, + status->data ); + header.version = XDM_PROTOCOL_VERSION; + header.opcode = (CARD16)UNWILLING; + header.length = 4 + Hostname.length + status->length; + XdmcpWriteHeader( &buffer, &header ); + XdmcpWriteARRAY8( &buffer, &Hostname ); + XdmcpWriteARRAY8( &buffer, status ); + XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen ); +} + +static unsigned long globalSessionID; + +#define NextSessionID() (++globalSessionID) + +void init_session_id( void ) +{ + /* Set randomly so we are unlikely to reuse id's from a previous + * incarnation so we don't say "Alive" to those displays. + * Start with low digits 0 to make debugging easier. + */ + globalSessionID = (time( (Time_t *)0 ) & 0x7fff) * 16000; + + Hostname.data = (char *)localHostname(); + Hostname.length = strlen( Hostname.data ); +} + +static ARRAY8 outOfMemory = { (CARD16)13, (CARD8Ptr)"Out of memory" }; +static ARRAY8 noValidAddr = { (CARD16)16, (CARD8Ptr)"No valid address" }; +static ARRAY8 noValidAuth = { (CARD16)22, (CARD8Ptr)"No valid authorization" }; +static ARRAY8 noAuthentic = { (CARD16)29, (CARD8Ptr)"XDM has no authentication key" }; + +static void +request_respond( struct sockaddr *from, int fromlen, int length, int fd ) +{ + CARD16 displayNumber; + ARRAY16 connectionTypes; + ARRAYofARRAY8 connectionAddresses; + ARRAY8 authenticationName; + ARRAY8 authenticationData; + ARRAYofARRAY8 authorizationNames; + ARRAY8 manufacturerDisplayID; + ARRAY8Ptr reason = 0; + int expectlen; + int i, j; + struct protoDisplay *pdpy; + ARRAY8 authorizationName, authorizationData; + ARRAY8Ptr connectionAddress; + + Debug( "<request> respond %d\n", length ); + connectionTypes.data = 0; + connectionAddresses.data = 0; + authenticationName.data = 0; + authenticationData.data = 0; + authorizationNames.data = 0; + authorizationName.length = 0; + authorizationData.length = 0; + manufacturerDisplayID.data = 0; + if (XdmcpReadCARD16( &buffer, &displayNumber ) && + XdmcpReadARRAY16( &buffer, &connectionTypes ) && + XdmcpReadARRAYofARRAY8( &buffer, &connectionAddresses ) && + XdmcpReadARRAY8( &buffer, &authenticationName ) && + XdmcpReadARRAY8( &buffer, &authenticationData ) && + XdmcpReadARRAYofARRAY8( &buffer, &authorizationNames ) && + XdmcpReadARRAY8( &buffer, &manufacturerDisplayID )) + { + expectlen = 0; + expectlen += 2; /* displayNumber */ + expectlen += 1 + 2 * connectionTypes.length; /* connectionTypes */ + expectlen += 1; /* connectionAddresses */ + for (i = 0; i < (int)connectionAddresses.length; i++) + expectlen += 2 + connectionAddresses.data[i].length; + expectlen += 2 + authenticationName.length; /* authenticationName */ + expectlen += 2 + authenticationData.length; /* authenticationData */ + expectlen += 1; /* authoriationNames */ + for (i = 0; i < (int)authorizationNames.length; i++) + expectlen += 2 + authorizationNames.data[i].length; + expectlen += 2 + manufacturerDisplayID.length; /* displayID */ + if (expectlen != length) { + Debug( "<request> length error got %d expect %d\n", + length, expectlen ); + goto abort; + } + if (connectionTypes.length == 0 || + connectionAddresses.length != connectionTypes.length) + { + reason = &noValidAddr; + pdpy = 0; + goto decline; + } + pdpy = FindProtoDisplay( (XdmcpNetaddr)from, fromlen, displayNumber ); + if (!pdpy) { + + /* Check this Display against the Manager's policy */ + reason = Accept( from, fromlen, displayNumber ); + if (reason) + goto decline; + + /* Check the Display's stream services against Manager's policy */ + i = SelectConnectionTypeIndex( &connectionTypes, + &connectionAddresses ); + if (i < 0) { + reason = &noValidAddr; + goto decline; + } + + /* The Manager considers this a new session */ + connectionAddress = &connectionAddresses.data[i]; + pdpy = NewProtoDisplay( (XdmcpNetaddr)from, fromlen, displayNumber, + connectionTypes.data[i], connectionAddress, + NextSessionID() ); + Debug( "NewProtoDisplay %p\n", pdpy ); + if (!pdpy) { + reason = &outOfMemory; + goto decline; + } + } + if (authorizationNames.length == 0) + j = 0; + else + j = SelectAuthorizationTypeIndex( &authenticationName, + &authorizationNames ); + if (j < 0) { + reason = &noValidAuth; + goto decline; + } + if (!CheckAuthentication( pdpy, + &manufacturerDisplayID, + &authenticationName, + &authenticationData )) + { + reason = &noAuthentic; + goto decline; + } + if (j < (int)authorizationNames.length) { + Xauth *auth; + SetProtoDisplayAuthorization( pdpy, + (unsigned short)authorizationNames.data[j].length, + (char *)authorizationNames.data[j].data ); + auth = pdpy->xdmcpAuthorization; + if (!auth) + auth = pdpy->fileAuthorization; + if (auth) { + authorizationName.length = auth->name_length; + authorizationName.data = (CARD8Ptr) auth->name; + authorizationData.length = auth->data_length; + authorizationData.data = (CARD8Ptr) auth->data; + } + } + if (pdpy) { + send_accept( from, fromlen, pdpy->sessionID, + &authenticationName, + &authenticationData, + &authorizationName, + &authorizationData, fd ); + } else { + decline: + send_decline( from, fromlen, &authenticationName, + &authenticationData, + reason, fd ); + if (pdpy) + DisposeProtoDisplay( pdpy ); + } + } + abort: + XdmcpDisposeARRAY16( &connectionTypes ); + XdmcpDisposeARRAYofARRAY8( &connectionAddresses ); + XdmcpDisposeARRAY8( &authenticationName ); + XdmcpDisposeARRAY8( &authenticationData ); + XdmcpDisposeARRAYofARRAY8( &authorizationNames ); + XdmcpDisposeARRAY8( &manufacturerDisplayID ); +} + +static void +send_accept( struct sockaddr *to, int tolen, CARD32 sessionID, + ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData, + ARRAY8Ptr authorizationName, ARRAY8Ptr authorizationData, + int fd ) +{ + XdmcpHeader header; + + Debug( "<accept> session ID %ld\n", (long)sessionID ); + header.version = XDM_PROTOCOL_VERSION; + header.opcode = (CARD16)ACCEPT; + header.length = 4; /* session ID */ + header.length += 2 + authenticationName->length; + header.length += 2 + authenticationData->length; + header.length += 2 + authorizationName->length; + header.length += 2 + authorizationData->length; + XdmcpWriteHeader( &buffer, &header ); + XdmcpWriteCARD32( &buffer, sessionID ); + XdmcpWriteARRAY8( &buffer, authenticationName ); + XdmcpWriteARRAY8( &buffer, authenticationData ); + XdmcpWriteARRAY8( &buffer, authorizationName ); + XdmcpWriteARRAY8( &buffer, authorizationData ); + XdmcpFlush( fd, &buffer, (XdmcpNetaddr)to, tolen ); +} + +static void +send_decline( struct sockaddr *to, int tolen, + ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData, + ARRAY8Ptr status, int fd ) +{ + XdmcpHeader header; + + Debug( "<decline> %.*s\n", status->length, status->data ); + header.version = XDM_PROTOCOL_VERSION; + header.opcode = (CARD16)DECLINE; + header.length = 0; + header.length += 2 + status->length; + header.length += 2 + authenticationName->length; + header.length += 2 + authenticationData->length; + XdmcpWriteHeader( &buffer, &header ); + XdmcpWriteARRAY8( &buffer, status ); + XdmcpWriteARRAY8( &buffer, authenticationName ); + XdmcpWriteARRAY8( &buffer, authenticationData ); + XdmcpFlush( fd, &buffer, (XdmcpNetaddr)to, tolen ); +} + +static void +manage( struct sockaddr *from, int fromlen, int length, int fd ) +{ + CARD32 sessionID; + CARD16 displayNumber; + ARRAY8 displayClass; + int expectlen; + struct protoDisplay *pdpy; + struct display *d; + char *name = NULL; + char *class2 = NULL; + XdmcpNetaddr from_save; + ARRAY8 clientAddress, clientPort; + CARD16 connectionType; + + Debug( "<manage> %d\n", length ); + displayClass.data = 0; + displayClass.length = 0; + if (XdmcpReadCARD32( &buffer, &sessionID ) && + XdmcpReadCARD16( &buffer, &displayNumber ) && + XdmcpReadARRAY8( &buffer, &displayClass )) + { + expectlen = 4 + /* session ID */ + 2 + /* displayNumber */ + 2 + displayClass.length; /* displayClass */ + if (expectlen != length) { + Debug( "<manage> length error got %d expect %d\n", length, expectlen ); + goto abort; + } + pdpy = FindProtoDisplay( (XdmcpNetaddr)from, fromlen, displayNumber ); + Debug( "<manage> session ID %ld, pdpy %p\n", (long)sessionID, pdpy ); + if (!pdpy || pdpy->sessionID != sessionID) { + /* + * We may have already started a session for this display + * but it hasn't seen the response in the form of an + * XOpenDisplay() yet. So check if it is in the list of active + * displays, and if so check that the session id's match. + * If all this is true, then we have a duplicate request that + * can be ignored. + */ + if (!pdpy && + (d = FindDisplayByAddress( (XdmcpNetaddr)from, fromlen, + displayNumber )) && + d->sessionID == sessionID) + { + Debug( "manage: got duplicate pkt, ignoring\n" ); + goto abort; + } + Debug( "session ID %ld refused\n", (long)sessionID ); + if (pdpy) + Debug( "existing session ID %ld\n", (long)pdpy->sessionID ); + send_refuse( from, fromlen, sessionID, fd ); + } else { + name = NetworkAddressToName( pdpy->connectionType, + &pdpy->connectionAddress, + from, + pdpy->displayNumber ); + if (!name) { + Debug( "could not compute display name\n" ); + send_failed( from, fromlen, "(no name)", sessionID, + "out of memory", fd ); + goto abort; + } + Debug( "computed display name: %s\n", name ); + if ((d = FindDisplayByName( name ))) { + Debug( "terminating active session for %s\n", d->name ); + StopDisplay( d ); + } + if (displayClass.length) { + if (!StrNDup( &class2, (char *)displayClass.data, + displayClass.length )) + { + send_failed( from, fromlen, name, sessionID, + "out of memory", fd ); + goto abort; + } + } + if (!(from_save = (XdmcpNetaddr)Malloc( fromlen ))) { + send_failed( from, fromlen, name, sessionID, + "out of memory", fd ); + goto abort; + } + memmove( from_save, from, fromlen ); + if (!(d = NewDisplay( name ))) { + free( (char *)from_save ); + send_failed( from, fromlen, name, sessionID, + "out of memory", fd ); + goto abort; + } + d->class2 = class2; + class2 = 0; + d->displayType = dForeign | dTransient | dFromXDMCP; + d->sessionID = pdpy->sessionID; + d->from.data = (unsigned char *)from_save; + d->from.length = fromlen; + d->displayNumber = pdpy->displayNumber; + ClientAddress( from, &clientAddress, &clientPort, + &connectionType ); + d->useChooser = 0; + d->xdmcpFd = fd; + if (IsIndirectClient( &clientAddress, connectionType )) { + Debug( "IsIndirectClient\n" ); + ForgetIndirectClient( &clientAddress, connectionType ); + if (UseChooser( &clientAddress, connectionType )) { + d->useChooser = 1; + Debug( "use chooser for %s\n", d->name ); + } + } + d->clientAddr = clientAddress; + d->connectionType = connectionType; + XdmcpDisposeARRAY8( &clientPort ); + if (pdpy->fileAuthorization) { + d->authorizations = (Xauth **)Malloc( sizeof(Xauth *) ); + if (!d->authorizations) { + free( (char *)from_save ); + free( (char *)d ); + send_failed( from, fromlen, name, sessionID, + "out of memory", fd ); + goto abort; + } + d->authorizations[0] = pdpy->fileAuthorization; + d->authNum = 1; + pdpy->fileAuthorization = 0; + } + DisposeProtoDisplay( pdpy ); + Debug( "starting display %s,%s\n", d->name, d->class2 ); + if (LoadDisplayResources( d ) < 0) { + LogError( "Unable to read configuration for display %s; " + "stopping it.\n", d->name ); + StopDisplay( d ); + } else + StartDisplay( d ); + CloseGetter(); + } + } +abort: + XdmcpDisposeARRAY8( &displayClass ); + if (name) + free( (char *)name ); + if (class2) + free( (char *)class2 ); +} + +void +SendFailed( struct display *d, const char *reason ) +{ + Debug( "display start failed, sending <failed>\n" ); + send_failed( (struct sockaddr *)(d->from.data), d->from.length, d->name, + d->sessionID, reason, d->xdmcpFd ); +} + +static void +send_failed( struct sockaddr *from, int fromlen, + const char *name, CARD32 sessionID, const char *reason, int fd ) +{ + char buf[360]; + XdmcpHeader header; + ARRAY8 status; + + sprintf( buf, "Session %ld failed for display %.260s: %s", + (long)sessionID, name, reason ); + Debug( "send_failed(%\"s)\n", buf ); + status.length = strlen( buf ); + status.data = (CARD8Ptr) buf; + header.version = XDM_PROTOCOL_VERSION; + header.opcode = (CARD16)FAILED; + header.length = 6 + status.length; + XdmcpWriteHeader( &buffer, &header ); + XdmcpWriteCARD32( &buffer, sessionID ); + XdmcpWriteARRAY8( &buffer, &status ); + XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen ); +} + +static void +send_refuse( struct sockaddr *from, int fromlen, CARD32 sessionID, int fd ) +{ + XdmcpHeader header; + + Debug( "send <refuse> %ld\n", (long)sessionID ); + header.version = XDM_PROTOCOL_VERSION; + header.opcode = (CARD16)REFUSE; + header.length = 4; + XdmcpWriteHeader( &buffer, &header ); + XdmcpWriteCARD32( &buffer, sessionID ); + XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen ); +} + +static void +send_alive( struct sockaddr *from, int fromlen, int length, int fd ) +{ + CARD32 sessionID; + CARD16 displayNumber; + struct display *d; + XdmcpHeader header; + CARD8 sendRunning; + CARD32 sendSessionID; + + Debug( "send <alive>\n" ); + if (XdmcpReadCARD16( &buffer, &displayNumber ) && + XdmcpReadCARD32( &buffer, &sessionID )) + { + if (length == 6) { + if (!(d = FindDisplayBySessionID( sessionID ))) + d = FindDisplayByAddress( (XdmcpNetaddr)from, fromlen, + displayNumber ); + sendRunning = 0; + sendSessionID = 0; + if (d && d->status == running) { + if (d->sessionID == sessionID) + sendRunning = 1; + sendSessionID = d->sessionID; + } + header.version = XDM_PROTOCOL_VERSION; + header.opcode = (CARD16)ALIVE; + header.length = 5; + Debug( "<alive>: %d %ld\n", sendRunning, (long)sendSessionID ); + XdmcpWriteHeader( &buffer, &header ); + XdmcpWriteCARD8( &buffer, sendRunning ); + XdmcpWriteCARD32( &buffer, sendSessionID ); + XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen ); + } + } +} + +char * +NetworkAddressToHostname( CARD16 connectionType, ARRAY8Ptr connectionAddress ) +{ + switch (connectionType) { + case FamilyInternet: +#if defined(IPv6) && defined(AF_INET6) + case FamilyInternet6: +#endif + { + struct hostent *he; + char *myDot, *name, *lname; + int af_type; +#if defined(IPv6) && defined(AF_INET6) + char dotted[INET6_ADDRSTRLEN]; + + if (connectionType == FamilyInternet6) + af_type = AF_INET6; + else +#endif + af_type = AF_INET; + + he = gethostbyaddr( (char *)connectionAddress->data, + connectionAddress->length, af_type ); + if (he) { +#if defined(IPv6) && defined(AF_INET6) + struct addrinfo *ai, *nai; + if (!getaddrinfo( he->h_name, NULL, NULL, &ai )) { + for (nai = ai; nai; nai = nai->ai_next) { + if (af_type == nai->ai_family && + !memcmp( nai->ai_family == AF_INET ? + (char *)&((struct sockaddr_in *)nai->ai_addr)->sin_addr : + (char *)&((struct sockaddr_in6 *)nai->ai_addr)->sin6_addr, + connectionAddress->data, + connectionAddress->length )) + { + freeaddrinfo( ai ); + goto oki; + } + } + freeaddrinfo( ai ); +#else + if ((he = gethostbyname( he->h_name )) && + he->h_addrtype == AF_INET) + { + int i; + for (i = 0; he->h_addr_list[i]; i++) + if (!memcmp( he->h_addr_list[i], + connectionAddress->data, 4 )) + goto oki; +#endif + LogError( "DNS spoof attempt or misconfigured resolver.\n" ); + } + goto gotnone; + oki: + if (StrDup( &name, he->h_name ) && + !strchr( name, '.' ) && + (myDot = strchr( localHostname(), '.' ))) + { + if (ASPrintf( &lname, "%s%s", name, myDot )) { +#if defined(IPv6) && defined(AF_INET6) + if (!getaddrinfo( lname, NULL, NULL, &ai )) { + for (nai = ai; nai; nai = nai->ai_next) { + if (af_type == nai->ai_family && + !memcmp( nai->ai_family == AF_INET ? + (char *)&((struct sockaddr_in *)nai->ai_addr)->sin_addr : + (char *)&((struct sockaddr_in6 *)nai->ai_addr)->sin6_addr, + connectionAddress->data, + connectionAddress->length )) + { + freeaddrinfo( ai ); + free( name ); + return lname; + } + } + freeaddrinfo( ai ); + } +#else + if ((he = gethostbyname( lname )) && + he->h_addrtype == AF_INET) + { + int i; + for (i = 0; he->h_addr_list[i]; i++) + if (!memcmp( he->h_addr_list[i], + connectionAddress->data, 4 )) + { + free( name ); + return lname; + } + } +#endif + free( lname ); + } + } + } else { + gotnone: + /* can't get name, so use emergency fallback */ +#if defined(IPv6) && defined(AF_INET6) + inet_ntop( af_type, connectionAddress->data, + dotted, sizeof(dotted) ); + StrDup( &name, dotted ); +#else + ASPrintf( &name, "%[4|'.'hhu", connectionAddress->data ); +#endif + LogWarn( "Cannot convert Internet address %s to host name\n", + name ); + } + return name; + } +#ifdef DNET + case FamilyDECnet: + break; +#endif /* DNET */ + default: + break; + } + return 0; +} + +#endif /* XDMCP */ + diff --git a/kdm/config.def b/kdm/config.def new file mode 100644 index 000000000..ae026ba52 --- /dev/null +++ b/kdm/config.def @@ -0,0 +1,2615 @@ +# +# Copyright 2004-2005 Oswald Buddenhagen <[email protected]> +# +# 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 holders 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 holders. +# + +# The contents of this section are copied into config.ci verbatim. +<code> +#define RCVERMAJOR 2 +#define RCVERMINOR 3 + +#define KDMCONF KDE_CONFDIR "/kdm" +#define KDMDATA KDE_DATADIR "/kdm" + +#ifdef _AIX +# define HALT_CMD "/usr/sbin/shutdown -h now" +# define REBOOT_CMD "/usr/sbin/shutdown -r now" +#elif defined(BSD) +# define HALT_CMD "/sbin/shutdown -h now" +# define REBOOT_CMD "/sbin/shutdown -r now" +#elif defined(__SVR4) +# define HALT_CMD "/usr/sbin/halt" +# define REBOOT_CMD "/usr/sbin/reboot" +#else +# define HALT_CMD "/sbin/halt" +# define REBOOT_CMD "/sbin/reboot" +#endif + +#if defined(BSD) || defined(__linux__) +# define DEF_USER_PATH "/usr/local/bin:/usr/bin:/bin:" XBINDIR ":/usr/games" +# define DEF_SYSTEM_PATH "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:" XBINDIR +#else +# define DEF_USER_PATH "/usr/local/bin:/usr/bin:/bin:" XBINDIR ":/usr/games:/usr/ucb" +# define DEF_SYSTEM_PATH "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:" XBINDIR ":/etc:/usr/ucb" +#endif + +#if 0 /*def HASXDMAUTH*/ +# define DEF_AUTH_NAME "XDM-AUTHORIZATION-1,MIT-MAGIC-COOKIE-1" +#else +# define DEF_AUTH_NAME "MIT-MAGIC-COOKIE-1" +#endif + +#ifdef __linux__ +# define HAVE_VTS +#elif defined(__sun__) +# define DEF_SERVER_TTY "console" +#elif defined(_AIX) +# define DEF_SERVER_TTY "lft0" +#else +# define DEF_SERVER_TTY "" +#endif + +#ifdef _AIX +# define DEF_SERVER_CMD XBINDIR "/X -T -force" +#elif defined(__linux__) || defined(__GNU__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +/* we just assume that any free *nix installation has a recent xfree86/xorg */ +# define DEF_SERVER_CMD XBINDIR "/X -br" +#else +# define DEF_SERVER_CMD XBINDIR "/X" +#endif +</code> + +# The contents of this section are copied mostly verbatim to the +# default/example configuration file. +# Everything indented with a space is considered a comment for the output; +# it is prefixed with a hash mark but otherwise copied verbatim (except +# for lines consisting of a single underscore, which generate empty comment +# lines). +# Section headers are "condensation seeds" for the Instance:s in the key +# definitions below. +<kdmrc> + &kdm; master configuration file + _ + Definition: the greeter is the login dialog, i.e., the part of &kdm; + which the user sees. + _ + You can configure every X-display individually. + Every display has a display name, which consists of a host name + (which is empty for local displays specified in {Static|Reserve}Servers), + a colon, and a display number. Additionally, a display belongs to a + display class (which can be ignored in most cases; the control center + does not support this feature at all). + Sections with display-specific settings have the formal syntax + "[X-" host [":" number [ "_" class ]] "-" sub-section "]" + You can use the "*" wildcard for host, number, and class. You may omit + trailing components; they are assumed to be "*" then. + The host part may be a domain specification like ".inf.tu-dresden.de". + It may also be "+", which means non-empty, i.e. remote displays only. + From which section a setting is actually taken is determined by these + rules: + - an exact match takes precedence over a partial match (for the host part), + which in turn takes precedence over a wildcard ("+" taking precedence + over "*") + - precedence decreases from left to right for equally exact matches + Example: display name "myhost:0", class "dpy". + [X-myhost:0_dpy] precedes + [X-myhost:0_*] (same as [X-myhost:0]) precedes + [X-myhost:*_dpy] precedes + [X-myhost:*_*] (same as [X-myhost]) precedes + [X-+:0_dpy] precedes + [X-*:0_dpy] precedes + [X-*:0_*] (same as [X-*:0]) precedes + [X-*:*_*] (same as [X-*]) + These sections do NOT match this display: + [X-hishost], [X-myhost:0_dec], [X-*:1], [X-:*] + If a setting is not found in any matching section, the default is used. + _ + Every comment applies to the following section or key. Note that all + comments will be lost if you change this file with the kcontrol frontend. + The defaults refer to &kdm;'s built-in values, not anything set in this file. + _ + Special characters need to be backslash-escaped (leading and trailing + spaces (\\s), tab (\\t), linefeed (\\n), carriage return (\\r) and the + backslash itself (\\\\)). + In lists, fields are separated with commas without whitespace in between. + Some command strings are subject to simplified sh-style word splitting: + single quotes (') and double quotes (") have the usual meaning; the backslash + quotes everything (not only special characters). Note that the backslashes + need to be doubled because of the two levels of quoting. + +[General] + +[Xdmcp] + +[Shutdown] + + Rough estimations about how many seconds &kdm; will spend at most on + - opening a connection to the X-server (OpenTime) if the attempt + - times out: OpenTimeout + - is refused: OpenRepeat * OpenDelay + - starting a local X-server (ServerTime): + ServerAttempts * (ServerTimeout + OpenDelay) + - starting a display: + - local display: ServerTime + OpenTime + - foreign display: StartAttempts * OpenTime + - &XDMCP; display: OpenTime (repeated indefinitely by client) + + Core config for all displays +[X-*-Core] + + Greeter config for all displays +[X-*-Greeter] + + Core config for local displays +[X-:*-Core] + + Greeter config for local displays +[X-:*-Greeter] + + Core config for 1st local display +[X-:0-Core] + + Greeter config for 1st local display +[X-:0-Greeter] +</kdmrc> + +# The contents of this section are copied into kdmrc-ref.docbook. +# The macro %REF% is replaced with the accumulated Description:s from the key +# definitions below. +<docu> +<chapter id="kdm-files"> +<title>The Files &kdm; Uses for Configuration</title> + +<para>This chapter documents the files that control &kdm;'s behavior. +Some of this can be also controlled from the &kcontrol; module, but +not all.</para> + +<sect1 id="kdmrc"> +<title>&kdmrc; - The &kdm; master configuration file</title> + +<para>The basic format of the file is <quote>INI-like</quote>. +Options are key/value pairs, placed in sections. +Everything in the file is case sensitive. +Syntactic errors and unrecognized key/section identifiers cause &kdm; to +issue non-fatal error messages.</para> + +<para>Lines beginning with <literal>#</literal> are comments; empty lines +are ignored as well.</para> + +<para>Sections are denoted by +<literal>[</literal><replaceable>Name of Section</replaceable><literal>]</literal>. +</para> + +<para>You can configure every X-display individually.</para> +<para>Every display has a display name, which consists of a host name +(which is empty for local displays specified in <option>StaticServers</option> +or <option>ReserveServers</option>), a colon, and a display number. +Additionally, a display belongs to a +display class (which can be ignored in most cases).</para> + +<para>Sections with display-specific settings have the formal syntax +<literal>[X-</literal> <replaceable>host</replaceable> [ <literal>:</literal> <replaceable>number</replaceable> [ <literal>_</literal> <replaceable>class</replaceable> ] ] <literal>-</literal> <replaceable>sub-section</replaceable> <literal>]</literal> +</para> +<para>All sections with the same <replaceable>sub-section</replaceable> +make up a section class.</para> + +<para>You can use the wildcard <literal>*</literal> (match any) for +<replaceable>host</replaceable>, <replaceable>number</replaceable>, +and <replaceable>class</replaceable>. You may omit trailing components; +they are assumed to be <literal>*</literal> then. The host part may be a +domain specification like <replaceable>.inf.tu-dresden.de</replaceable> +or the wildcard <literal>+</literal> (match non-empty).</para> + +<para>From which section a setting is actually taken is determined by +these rules:</para> + +<itemizedlist> +<listitem> +<para>An exact match takes precedence over a partial match (for the +host part), which in turn takes precedence over a wildcard +(<literal>+</literal> taking precendence over <literal>*</literal>).</para> +</listitem> + +<listitem> +<para>Precedence decreases from left to right for equally exact matches.</para> +</listitem> + +<listitem> + +<para> +Example: display name <quote>myhost.foo:0</quote>, class <quote>dpy</quote> +</para> +<itemizedlist> +<listitem> +<para>[X-myhost.foo:0_dpy] precedes</para> +</listitem> +<listitem> +<para>[X-myhost.foo:0_*] (same as [X-myhost.foo:0]) precedes</para> +</listitem> +<listitem> +<para>[X-myhost.foo:*_dpy] precedes</para> +</listitem> +<listitem> +<para>[X-myhost.foo:*_*] (same as [X-myhost.foo]) precedes</para> +</listitem> +<listitem> +<para>[X-.foo:*_*] (same as [X-.foo]) precedes</para> +</listitem> +<listitem> +<para>[X-+:0_dpy] precedes</para> +</listitem> +<listitem> +<para>[X-*:0_dpy] precedes</para> +</listitem> +<listitem> +<para>[X-*:0_*] (same as [X-*:0]) precedes</para> +</listitem> +<listitem> +<para>[X-*:*_*] (same as [X-*]).</para> +</listitem> +<listitem> +<para>These sections do <emphasis>not</emphasis> match this display:</para> +<para>[X-hishost], [X-myhost.foo:0_dec], [X-*:1], [X-:*]</para> +</listitem> +</itemizedlist> + +</listitem> + +</itemizedlist> + +<para>Common sections are [X-*] (all displays), [X-:*] (all local displays) +and [X-:0] (the first local display).</para> + +<para>The format for all keys is +<userinput><option><replaceable>key</replaceable></option> <literal>=</literal> <parameter>value</parameter></userinput>. +Keys are only valid in the section class they are defined for. +Some keys do not apply to particular displays, in which case they are ignored. +</para> + +<para>If a setting is not found in any matching section, the default +is used.</para> + +<para>Special characters need to be backslash-escaped (leading and trailing +spaces (<literal>\s</literal>), tab (<literal>\t</literal>), linefeed +(<literal>\n</literal>), carriage return (<literal>\r</literal>) and the +backslash itself (<literal>\\</literal>)).</para> +<para>In lists, fields are separated with commas without whitespace in between. +</para> +<para>Some command strings are subject to simplified sh-style word splitting: +single quotes (<literal>'</literal>) and double quotes (<literal>"</literal>) +have the usual meaning; the backslash quotes everything (not only special +characters). Note that the backslashes need to be doubled because of the +two levels of quoting.</para> + +<note><para>A pristine &kdmrc; is very thoroughly commented. +All comments will be lost if you change this file with the +kcontrol frontend.</para></note> + +%REF% + +</sect1> + +<sect1 id="kdmrc-xservers"> +<title>Specifying permanent &X-Server;s</title> + +<para>Each entry in the <option>StaticServers</option> list indicates a +display which should constantly be +managed and which is not using &XDMCP;. This method is typically used only for +local &X-Server;s that are started by &kdm;, but &kdm; can manage externally +started (<quote>foreign</quote>) &X-Server;s as well, may they run on the +local machine or rather remotely.</para> + +<para>The formal syntax of a specification is +<screen> +<userinput><replaceable>display name</replaceable> [<literal>_</literal><replaceable>display class</replaceable>]</userinput> +</screen> +for all &X-Server;s. <quote>Foreign</quote> displays differ in having +a host name in the display name, may it be <literal>localhost</literal>.</para> + +<para>The <replaceable>display name</replaceable> must be something that can +be passed in the <option>-display</option> option to an X program. This string +is used to generate the display-specific section names, so be careful to match +the names. +The display name of &XDMCP; displays is derived from the display's address by +reverse host name resolution. For configuration purposes, the +<literal>localhost</literal> prefix from locally running &XDMCP; displays is +<emphasis>not</emphasis> stripped to make them distinguishable from local +&X-Server;s started by &kdm;.</para> + +<para>The <replaceable>display class</replaceable> portion is also used in the +display-specific sections. This is useful if you have a large collection of +similar displays (such as a corral of X terminals) and would like to set +options for groups of them. +When using &XDMCP;, the display is required to specify the display class, +so the manual for your particular X terminal should document the display +class string for your device. If it does not, you can run &kdm; in debug +mode and <command>grep</command> the log for <quote>class</quote>.</para> + +<para>The displays specified in <option>ReserveServers</option> will not be +started when &kdm; starts up, but when it is explicitly requested via +the command socket (or <acronym>FiFo</acronym>). +If reserve displays are specified, the &kde; menu will have a +<guilabel>Start New Session</guilabel> item near the bottom; use that to +activate a reserve display with a new login session. The monitor will switch +to the new display, and you will have a minute to login. If there are no more +reserve displays available, the menu item will be disabled.</para> + +<para>When &kdm; starts a session, it sets up authorization data for the +&X-Server;. For local servers, &kdm; passes +<command><option>-auth</option> <filename><replaceable>filename</replaceable></filename></command> +on the &X-Server;'s command line to point it at its authorization data. +For &XDMCP; displays, &kdm; passes the authorization data to the &X-Server; +via the <quote>Accept</quote> &XDMCP; message.</para> + +</sect1> + +<sect1 id="kdmrc-xaccess"> +<title>&XDMCP; access control</title> + +<para>The file specified by the <option>AccessFile</option> option provides +information which &kdm; uses to control access from displays requesting service +via &XDMCP;. +The file contains four types of entries: entries which control the response +to <quote>Direct</quote> and <quote>Broadcast</quote> queries, entries which +control the response to <quote>Indirect</quote> queries, macro definitions for +<quote>Indirect</quote> entries, and entries which control on which network +interfaces &kdm; listens for &XDMCP; queries. +Blank lines are ignored, <literal>#</literal> is treated as a comment +delimiter causing the rest of that line to be ignored, and <literal>\</literal> +causes an immediately following newline to be ignored, allowing indirect host +lists to span multiple lines. +</para> + +<para>The format of the <quote>Direct</quote> entries is simple, either a +host name or a pattern, which is compared against the host name of the display +device. +Patterns are distinguished from host names by the inclusion of one or more +meta characters; <literal>*</literal> matches any sequence of 0 or more +characters, and <literal>?</literal> matches any single character. +If the entry is a host name, all comparisons are done using network addresses, +so any name which converts to the correct network address may be used. Note +that only the first network address returned for a host name is used. +For patterns, only canonical host names are used in the comparison, so ensure +that you do not attempt to match aliases. +Host names from &XDMCP; queries always contain the local domain name +even if the reverse lookup returns a short name, so you can use +patterns for the local domain. +Preceding the entry with a <literal>!</literal> character causes hosts which +match that entry to be excluded. +To only respond to <quote>Direct</quote> queries for a host or pattern, +it can be followed by the optional <literal>NOBROADCAST</literal> keyword. +This can be used to prevent a &kdm; server from appearing on menus based on +<quote>Broadcast</quote> queries.</para> + +<para>An <quote>Indirect</quote> entry also contains a host name or pattern, +but follows it with a list of host names or macros to which the queries +should be forwarded. <quote>Indirect</quote> entries can be excluding as well, +in which case a (valid) dummy host name must be supplied to make the entry +distinguishable from a <quote>Direct</quote> entry. +If compiled with IPv6 support, multicast address groups may also be included +in the list of addresses the queries are forwarded to. +<!-- Not actually implemented! +Multicast addresses may be followed by an optional <literal>/</literal> +character and hop count. If no hop count is specified, the multicast hop count +defaults to 1, keeping the packet on the local network. For IPv4 multicasting, +the hop count is used as the TTL. +--> +If the indirect host list contains the keyword <literal>CHOOSER</literal>, +<quote>Indirect</quote> queries are not forwarded, but instead a host chooser +dialog is displayed by &kdm;. The chooser will send a <quote>Direct</quote> +query to each of the remaining host names in the list and offer a menu of +all the hosts that respond. The host list may contain the keyword +<literal>BROADCAST</literal>, to make the chooser send a +<quote>Broadcast</quote> query as well; note that on some operating systems, +UDP packets cannot be broadcast, so this feature will not work. +</para> + +<para>When checking access for a particular display host, each entry is scanned +in turn and the first matching entry determines the response. +<quote>Direct</quote> and <quote>Broadcast</quote> entries are ignored when +scanning for an <quote>Indirect</quote> entry and vice-versa.</para> + +<para>A macro definition contains a macro name and a list of host names and +other macros that the macro expands to. To distinguish macros from hostnames, +macro names start with a <literal>%</literal> character.</para> + +<para>The last entry type is the <literal>LISTEN</literal> directive. +The formal syntax is +<screen> +<userinput> <literal>LISTEN</literal> [<replaceable>interface</replaceable> [<replaceable>multicast list</replaceable>]]</userinput> +</screen> +If one or more <literal>LISTEN</literal> lines are specified, &kdm; listens +for &XDMCP; requests only on the specified interfaces. +<replaceable>interface</replaceable> may be a hostname or IP address +representing a network interface on this machine, or the wildcard +<literal>*</literal> to represent all available network interfaces. +If multicast group addresses are listed on a <literal>LISTEN</literal> line, +&kdm; joins the multicast groups on the given interface. For IPv6 multicasts, +the IANA has assigned ff0<replaceable>X</replaceable>:0:0:0:0:0:0:12b as the +permanently assigned range of multicast addresses for &XDMCP;. The +<replaceable>X</replaceable> in the prefix may be replaced by any valid scope +identifier, such as 1 for Node-Local, 2 for Link-Local, 5 for Site-Local, and +so on (see IETF RFC 2373 or its replacement for further details and scope +definitions). &kdm; defaults to listening on the Link-Local scope address +ff02:0:0:0:0:0:0:12b to most closely match the IPv4 subnet broadcast behavior. +If no <literal>LISTEN</literal> lines are given, &kdm; listens on all +interfaces and joins the default &XDMCP; IPv6 multicast group (when +compiled with IPv6 support). +To disable listening for &XDMCP; requests altogether, a +<literal>LISTEN</literal> line with no addresses may be specified, but using +the <literal>[Xdmcp]</literal> <option>Enable</option> option is preferred. +</para> + +</sect1> + +<sect1 id="kdm-scripts"> +<title>Supplementary programs</title> + +<para> +The following programs are run by &kdm; at various stages of a session. +They typically are shell scripts. +</para> + +<para> +The Setup, Startup and Reset programs are run as +<systemitem class="username">root</systemitem>, so they should be careful +about security. +Their first argument is <literal>auto</literal> if the session results +from an automatic login; otherwise, no arguments are passed to them. +</para> + +<sect2 id="kdmrc-xsetup"> +<title>Setup program</title> + +<para> +The <filename>Xsetup</filename> program is run after the &X-Server; is +started or reset, but before the greeter is offered. +This is the place to change the root background (if +<option>UseBackground</option> is disabled) or bring up other windows that +should appear on the screen along with the greeter. +</para> + +<para> +In addition to any specified by <option>ExportList</option>, +the following environment variables are passed:</para> +<variablelist> + <varlistentry> + <term>DISPLAY</term> + <listitem><para>the associated display name</para></listitem> + </varlistentry> + <varlistentry> + <term>PATH</term> + <listitem><para>the value of <option>SystemPath</option></para></listitem> + </varlistentry> + <varlistentry> + <term>SHELL</term> + <listitem><para>the value of <option>SystemShell</option></para></listitem> + </varlistentry> + <varlistentry> + <term>XAUTHORITY</term> + <listitem><para>may be set to an authority file</para></listitem> + </varlistentry> + <varlistentry> + <term>DM_CONTROL</term> + <listitem><para>the value of <option>FifoDir</option></para></listitem> + </varlistentry> +</variablelist> + +<para> Note that since &kdm; grabs the keyboard, any other windows will not be +able to receive keyboard input. They will be able to interact with the mouse, +however; beware of potential security holes here. If <option>GrabServer</option> +is set, <filename>Xsetup</filename> will not be able to connect to the display +at all. Resources for this program can be put into the file named by +<option>Resources</option>. +</para> + +</sect2> + +<sect2 id="kdmrc-xstartup"> +<title>Startup program</title> + +<para>The <filename>Xstartup</filename> program is run as +<systemitem class="username">root</systemitem> when the user logs in. +This is the place to put commands which add entries to +<filename>utmp</filename> (the <command>sessreg</command> program +may be useful here), mount users' home directories from file servers, +or abort the session if some requirements are not met (but note that on +modern systems, many of these tasks are already taken care of by +<acronym>PAM</acronym> modules).</para> + +<para>In addition to any specified by <option>ExportList</option>, +the following environment variables are passed:</para> +<variablelist> + <varlistentry> + <term>DISPLAY</term> + <listitem><para>the associated display name</para></listitem> + </varlistentry> + <varlistentry> + <term>HOME</term> + <listitem><para>the initial working directory of the user</para></listitem> + </varlistentry> + <varlistentry> + <term>LOGNAME</term> + <listitem><para>the username</para></listitem> + </varlistentry> + <varlistentry> + <term>USER</term> + <listitem><para>the username</para></listitem> + </varlistentry> + <varlistentry> + <term>PATH</term> + <listitem><para>the value of <option>SystemPath</option></para></listitem> + </varlistentry> + <varlistentry> + <term>SHELL</term> + <listitem><para>the value of <option>SystemShell</option></para></listitem> + </varlistentry> + <varlistentry> + <term>XAUTHORITY</term> + <listitem><para>may be set to an authority file</para></listitem> + </varlistentry> + <varlistentry> + <term>DM_CONTROL</term> + <listitem><para>the value of <option>FifoDir</option></para></listitem> + </varlistentry> +</variablelist> + +<para>&kdm; waits until this program exits before starting the user session. +If the exit value of this program is non-zero, &kdm; discontinues the session +and starts another authentication cycle.</para> + +</sect2> + +<sect2 id="kdmrc-xsession"> +<title>Session program</title> + +<para>The <filename>Xsession</filename> program is the command which is run +as the user's session. It is run with the permissions of the authorized user. +One of the keywords <literal>failsafe</literal>, <literal>default</literal> +or <literal>custom</literal>, or a string to <command>eval</command> by a +Bourne-compatible shell is passed as the first argument.</para> + +<para>In addition to any specified by <option>ExportList</option>, +the following environment variables are passed:</para> +<variablelist> + <varlistentry> + <term>DISPLAY</term> + <listitem><para>the associated display name</para></listitem> + </varlistentry> + <varlistentry> + <term>HOME</term> + <listitem><para>the initial working directory of the user</para></listitem> + </varlistentry> + <varlistentry> + <term>LOGNAME</term> + <listitem><para>the username</para></listitem> + </varlistentry> + <varlistentry> + <term>USER</term> + <listitem><para>the username</para></listitem> + </varlistentry> + <varlistentry> + <term>PATH</term> + <listitem><para>the value of <option>UserPath</option> + (or <option>SystemPath</option> for + <systemitem class="username">root</systemitem> user sessions)</para> + </listitem> + </varlistentry> + <varlistentry> + <term>SHELL</term> + <listitem><para>the user's default shell</para></listitem> + </varlistentry> + <varlistentry> + <term>XAUTHORITY</term> + <listitem><para>may be set to a non-standard authority file</para></listitem> + </varlistentry> + <varlistentry> + <term>KRBTKFILE</term> + <listitem><para>may be set to a Kerberos4 credentials cache name</para> + </listitem> + </varlistentry> + <varlistentry> + <term>KRB5CCNAME</term> + <listitem><para>may be set to a Kerberos5 credentials cache name</para> + </listitem> + </varlistentry> + <varlistentry> + <term>DM_CONTROL</term> + <listitem><para>the value of <option>FifoDir</option></para></listitem> + </varlistentry> + <varlistentry> + <term>XDM_MANAGED</term> + <listitem><para>will contain a comma-separated list of parameters the + session might find interesting, like the location of the command + <acronym>FiFo</acronym> and its capabilities, and which conversation + plugin was used for the login</para> + </listitem> + </varlistentry> + <varlistentry> + <term>DESKTOP_SESSION</term> + <listitem><para>the name of the session the user has chosen to run</para> + </listitem> + </varlistentry> +</variablelist> + +</sect2> + +<sect2 id="kdmrc-xreset"> +<title>Reset program</title> + +<para>Symmetrical with <filename>Xstartup</filename>, the +<filename>Xreset</filename> program is run after the user session has +terminated. Run as <systemitem class="username">root</systemitem>, it should +contain commands that undo the effects of commands in +<filename>Xstartup</filename>, removing entries from <filename>utmp</filename> +or unmounting directories from file servers.</para> + +<para>The environment variables that were passed to +<filename>Xstartup</filename> are also passed to <filename>Xreset</filename>. +</para> + +</sect2> + +</sect1> + +</chapter> +</docu> + + +# The rest of this file are section and key definitions for the options. +# The order of the keywords is fixed and everything is case sensitive. +# A keyword may expect supplementary data in the form of space-indented +# lines following it. Definitions are delimited by empty lines. +# +# Section definition: +# Section: <name> +# Section name. Section classes start with a dash. +# If: <expression> +# C preprocessor conditional for supporting this section. +# If it evaluates to false, all keys in this section are disabled as well. +# Description: +# A docbook description of this section is expected in the next lines. +# The contents are automatically enclosed in <para></para>. +# +# Option key definition: +# Key: <name> +# Option name. +# If: <expression> +# C preprocessor conditional for supporting this option. +# Type: (int|bool|enum|group|string|path|list) +# The option's data type. +# If the type is enum, the element definitions follow in the next lines: +# <term>[/<c #define>]: <docbook style description> +# Default: <default> +# Default value. string, path and list are copied verbatim and therefore +# must be already quoted appropriately. The other types are auto-quoted. +# If the default value is prefixed with a "*", a c #define def_<Key> is +# created. +# The default is automatically appended to the kdmrc comment and the +# documentation entry. +# CDefault: <verbose default> +# Append this instead of the real default to the two docs. The quoting +# rules are the same as for Default. +# DDefault: - +# If specified, the default value will not be appended to the documentation +# entry. The Description should mention the default then. Use this when +# the default is system-dependent. +# PostProc: <function> +# A function to postprocess the read config value before using it. +# User: (dummy|(core|greeter|greeter-c|dep|config)[(<variable>)][:font]) +# These entries specify which parts of kdm need the option in question: +# dummy: no user; entry is there only for syntactical correctness. +# dep: this option is an internal dependency for another option. +# config: this option configures the config reader itself. +# core: the kdm backend needs this option. +# greeter-c: the kdm frontend needs this option as a C data type. +# greeter: the kdm frontend needs this option as a C++/Qt data type. +# If a :font tag is appended, a string entry is converted to a QFont. +# If no variable name is specified, it will be derived from the Key by +# un-capitalizing it. +# Instance: (-|[#][<display spec>/](!|<value>)) +# These entries specify option instances for the default/example kdmrc. +# A "-" entry is a dummy for syntactical correctness. +# A prefixing hash mark will be copied to kdmrc. +# For options in a section class a display must be specified. +# For bool options "!" can be used as the value to specify the negation +# of the default. +# Update: <function>[/<number>] +# Call this function on each occurence of this option in genkdmconf. +# Options with higher numbers (default is 0) will be processed later. +# Merge: (xdm[:<resource>][(<function>)]|kdm:[<section>/][<key>][<function>]) +# Specify config options to merge from xdm and older kdm versions. +# Kdm options from the current version are automatically merged. +# When merging an xdm resource and no resource name is specified, it is +# derived from the Key by un-capitalizing it. +# When merging a kdm option, at least one of <section> and <key> must +# be given; an unspecified entity defaults to the current Section/Key. +# <section> may be a dash-prefixed section class. +# A function to postprocess the read value can be specified. +# Comment: [&|-] +# A kdmrc comment for this option is expected in the next lines. +# If "-" is given to Comment, no comment is generated at all. +# If "&" is given, the comment is derived from the Description below by +# applying some simple docbook interpretation to it. Note that the +# Description must be preformatted in this case. Use +# sed -ne 's/^\(.\{79,\}\)$/\1/p' < kdmrc +# after running "make install" to see whether all lines still fit. +# If Type is enum, a list of the previously defined element/description +# pairs is appended; the descriptions undergo docbook interpretation. +# Finally, a sentence with the Default (or CDefault, if given) is appended. +# Description: [!|-] +# A docbook description of this option is expected in the next lines. +# The contents are automatically enclosed in <para></para>. +# If "-" is given to Description, no comment is generated at all. +# If "!" is given, enums are not treated specially; otherwise, the macro +# %ENUM% is replaced with a list of the defined element/description pairs, +# or - if the macro is not present - the list is appended to the +# description. +# Finally, a sentence with the Default (or CDefault, if given) is appended, +# unless "DDefault: -" was specified. +# Each option entry generates an anchor named option-<lowercase(Key)>; +# it can be referenced in the main documentation. +# Do not forget to run "make ref" in kdebase/doc/kdm after changing +# Descriptions. + +Section: General +Description: + This section contains global options that do not fit into any specific section. + +Key: ConfigVersion +Type: string +Default: "" +CDefault: - +User: dummy +# will be overwritten +Instance: +Comment: + This option exists solely for the purpose of a clean automatic upgrade. + Do not even think about changing it! +Description: + This option exists solely for the purpose of clean automatic upgrades. + <emphasis>Do not</emphasis> change it, you may interfere with future + upgrades and this could result in &kdm; failing to run. + +Key: PAMService +If: defined(USE_PAM) +Type: string +Default: KDM_PAM_SERVICE +User: core +Instance: - +Comment: - +Description: - + +<legacy> +Proc: absorb_xservers +# note: this can miss Xservers from kdm for kde 2.2 because of stupid default. +Source: kdm:General/Xservers +Source: xdm:servers +</legacy> + +Key: StaticServers +Type: list +Default: ":0" +User: core +Instance: ":0" +Comment: + List of permanent displays. Displays with a hostname are foreign. A display + class may be specified separated by an underscore. +Description: + List of displays (&X-Server;s) permanently managed by &kdm;. Displays with a + hostname are foreign displays which are expected to be already running, + the others are local displays for which &kdm; starts an own &X-Server;; + see <option>ServerCmd</option>. Each display may belong to a display class; + append it to the display name separated by an underscore. + See <xref linkend="kdmrc-xservers"/> for the details. + +Key: ReserveServers +Type: list +Default: "" +User: core +Instance: ":1,:2,:3" +Comment: & +Description: + List of on-demand displays. See <option>StaticServers</option> for syntax. + +Key: ServerVTs +If: defined(HAVE_VTS) +Type: list +Default: "" +User: core +Instance: #"7,8,-9,-10" +Update: upd_servervts +Comment: + VTs to allocate to &X-Server;s. A negative number means that the VT will be + used only if it is free. If all VTs in this list are used up, the next free + one greater than the last one in this list will be allocated. +Description: + List of Virtual Terminals to allocate to &X-Server;s. For negative numbers the + absolute value is used, and the <acronym>VT</acronym> will be allocated only + if the kernel says it is free. If &kdm; exhausts this list, it will allocate + free <acronym>VT</acronym>s greater than the absolute value of the last entry + in this list. + Currently Linux only. + +Key: ConsoleTTYs +If: defined(HAVE_VTS) +Type: list +Default: "" +User: core +Instance: #"tty1,tty2,tty3,tty4,tty5,tty6" +Update: upd_consolettys +Comment: + TTYs (without /dev/) to monitor for activity while in console mode. +Description: + This option is for operating systems (<acronym>OS</acronym>s) with support + for virtual terminals (<acronym>VT</acronym>s), by both &kdm; and the + <acronym>OS</acronym>s itself. + Currently this applies only to Linux. + </para><para> + When &kdm; switches to console mode, it starts monitoring all + <acronym>TTY</acronym> lines listed here (without the leading + <literal>/dev/</literal>). + If none of them is active for some time, &kdm; switches back to the X login. + +Key: PidFile +Type: string +Default: "" +User: core +Instance: "/var/run/kdm.pid" +Merge: xdm +Comment: + Where &kdm; should store its PID (do not store if empty). +Description: + The filename specified will be created to contain an ASCII representation + of the process ID of the main &kdm; process; the PID will not be stored + if the filename is empty. + +Key: LockPidFile +Type: bool +Default: true +User: core +Instance: #! +Merge: xdm +Comment: + Whether &kdm; should lock the PID file to prevent having multiple &kdm; + instances running at once. Do not change unless you are brave. +Description: + This option controls whether &kdm; uses file locking to keep multiple + display managers from running onto each other. + +Key: AuthDir +Type: path +# differs from XDM +Default: "/var/run/xauth" +User: core +Instance: #"/tmp" +Merge: xdm(P_authDir) +Comment: + Where to store authorization files. +Description: + This names a directory under which &kdm; stores &X-Server; authorization + files while initializing the session. &kdm; expects the system to clean up + this directory from stale files on reboot. + </para><para> + The authorization file to be used for a particular display can be + specified with the <option>AuthFile</option> option in [X-*-Core]. + +Key: AutoRescan +Type: bool +Default: true +User: core +Instance: #! +Merge: xdm +Comment: + Whether &kdm; should automatically re-read configuration files, if it + finds them having changed. +Description: + This boolean controls whether &kdm; automatically re-reads its + configuration files if it finds them to have changed. + +Key: ExportList +Type: list +Default: "" +User: core +Instance: #"LD_LIBRARY_PATH,ANOTHER_IMPORTANT_VAR" +Merge: xdm(P_List) +Comment: & +Description: + Additional environment variables &kdm; should pass on to all programs it runs. + <envar>LD_LIBRARY_PATH</envar> and <envar>XCURSOR_THEME</envar> are good candidates; + otherwise, it should not be necessary very often. + +Key: RandomFile +If: !defined(ARC4_RANDOM) && !defined(DEV_RANDOM) +Type: string +Default: "/dev/mem" +User: core +Instance: #"" +Merge: xdm +Comment: + A file &kdm; should read entropy from. +Description: + If the system has no native entropy source like /dev/urandom (see + <option>RandomDevice</option>) and no entropy daemon like EGD (see + <option>PrngdSocket</option> and <option>PrngdPort</option>) is running, + &kdm; will fall back to its own pseudo-random number generator + that will, among other things, successively checksum parts of this file + (which, obviously, should change frequently). + </para><para> + This option does not exist on Linux and various BSDs. + +Key: PrngdSocket +If: !defined(ARC4_RANDOM) && !defined(DEV_RANDOM) +Type: string +# differs from xdm! +Default: "" +User: core +Instance: #"/tmp/entropy" +Merge: xdm +Comment: + A UNIX domain socket &kdm; should read entropy from. +Description: + If the system has no native entropy source like /dev/urandom (see + <option>RandomDevice</option>), read random data from a Pseudo-Random + Number Generator Daemon, + like EGD (http://egd.sourceforge.net) via this UNIX domain socket. + </para><para> + This option does not exist on Linux and various BSDs. + +Key: PrngdPort +If: !defined(ARC4_RANDOM) && !defined(DEV_RANDOM) +Type: int +Default: 0 +User: core +Instance: #4840 +Merge: xdm +Comment: + A TCP socket on localhost &kdm; should read entropy from. +Description: + Same as <option>PrngdSocket</option>, only use a TCP socket on localhost. + +Key: RandomDevice +If: !defined(ARC4_RANDOM) +Type: string +Default: "" +User: core +Instance: #"/dev/altrandom" +Merge: xdm +Comment: + A character device &kdm; should read entropy from. + Empty means use the system's preferred entropy device. +Description: + The path to a character device which &kdm; should read random data from. + Empty means to use the system's preferred entropy device if there is one. + </para><para> + This option does not exist on OpenBSD, as it uses the arc4_random + function instead. + +Key: FifoDir +Type: path +Default: *"/var/run/xdmctl" +User: core +Instance: #"/tmp" +Update: upd_fifodir +Comment: + Where the command FiFos should be created; make it empty to disable + them. +Description: + The directory in which the command <acronym>FiFo</acronym>s should + be created; make it empty to disable them. +# See <xref linkend="kdm-fifos"/> for the details. + +Key: FifoGroup +Type: group +Default: 0 +User: core +Instance: #xdmctl +Comment: & +Description: + The group to which the global command <acronym>FiFo</acronym> should belong; + can be either a name or a numerical ID. + +Key: DataDir +Type: path +Default: *"/var/lib/kdm" +User: greeter +Instance: #"" +Update: upd_datadir +Comment: + The directory in which &kdm; should store persistent working data. +Description: + The directory in which &kdm; should store persistent working data; such data + is, for example, the previous user that logged in on a particular display. + +Key: DmrcDir +Type: path +Default: "" +User: core +Instance: #"/nfs-shared/var/dmrcs" +Comment: & +Description: + The directory in which &kdm; should store users' <filename>.dmrc</filename> files. This is only + needed if the home directories are not readable before actually logging in + (like with AFS). + + +Section: Xdmcp +If: defined(XDMCP) +Description: + This section contains options that control &kdm;'s handling of + &XDMCP; requests. +# See <xref linkend="xdmcp-with-kdm"/> to find out what &XDMCP; is. + +Key: Enable +Type: bool +Default: true +User: dep(xdmcpEnable) +Instance: false +Comment: & +Description: + Whether &kdm; should listen to incoming &XDMCP; requests. + +Key: Port +Type: int +Default: 177 +PostProc: PrequestPort +User: core(request_port) +Instance: # +Merge: xdm:requestPort(P_requestPort) +Comment: + The UDP port on which &kdm; should listen for &XDMCP; requests. Do not change. +Description: + This indicates the UDP port number which &kdm; uses to listen for incoming + &XDMCP; requests. Unless you need to debug the system, leave this with its + default value. + +Key: KeyFile +Type: string +Default: "" +User: core +Instance: #KDMCONF "/kdmkeys" +Update: cp_keyfile +Merge: xdm +Comment: + File with the private keys of X-terminals. Required for XDM authentication. +Description: + XDM-AUTHENTICATION-1 style &XDMCP; authentication requires a private + key to be shared between &kdm; and the terminal. This option specifies + the file containing those values. Each entry in the file consists of a + display name and the shared key. + +Key: Xaccess +Type: string +# differs from xdm +Default: *KDMCONF "/Xaccess" +User: config(Xaccess) +Instance: #"" +Update: mk_xaccess +Merge: xdm:accessFile +Comment: + &XDMCP; access control file in the usual XDM-Xaccess format. +Description: + To prevent unauthorized &XDMCP; service and to allow forwarding of &XDMCP; + IndirectQuery requests, this file contains a database of hostnames which + are either allowed direct access to this machine, or have a list of hosts + to which queries should be forwarded to. The format of this file is + described in <xref linkend="kdmrc-xaccess"/>. + +Key: ChoiceTimeout +Type: int +Default: 15 +User: core +Instance: #10 +Merge: xdm +Comment: + Number of seconds to wait for display to respond after the user has + selected a host from the chooser. +Description: + Number of seconds to wait for the display to respond after the user has + selected a host from the chooser. If the display sends an &XDMCP; + IndirectQuery within this time, the request is forwarded to the chosen + host; otherwise, it is assumed to be from a new session and the chooser + is offered again. + +Key: RemoveDomainname +Type: bool +Default: true +User: core +Instance: #! +Merge: xdm +Comment: + Strip domain name from remote display names if it is equal to the local + domain. +Description: + When computing the display name for &XDMCP; clients, the name resolver will + typically create a fully qualified host name for the terminal. As this is + sometimes confusing, &kdm; will remove the domain name portion of the host + name if it is the same as the domain name of the local host when this option + is enabled. + +Key: SourceAddress +Type: bool +Default: false +User: core +Instance: #! +Merge: xdm +Comment: + Use the numeric IP address of the incoming connection on multihomed hosts + instead of the host name. +Description: + Use the numeric IP address of the incoming connection on multihomed hosts + instead of the host name. This is to avoid trying to connect on the wrong + interface which might be down at this time. + +Key: Willing +Type: string +Default: "" +User: core +# will be overwritten +Instance: # +Update: mk_willing +Merge: xdm +Merge: kdm:Xwilling +Comment: + The program which is invoked to dynamically generate replies to &XDMCP; + DirectQuery or BroadcastQuery requests. + If empty, no program is invoked and "Willing to manage" is sent. +Description: + This specifies a program which is run (as + <systemitem class="username">root</systemitem>) when an &XDMCP; + DirectQuery or BroadcastQuery is received and this host is configured + to offer &XDMCP; display management. The output of this program may be + displayed in a chooser window. If no program is specified, the string + <quote>Willing to manage</quote> is sent. + + +Section: Shutdown +Description: + This section contains global options concerning system shutdown. + +Key: HaltCmd +Type: string +Default: HALT_CMD +DDefault: - +User: core(cmdHalt) +Instance: #"" +Comment: + The command (subject to word splitting) to run to halt the system. +Description: + The command (subject to word splitting) to run to halt/poweroff the system. + </para><para> + The default is something reasonable for the system on which &kdm; was built, like + <command>/sbin/shutdown <option>-h</option> <parameter>now</parameter></command>. + +Key: RebootCmd +Type: string +Default: REBOOT_CMD +DDefault: - +User: core(cmdReboot) +Instance: #"" +Comment: + The command (subject to word splitting) to run to reboot the system. +Description: + The command (subject to word splitting) to run to reboot the system. + </para><para> + The default is something reasonable for the system &kdm; on which was built, like + <command>/sbin/shutdown <option>-r</option> <parameter>now</parameter></command>. + +Key: AllowFifo +Type: bool +Default: false +User: core(fifoAllowShutdown) +Instance: #! +Comment: & +Description: + Whether it is allowed to shut down the system via the global command <acronym>FiFo</acronym>. + +Key: AllowFifoNow +Type: bool +Default: true +User: core(fifoAllowNuke) +Instance: #! +Comment: + Whether it is allowed to abort active sessions when shutting down the + system via the global command FiFo. +Description: + Whether it is allowed to abort active sessions when shutting down the + system via the global command <acronym>FiFo</acronym>. + </para><para> + This will have no effect unless <option>AllowFifo</option> is enabled. + +Key: BootManager +Type: enum + None/BO_NONE: no boot manager + Grub/BO_GRUB: Grub boot manager + Lilo/BO_LILO: Lilo boot manager (Linux on i386 & x86-64 only) +Default: None +User: core +User: greeter +Instance: #Grub +Merge: kdm:UseLilo(P_UseLilo) +Comment: & +Description: + The boot manager &kdm; should use for offering boot options in the + shutdown dialog. + + +Section: -Core +Description: + This section class contains options concerning the configuration + of the &kdm; backend (core). + +Key: OpenDelay +Type: int +Default: 15 +User: core +Instance: #*/ +Merge: xdm(P_openDelay) +Comment: + How long to wait before retrying to connect a display. +Description: + See <option>OpenRepeat</option>. + +Key: OpenTimeout +Type: int +Default: 120 +User: core +Instance: #*/ +Merge: xdm +Comment: + How long to wait before timing out a display connection attempt. +Description: + See <option>OpenRepeat</option>. + +Key: OpenRepeat +Type: int +Default: 5 +User: core +Instance: #*/ +Merge: xdm +Comment: + How many connection attempts to make during a start attempt. Note that + a timeout aborts the entire start attempt. +Description: + These options control the behavior of &kdm; when attempting to open a + connection to an &X-Server;. <option>OpenDelay</option> is the length + of the pause (in seconds) between successive attempts, + <option>OpenRepeat</option> is the number of attempts to make and + <option>OpenTimeout</option> is the amount of time to spend on a + connection attempt. After <option>OpenRepeat</option> attempts have been + made, or if <option>OpenTimeout</option> seconds elapse in any particular + connection attempt, the start attempt is considered failed. + +Key: StartAttempts +Type: int +Default: 4 +User: core +Instance: #*/ +Merge: xdm +Comment: + Try at most that many times to start a display. If this fails, the display + is disabled. +Description: + How many times &kdm; should attempt to start a <literal>foreign</literal> + display listed in <option>StaticServers</option> before giving up + and disabling it. + Local displays are attempted only once, and &XDMCP; displays are retried + indefinitely by the client (unless the option <option>-once</option> + was given to the &X-Server;). + +Key: ServerAttempts +Type: int +Default: 1 +User: core +Instance: #:*/ +Merge: xdm +Comment: + How often to try to run the &X-Server;. Running includes executing it and + waiting for it to come up. +Description: + How many times &kdm; should attempt to start up a local &X-Server;. + Starting up includes executing it and waiting for it to come up. + +Key: ServerTimeout +Type: int +Default: 15 +User: core +Instance: #:*/ +Comment: + How long to wait for a local &X-Server; to come up. +Description: + How many seconds &kdm; should wait for a local &X-Server; to come up. + +Key: ServerCmd +Type: string +Default: DEF_SERVER_CMD +DDefault: - +User: core +Instance: :*/DEF_SERVER_CMD +Comment: + The command line to start the &X-Server;, without display number and VT spec. + This string is subject to word splitting. +Description: + The command line to start the &X-Server;, without display number and VT spec. + This string is subject to word splitting. + </para><para> + The default is something reasonable for the system on which &kdm; was built, + like <command>/usr/X11R6/bin/X</command>. + +Key: ServerArgsLocal +Type: string +Default: "" +User: core +Instance: :*/"-nolisten tcp" +Comment: & +Description: + Additional arguments for the &X-Server;s for local sessions. + This string is subject to word splitting. + +Key: ServerArgsRemote +Type: string +Default: "" +User: core +Instance: #:*/"" +Comment: & +Description: + Additional arguments for the &X-Server;s for remote sessions. + This string is subject to word splitting. + +Key: ServerVT +If: defined(HAVE_VTS) +Type: int +Default: 0 +User: core(reqSrvVT) +Instance: #:0/7 +Comment: + The VT the &X-Server; should run on; auto-assign if zero, don't assign if -1. + Better leave it zero and use ServerVTs. +Description: + The VT the &X-Server; should run on. + <option>ServerVTs</option> should be used instead of this option. + Leave it zero to let &kdm; assign a <acronym>VT</acronym> automatically. + Set it to <literal>-1</literal> to avoid assigning a <acronym>VT</acronym> + alltogether - this is required for setups with multiple physical consoles. + Currently Linux only. + +Key: ServerTTY +If: !defined(HAVE_VTS) +Type: string +Default: "" +User: core(console) +Instance: :0/DEF_SERVER_TTY +Comment: + The TTY line (without /dev/) the &X-Server; covers physically. +Description: + This option is for <acronym>OS</acronym>s without support for + <acronym>VT</acronym>s, either by &kdm; or the <acronym>OS</acronym> itself. + Currently this applies to all <acronym>OS</acronym>s but Linux. + </para><para> + When &kdm; switches to console mode, it starts monitoring this + <acronym>TTY</acronym> line (specified without the leading + <literal>/dev/</literal>) for activity. If the line is not used for some time, + &kdm; switches back to the X login. + +Key: PingInterval +Type: int +Default: 5 +User: core +User: greeter +Instance: #*/ +Merge: xdm +Comment: + Ping remote display every that many minutes. +Description: + See <option>PingTimeout</option>. + +Key: PingTimeout +Type: int +Default: 5 +User: core +User: greeter +Instance: #*/ +Merge: xdm +Comment: + Wait for a Pong that many minutes. +Description: + To discover when <emphasis>remote</emphasis> displays disappear, &kdm; + regularly pings them. + <option>PingInterval</option> specifies the time (in minutes) between the + pings and <option>PingTimeout</option> specifies the maximum amount of + time (in minutes) to wait for the terminal to respond to the request. If + the terminal does not respond, the session is declared dead and terminated. + </para><para> + If you frequently use X terminals which can become isolated from + the managing host, you may wish to increase the timeout. The only worry + is that sessions will continue to exist after the terminal has been + accidentally disabled. + +Key: TerminateServer +Type: bool +Default: false +User: core +Instance: #:*/! +Merge: xdm +Comment: + Restart instead of resetting the local &X-Server; after session exit. + Use it if the server leaks memory etc. +Description: + Whether &kdm; should restart the local &X-Server; after session exit instead + of resetting it. Use this if the &X-Server; leaks memory or crashes the system + on reset attempts. + +Key: ResetSignal +Type: int +Default: 1 +CDefault: 1 (SIGHUP) +User: core +Instance: #:*/ +Merge: xdm +Comment: + The signal needed to reset the local &X-Server;. +Description: + The signal number to use to reset the local &X-Server;. + +Key: TermSignal +Type: int +Default: 15 +CDefault: 15 (SIGTERM) +User: core +Instance: #:*/ +Merge: xdm +Comment: + The signal needed to terminate the local &X-Server;. +Description: + The signal number to use to terminate the local &X-Server;. + +Key: Authorize +Type: bool +Default: true +User: core +Instance: #:*/! +Merge: xdm +Comment: + Create X-authorizations for local displays. +Description: + Controls whether &kdm; generates and uses authorization for + <emphasis>local</emphasis> &X-Server; connections. + For &XDMCP; displays the authorization requested by the display is used; + foreign non-&XDMCP; displays do not support authorization at all. + +Key: AuthNames +Type: list +Default: DEF_AUTH_NAME +User: core +Instance: #:*/"" +Merge: xdm:authName +Comment: + Which X-authorization mechanisms should be used. +Description: + If <option>Authorize</option> is true, use the authorization mechanisms + listed herein. The MIT-MAGIC-COOKIE-1 authorization is always available; + XDM-AUTHORIZATION-1, SUN-DES-1 and MIT-KERBEROS-5 might be available as well, + depending on the build configuration. + +Key: ResetForAuth +Type: bool +Default: false +User: core +Instance: #:*/! +Merge: xdm +Comment: + Need to reset the &X-Server; to make it read initial Xauth file. +Description: + Some <emphasis>old</emphasis> &X-Server;s re-read the authorization file + at &X-Server; reset time, instead of when checking the initial connection. + As &kdm; generates the authorization information just before connecting to + the display, an old &X-Server; would not get up-to-date authorization + information. This option causes &kdm; to send SIGHUP to the &X-Server; + after setting up the file, causing an additional &X-Server; reset to occur, + during which time the new authorization information will be read. + +Key: AuthFile +Type: string +Default: "" +User: core(clientAuthFile) +Instance: #*/"" +Merge: xdm +Comment: + The name of this &X-Server;'s Xauth file. + If empty, a random name in the AuthDir directory will be used. +Description: + This file is used to communicate the authorization data from &kdm; to + the &X-Server;, using the <option>-auth</option> &X-Server; command line + option. It should be kept in a directory which is not world-writable + as it could easily be removed, disabling the authorization mechanism in + the &X-Server;. If not specified, a random name is generated from + <option>AuthDir</option> and the name of the display. + +Key: Resources +# XXX strictly speaking this is supposed to be a string list, i think. +Type: string +Default: "" +User: core +Instance: #*/"" +Update: cp_resources +Merge: xdm +Comment: + Specify a file with X-resources for the greeter, chooser and background. + The KDE frontend does not use this file, so you do not need it unless you + use another background generator than krootimage. +Description: + This option specifies the name of the file to be loaded by + <command>xrdb</command> as the resource database onto the root window + of screen 0 of the display. KDE programs generally do not use + X-resources, so this option is only needed if the <option>Setup</option> + program needs some X-resources. + +Key: Xrdb +Type: string +Default: XBINDIR "/xrdb" +User: core +Instance: #*/"" +Merge: xdm +Comment: + The xrdb program to use to read the above specified recources. + Subject to word splitting. +Description: + The <command>xrdb</command> program to use to read the X-resources file + specified in <option>Recources</option>. + The command is subject to word splitting. + +Key: Setup +Type: string +Default: "" +User: core +# will be overwritten +Instance: #*/"" +Update: mk_setup +Merge: xdm +Comment: + A program to run before the greeter is shown. Can be used to start an + xconsole or an alternative background generator. Subject to word splitting. +Description: + This string is subject to word splitting. + It specifies a program which is run (as + <systemitem class="username">root</systemitem>) before offering the + greeter window. This may be used to change the appearance of the screen + around the greeter window or to put up other windows (e.g., you may want + to run <command>xconsole</command> here). + The conventional name for a file used here is <command>Xsetup</command>. + See <xref linkend="kdmrc-xsetup"/>. + +Key: Startup +Type: string +Default: "" +User: core +# will be overwritten +Instance: #*/"" +Update: mk_startup +Merge: xdm +Comment: + A program to run before a user session starts. Subject to word splitting. +Description: + This string is subject to word splitting. + It specifies a program which is run (as + <systemitem class="username">root</systemitem>) after the user + authentication process succeeds. + The conventional name for a file used here is <command>Xstartup</command>. + See <xref linkend="kdmrc-xstartup"/>. + +Key: Reset +Type: string +Default: "" +User: core +# will be overwritten +Instance: #*/"" +Update: mk_reset +Merge: xdm +Comment: + A program to run after a user session exits. Subject to word splitting. +Description: + This string is subject to word splitting. + It specifies a program which is run (as + <systemitem class="username">root</systemitem>) after the session + terminates. + The conventional name for a file used here is <command>Xreset</command>. + See <xref linkend="kdmrc-xreset"/>. + +Key: Session +Type: string +Default: XBINDIR "/xterm -ls -T" +#Merge: xdm - incompatible! +User: core +# will be overwritten +Instance: #*/"" +Update: mk_session +Comment: + The program which is run as the user which logs in. It is supposed to + interpret the session argument (see SessionsDirs) and start an appropriate + session according to it. Subject to word splitting. +Description: + This string is subject to word splitting. + It specifies the session program to be executed (as the user owning + the session). + The conventional name for a file used here is <command>Xsession</command>. + See <xref linkend="kdmrc-xsession"/>. + +Key: FailsafeClient +Type: string +Default: XBINDIR "/xterm" +User: core +Instance: #*/"" +Merge: xdm +Comment: + The program to run if Session fails. +Description: + If the <option>Session</option> program fails to execute, &kdm; will + fall back to this program. This program is executed with no arguments, + but executes using the same environment variables as the session would + have had (see <xref linkend="kdmrc-xsession"/>). + +Key: UserPath +Type: string +Default: DEF_USER_PATH +DDefault: - +User: core +Instance: #*/"" +Merge: xdm +Comment: + The PATH for the Session program. +Description: + The <envar>PATH</envar> environment variable for + non-<systemitem class="username">root</systemitem> <option>Session</option>s. + </para><para> + The default depends on the system &kdm; was built on. + +Key: SystemPath +Type: string +Default: DEF_SYSTEM_PATH +DDefault: - +User: core +Instance: #*/"" +Merge: xdm +Comment: + The PATH for Setup, Startup and Reset, etc. +Description: + The <envar>PATH</envar> environment variable for all programs but + non-<systemitem class="username">root</systemitem> + <option>Session</option>s. Note that it is good practice not to include + <literal>.</literal> (the current directory) into this entry. + </para><para> + The default depends on the system &kdm; was built on. + +Key: SystemShell +Type: string +Default: "/bin/sh" +User: core +Instance: #*/"/bin/bash" +Merge: xdm +Comment: + The default system shell. +Description: + The <envar>SHELL</envar> environment variable for all programs but the + <option>Session</option>. + +Key: UserAuthDir +Type: path +Default: "/tmp" +User: core +Instance: #*/"" +Merge: xdm +Comment: + Where to put the user's &X-Server; authorization file if ~/.Xauthority + cannot be created. +Description: + When &kdm; is unable to write to the usual user authorization file + ($<envar>HOME</envar>/.Xauthority), it creates a unique file name in this + directory and points the environment variable <envar>XAUTHORITY</envar> + at the created file. + +Key: AutoReLogin +Type: bool +Default: false +User: core +Instance: #*/! +Merge: xdm +Comment: + Whether to automatically restart sessions after &X-Server; crashes. + Note that enabling this makes circumventing screen lockers other than + KDE's built-in one possible! +Description: + If enabled, &kdm; will automatically restart a session after an &X-Server; + crash (or if it is killed by Alt-Ctrl-BackSpace). Note that enabling this + feature opens a security hole: a secured display lock can be circumvented + (unless &kde;'s built-in screen locker is used). + +Key: AllowRootLogin +Type: bool +Default: true +User: core +User: greeter(showRoot) +Instance: */false +Merge: xdm +Comment: + Allow root logins? +Description: + If disabled, do not allow <systemitem class="username">root</systemitem> + (and any other user with UID = 0) to log in directly. + +Key: AllowNullPasswd +Type: bool +Default: true +User: core +# sensible? +Instance: */false +Instance: :*/true +Merge: xdm +Comment: + Allow to log in, when user has set an empty password? +Description: + If disabled, only users that have passwords assigned can log in. + +Key: AllowShutdown +Type: enum + None/SHUT_NONE: no <guilabel>Shutdown...</guilabel> menu entry is shown at all + Root/SHUT_ROOT: the <systemitem class="username">root</systemitem> password must be entered to shut down + All/SHUT_ALL: everybody can shut down the machine +Default: All +User: core +User: greeter +Instance: */Root +Instance: :*/All +Merge: kdm:-Greeter/ +Comment: & +Description: + Who is allowed to shut down the system. This applies both to the + greeter and to the command <acronym>FiFo</acronym>. + +Key: AllowSdForceNow +Type: enum + None: no forced shutdown is allowed at all + Root: the <systemitem class="username">root</systemitem> password must be entered to shut down forcibly + All: everybody can shut down the machine forcibly +Default: All +User: core(allowNuke) +User: greeter(allowNuke) +Instance: #*/Root +Comment: & +Description: + Who is allowed to abort active sessions when shutting down. + +Key: DefaultSdMode +Type: enum + Schedule: shut down after all active sessions exit (possibly at once) + TryNow: shut down, if no active sessions are open; otherwise, do nothing + ForceNow: shut down unconditionally +Default: Schedule +User: core(defSdMode) +User: greeter(defSdMode) +Instance: #*/ForceNow +Comment: & +Description: + The default choice for the shutdown condition/timing. + +Key: ScheduledSd +Type: enum + Never/SHUT_NEVER: not at all + Optional/SHUT_OPTION: as a button in the simple shutdown dialogs + Always/SHUT_ALWAYS: instead of the simple shutdown dialogs +Default: Never +User: greeter +Instance: #*/Optional +Comment: & +Description: + How to offer shutdown scheduling options: + +Key: NoPassEnable +Type: bool +Default: false +User: dep +Instance: #:*/true +Comment: & +Description: + Enable password-less logins on this display. <emphasis>Use with extreme care!</emphasis> + +Key: NoPassUsers +Type: list +Default: "" +PostProc: PnoPassUsers +User: core +Instance: #:*/"fred,ethel" +Merge: xdm(P_noPassUsers) +Comment: + The users that do not need to provide a password to log in. NEVER list root! + "*" means all non-root users. @<group> means all users in that group. +Description: + The users that do not need to provide a password to log in. + Items which are prefixed with <literal>@</literal> represent all users in the + user group named by that item. + <literal>*</literal> means all users but + <systemitem class="username">root</systemitem> + (and any other user with UID = 0). + <emphasis>Never</emphasis> list <systemitem class="username">root</systemitem>. + +Key: AutoLoginEnable +Type: bool +Default: false +User: dep +Instance: #:0/true +Comment: & +Description: + Enable automatic login. <emphasis>Use with extreme care!</emphasis> + +Key: AutoLoginAgain +Type: bool +Default: false +User: core(autoAgain) +User: greeter +Instance: #:0/true +Comment: & +Description: + If true, auto-login after logout. If false, auto-login is performed only + when a display session starts up. + +Key: AutoLoginDelay +Type: int +Default: 0 +User: core(autoDelay) +User: greeter +Instance: #:0/10 +Comment: + The delay in seconds before automatic login kicks in. +Description: + The delay in seconds before automatic login kicks in. This is also known as + <quote>Timed Login</quote>. + +Key: AutoLoginUser +Type: string +Default: "" +PostProc: PautoLoginX +User: core(autoUser) +User: greeter +Instance: #:0/"fred" +Merge: xdm:autoUser(P_autoUser) +Comment: & +Description: + The user to log in automatically. <emphasis>Never</emphasis> specify <systemitem class="username">root</systemitem>! + +Key: AutoLoginPass +Type: string +Default: "" +PostProc: PautoLoginX +User: core(autoPass) +Instance: #:0/"secret!" +Merge: xdm:autoPass(P_autoPass) +Comment: & +Description: + The password for the user to log in automatically. This is <emphasis>not</emphasis> required + unless the user is logged into a <acronym>NIS</acronym> or Kerberos domain. If you use this + option, you should <command>chmod <option>600</option> <filename>kdmrc</filename></command> for obvious reasons. + +Key: AutoLoginLocked +Type: bool +Default: false +User: core(autoLock) +Instance: #:0/! +Comment: & +Description: + Immediately lock the automatically started session. This works only with + KDE sessions. + +Key: SessionsDirs +Type: list +Default: KDMDATA "/sessions" +User: core +User: greeter-c +Instance: #*/"/etc/X11/sessions,/usr/share/xsessions" +Comment: + The directories containing session type definitions in .desktop format. +Description: + A list of directories containing session type definitions. +# See <xref linkend="kdmrc-sessions"> for details. + +Key: ClientLogFile +Type: string +Default: ".xsession-errors" +User: core +Instance: */".xsession-errors-%s" +Instance: :0/".xsession-errors" +Comment: + The file (relative to $HOME) to redirect the session output to. This is + a printf format string; one %s will be replaced with the display name. +Description: + The file (relative to the user's home directory) to redirect the session + output to. One occurrence of <parameter>%s</parameter> in this string will be + substituted with the display name. Use <parameter>%%</parameter> to obtain a + literal <literal>%</literal>. + +Key: UseSessReg +Type: bool +Default: true +User: core +Instance: #*/! +Comment: + Whether &kdm;'s built-in utmp/wtmp/lastlog registration should be used. +Description: + Specify whether &kdm;'s built-in utmp/wtmp/lastlog registration should + be used. If it is not, the tool <command>sessreg</command> should be used + in the <option>Startup</option> and <option>Reset</option> scripts, or, + alternatively, the pam_lastlog module should be used on + <acronym>PAM</acronym>-enabled systems. + + +Section: -Greeter +Description: + This section class contains options concerning the configuration + of the &kdm; frontend (greeter). + +Key: GUIStyle +Type: string +Default: "" +User: greeter +Instance: #*/"Windows" +Update: upd_guistyle +Comment: + Widget style of the greeter. "" means the built-in default which currently + is "Plastik". +Description: + Specify the widget style for the greeter. Empty means to use the + built-in default which currently is <literal>Plastik</literal>. + +Key: ColorScheme +Type: string +Default: "" +User: greeter +Instance: #*/"Pumpkin" +Comment: + Widget color scheme of the greeter. "" means the built-in default which + currently is yellowish grey with some light blue and yellow elements. +Description: + Specify the widget color scheme for the greeter. Empty means to use + the built-in default which currently is yellowish grey with some light + blue and yellow elements. + +Key: LogoArea +Type: enum + None/LOGO_NONE: nothing + Logo/LOGO_LOGO: the image specified by <option>LogoPixmap</option> + Clock/LOGO_CLOCK: a neat analog clock +Default: Clock +User: greeter +Instance: */Logo +Comment: + What should be shown in the greeter's logo are: +Description: + What should be shown in the greeter righthand of the input lines (if + <option>UserList</option> is disabled) or above them (if + <option>UserList</option> is enabled): + +Key: LogoPixmap +Type: string +Default: "" +User: greeter(logo) +Instance: */KDMDATA "/pics/kdelogo.png" +Comment: + The image to show when LogoArea=Logo. +Description: + The image to show in the greeter if <option>LogoArea</option> is + <literal>Logo</literal>. + +Key: GreeterPos +Type: string +Default: "50,50" +User: greeter-c +Instance: #*/"30,40" +Comment: + The relative coordinates (X,Y in percent) of the center of the greeter. +Description: + The relative coordinates (percentages of the screen size; X,Y) at which + the center of the greeter is put. &kdm; aligns the greeter to the edges + of the screen it would cross otherwise. + +Key: GreeterScreen +Type: int +Default: 0 +User: greeter +Instance: #*/-1 +Comment: & +Description: + The screen the greeter should be displayed on in multi-headed and Xinerama + setups. The numbering starts with 0. For Xinerama, it corresponds to the + listing order in the active ServerLayout section of XF86Config; -1 means + to use the upper-left screen, -2 means to use the upper-right screen. + +Key: GreetString +Type: string +Default: "Welcome to %s at %n" +User: greeter +Instance: #*/"K Desktop Environment (%n)" +Comment: + The headline in the greeter. The following character pairs are replaced: + - %d -> current display + - %h -> host name, possibly with domain name + - %n -> node name, most probably the host name without domain name + - %s -> the operating system + - %r -> the operating system's version + - %m -> the machine (hardware) type + - %% -> a single % +Description: + The headline in the greeter. An empty greeting means none at all. + </para><para> + The following character pairs are replaced by their value: + <variablelist> + <varlistentry> + <term><parameter>%d</parameter></term> + <listitem><para>name of the current display</para></listitem> + </varlistentry> + <varlistentry> + <term><parameter>%h</parameter></term> + <listitem><para>local host name, possibly with the + domain name</para></listitem> + </varlistentry> + <varlistentry> + <term><parameter>%n</parameter></term> + <listitem><para>local node name, most probably the host name without the + domain name</para></listitem> + </varlistentry> + <varlistentry> + <term><parameter>%s</parameter></term> + <listitem><para>operating system</para></listitem> + </varlistentry> + <varlistentry> + <term><parameter>%r</parameter></term> + <listitem><para>operating system version</para></listitem> + </varlistentry> + <varlistentry> + <term><parameter>%m</parameter></term> + <listitem><para>machine (hardware) type</para></listitem> + </varlistentry> + <varlistentry> + <term><parameter>%%</parameter></term> + <listitem><para>a single <literal>%</literal></para></listitem> + </varlistentry> + </variablelist> + +# This needs to come _in front_ of the font settings to be effective! +Key: AntiAliasing +Type: bool +Default: false +User: greeter +Instance: #*/! +Comment: & +Description: + Whether the fonts used in the greeter should be antialiased. + +Key: GreetFont +Type: string +Default: "Serif,20,5,0,50,0" +CDefault: "Serif,20,bold" +User: greeter:font +Instance: #*/"Serif,20,5,0,50,0" +Comment: & +Description: + The font for the greeter headline. + +Key: StdFont +Type: string +Default: "Sans Serif,10,5,0,50,0" +CDefault: "Sans Serif,10" +User: greeter(normalFont):font +Instance: #*/"Sans Serif,10,5,0,50,0" +Comment: & +Description: + The normal font used in the greeter. + +Key: FailFont +Type: string +Default: "Sans Serif,10,5,0,75,0" +CDefault: "Sans Serif,10,bold" +User: greeter:font +Instance: #*/"Sans Serif,10,5,0,75,0" +Comment: & +Description: + The font used for the <quote>Login Failed</quote> message. + +Key: NumLock +Type: enum + Off: turn off + On: turn on + Keep: do not change the state +Default: Keep +User: greeter(numLockStatus) +Instance: #*/Off +Comment: & +Description: + What to do with the Num Lock modifier for the time the greeter is running: + +Key: Language +Type: string +Default: "en_US" +User: greeter-c +Instance: #*/"de_DE" +Update: upd_language +Comment: & +Description: + Language and locale to use in the greeter, encoded like $<envar>LC_LANG</envar>. + +Key: UserCompletion +Type: bool +Default: false +User: greeter +Instance: #*/! +Comment: & +Description: + Enable autocompletion in the username line edit. + +Key: UserList +Type: bool +Default: true +User: greeter +Instance: #*/! +Comment: + Enable user list (names along with images) in the greeter. +Description: + Show a user list with unix login names, real names, and images in the greeter. + +Key: ShowUsers +Type: enum + NotHidden/SHOW_ALL: all users except those listed in HiddenUsers + Selected/SHOW_SEL: only the users listed in SelectedUsers +Default: NotHidden +User: greeter +Instance: #*/Selected +Update: upd_showusers +Comment: + User selection for UserCompletion and UserList: +Description: ! + This option controls which users will be shown in the user view + (<option>UserList</option>) and/or offered for autocompletion + (<option>UserCompletion</option>). + If it is <literal>Selected</literal>, <option>SelectedUsers</option> contains + the final list of users. + If it is <literal>NotHidden</literal>, the initial user list are all users + found on the system. Users contained in <option>HiddenUsers</option> are + removed from the list, just like all users with a UID greater than specified + in <option>MaxShowUID</option> and users with a non-zero UID less than + specified in <option>MinShowUID</option>. + Items in <option>SelectedUsers</option> and <option>HiddenUsers</option> + which are prefixed with <literal>@</literal> represent all users in the + user group named by that item. + Finally, the user list will be sorted alphabetically, if + <option>SortUsers</option> is enabled. + +Key: SelectedUsers +Type: list +Default: "" +User: greeter-c(users) +Instance: #*/"root,johndoe" +Merge: kdm:Users +Comment: + For ShowUsers=Selected. @<group> means all users in that group. +Description: + See <option>ShowUsers</option>. + +Key: HiddenUsers +Type: list +Default: "" +User: greeter-c(noUsers) +Instance: #*/"root" +# depends on {Min,Max}ShowUID +Update: upd_hiddenusers/1 +Merge: kdm:NoUsers +Comment: + For ShowUsers=NotHidden. @<group> means all users in that group. +Description: + See <option>ShowUsers</option>. + +Key: MinShowUID +Type: int +Default: 0 +User: greeter(lowUserId) +# will be overwritten +Instance: #*/ +Update: upd_minshowuid +Comment: + Special case of HiddenUsers: users with a non-zero UID less than this number + will not be shown as well. +Description: + See <option>ShowUsers</option>. + +Key: MaxShowUID +Type: int +Default: 65535 +User: greeter(highUserId) +# will be overwritten +Instance: #*/ +Update: upd_maxshowuid +Comment: + Complement to MinShowUID: users with a UID greater than this number will + not be shown as well. +Description: + See <option>ShowUsers</option>. + +Key: SortUsers +Type: bool +Default: true +User: greeter +Instance: #*/! +Comment: + If false, the users are listed in the order they appear in /etc/passwd. + If true, they are sorted alphabetically. +Description: + See <option>ShowUsers</option>. + +Key: FaceSource +Type: enum + AdminOnly/FACE_ADMIN_ONLY: from <filename><<option>FaceDir</option>>/$<envar>USER</envar>.face[.icon]</filename> + PreferAdmin/FACE_PREFER_ADMIN: prefer <<option>FaceDir</option>>, fallback on $<envar>HOME</envar> + PreferUser/FACE_PREFER_USER: ... and the other way round + UserOnly/FACE_USER_ONLY: from the user's <filename>$<envar>HOME</envar>/.face[.icon]</filename> +Default: AdminOnly +User: greeter +Instance: #*/PreferUser +Comment: + Specify, where the users' pictures should be taken from. +Description: + If <option>UserList</option> is enabled, this specifies where &kdm; gets the + images from: + </para> + %ENUM% + <para> + The images can be in any format Qt recognizes, but the filename + must match &kdm;'s expectations: <literal>.face.icon</literal> should be a + 48x48 icon, while <literal>.face</literal> should be a 300x300 image. + Currently the big image is used only as a fallback and is scaled down, + but in the future it might be displayed full-size in the logo area or a + tooltip. + +Key: FaceDir +Type: string +Default: *KDMDATA "/faces" +User: greeter +Instance: #*/"/usr/share/faces" +Update: upd_facedir +Comment: + The directory containing the user images if FaceSource is not UserOnly. +Description: + See <option>FaceSource</option>. + +Key: PreselectUser +Type: enum + None/PRESEL_NONE: do not preselect any user + Previous/PRESEL_PREV: the user which successfully logged in last time + Default/PRESEL_DEFAULT: the user specified in the <option>DefaultUser</option> option +Default: None +User: greeter(preselUser) +Instance: #*/Previous +Instance: :*/Previous +Instance: #:0/Default +Comment: + Specify, if/which user should be preselected for log in. +Description: + Specify, if/which user should be preselected for log in: + </para> + %ENUM% + <para> + If <option>FocusPasswd</option> is enabled and a user was preselected, + the cursor is placed in the password input field automatically. + </para> + <note><para>Enabling user preselection can be considered a security hole, + as it presents a valid login name to a potential attacker, so he + <quote>only</quote> needs to guess the password. On the other hand, + one could set <option>DefaultUser</option> to a fake login name.</para></note> + <para> + +Key: DefaultUser +Type: string +Default: "" +User: greeter +Instance: #:0/"johndoe" +Comment: + The user to preselect if PreselectUser=Default. +Description: + See <option>PreselectUser</option>. + +Key: FocusPasswd +Type: bool +Default: false +User: greeter +Instance: #*/! +Instance: :*/true +Comment: + If this is true, the password input line is focused automatically if + a user is preselected. +Description: + See <option>PreselectUser</option>. + +Key: EchoMode +Type: enum + OneStar: <literal>*</literal> is shown for every typed letter + ThreeStars: <literal>***</literal> is shown for every typed letter + NoEcho: nothing is shown at all, the cursor does not move +# HACK! This must be in sync with KPasswordEdit::EchoModes (kpassdlg.h) +Default: OneStar +User: greeter +Instance: #*/NoEcho +Comment: & +Description: + The password input fields cloak the typed in text. Specify, how to do it: + +Key: UseBackground +Type: bool +Default: true +User: greeter +Instance: #*/! +Comment: + If true, krootimage will be automatically started by &kdm;; otherwise, the + Setup script should be used to setup the background. +Description: + If enabled, &kdm; will automatically start the <command>krootimage</command> + program to set up the background; otherwise, the <option>Setup</option> + program is responsible for the background. + +Key: BackgroundCfg +Type: string +Default: *KDMCONF "/backgroundrc" +User: greeter-c +Instance: #*/"" +Update: handBgCfg +Comment: + The configuration file to be used by krootimage. +Description: + The configuration file to be used by <command>krootimage</command>. + It contains a section named <literal>[Desktop0]</literal> like + <filename>kdesktoprc</filename> does. Its options are not described + herein; guess their meanings or use the control center. + +Key: GrabServer +Type: bool +Default: false +User: greeter-c +Instance: #*/! +Comment: + Hold the &X-Server; grabbed the whole time the greeter is visible. This + may be more secure, but it will disable any background and other + X-clients started from the Setup script. +Description: + To improve security, the greeter grabs the &X-Server; and then the keyboard + when it starts up. This option specifies if the &X-Server; grab should be held + for the duration of the name/password reading. When disabled, the &X-Server; + is ungrabbed after the keyboard grab succeeds; otherwise, the &X-Server; is + grabbed until just before the session begins. + </para> + <note><para>Enabling this option disables <option>UseBackground</option> and + <option>Setup</option>.</para></note> + <para> + +Key: GrabTimeout +Type: int +Default: 3 +User: greeter +Instance: #*/ +Comment: + How many seconds to wait for grab to succeed. +Description: + This option specifies the maximum time &kdm; will wait for the grabs to + succeed. A grab may fail if some other X-client has the &X-Server; or the + keyboard grabbed, or possibly if the network latencies are very high. You + should be cautious when raising the timeout, as a user can be spoofed by + a look-alike window on the display. If a grab fails, &kdm; kills and + restarts the &X-Server; (if possible) and the session. + +Key: AuthComplain +Type: bool +Default: true +User: greeter +Instance: #*/! +Merge: xdm +Comment: + Warn, if display has no X-authorization (local auth cannot be created, + &XDMCP; display wants no auth, or display is foreign from StaticServers). +Description: + Warn, if a display has no X-authorization. This will be the case if + <itemizedlist> + <listitem><para> + the authorization file for a local &X-Server; could not be created, + </para></listitem> + <listitem><para> + a remote display from &XDMCP; did not request any authorization or + </para></listitem> + <listitem><para> + the display is a <quote>foreign</quote> display specified in + <option>StaticServers</option>. + </para></listitem> + </itemizedlist> + +Key: LoginMode +If: defined(XDMCP) +Type: enum + LocalOnly/LOGIN_LOCAL_ONLY: only local login possible + DefaultLocal/LOGIN_DEFAULT_LOCAL: start up in local mode, but allow switching to remote mode + DefaultRemote/LOGIN_DEFAULT_REMOTE: ... and the other way round + RemoteOnly/LOGIN_REMOTE_ONLY: only choice of remote host possible +Default: LocalOnly +User: core +User: greeter +Instance: :*/DefaultLocal +# from make_it_cool branch and SuSE 8.1 +Merge: kdm:EnableChooser(P_EnableChooser) +Comment: & +Description: + Specify whether the greeter of local displays should start up in host chooser + (remote) or login (local) mode and whether it is allowed to switch to the + other mode. + +Key: ChooserHosts +If: defined(XDMCP) +Type: list +Default: "*" +User: core +Instance: #:*/"*,ugly,sky,dino,kiste.local,login.crap.com" +Comment: + A list of hosts to be automatically added to the remote login menu. The + special name "*" means broadcast. +Description: + A list of hosts to be automatically added to the remote login menu. + The special name <literal>*</literal> means broadcast. + Has no effect if <option>LoginMode</option> is <literal>LocalOnly</literal>. + +Key: ForgingSeed +Type: int +Default: 0 +User: greeter +Instance: #*/ +Update: upd_forgingseed +Comment: + Random seed for forging saved session types, etc. of unknown users. + This value should be random but constant across the login domain. +Description: + Use this number as a random seed when forging saved session types, etc. of + unknown users. This is used to avoid telling an attacker about existing users + by reverse conclusion. This value should be random but constant across the + login domain. + +Key: ShowLog +If: defined(WITH_KDM_XCONSOLE) +Type: bool +Default: false +User: greeter +Instance: :0/true +Comment: + Enable &kdm;'s built-in xconsole. Note that this can be enabled for only + one display at a time. +Description: + Enable &kdm;'s built-in <command>xconsole</command>. + Note that this can be enabled for only one display at a time. + This option is available only if &kdm; was <command>configure</command>d + with <option>--enable-kdm-xconsole</option>. + +Key: LogSource +If: defined(WITH_KDM_XCONSOLE) +Type: string +Default: "" +User: greeter-c +Instance: :0/"/dev/xconsole" +Comment: + The data source for &kdm;'s built-in xconsole. + If empty, a console log redirection is requested from /dev/console. +Description: + The data source for &kdm;'s built-in <command>xconsole</command>. + If empty, a console log redirection is requested from + <filename>/dev/console</filename>. + Has no effect if <option>ShowLog</option> is disabled. + +Key: PluginsLogin +Type: list +Default: "classic" +User: greeter +Instance: #*/"sign" +Comment: + Specify conversation plugins for the login dialog. Each plugin can be + specified as a base name (which expands to $kde_modulesdir/kgreet_$base) + or as a full pathname. +Description: + Specify conversation plugins for the login dialog; the first in the list + is selected initially. + Each plugin can be specified as a base name (which expands to + <filename>$<envar>kde_modulesdir</envar>/kgreet_<replaceable>base</replaceable></filename>) + or as a full pathname. + </para><para> + Conversation plugins are modules for the greeter which obtain authentication + data from the user. Currently only the <literal>classic</literal> plugin is + shipped with &kde;; it presents the well-known username and password form. + +Key: PluginsShutdown +Type: list +Default: "classic" +User: greeter +Instance: #*/"modern" +Comment: & +Description: + Same as <option>PluginsLogin</option>, but for the shutdown dialog. + +Key: PluginOptions +Type: list +Default: "" +User: greeter +Instance: #*/"SomeKey=randomvalue,Foo=bar" +Comment: + A list of options of the form Key=Value. The conversation plugins can query + these settings; it is up to them what possible keys are. +Description: + A list of options of the form + <replaceable>Key</replaceable><literal>=</literal><replaceable>Value</replaceable>. + The conversation plugins can query these settings; it is up to them what + possible keys are. + +Key: AllowConsole +Type: bool +Default: true +User: greeter(hasConsole) +Instance: #*/! +Comment: & +Description: + Show the <guilabel>Console Login</guilabel> action in the greeter (if <option>ServerTTY</option>/<option>ConsoleTTYs</option> + is configured). + +Key: AllowClose +Type: bool +Default: true +User: greeter +Instance: :*/false +Comment: & +Description: + Show the <guilabel>Restart X Server</guilabel>/<guilabel>Close Connection</guilabel> action in the greeter. + +Key: Preloader +Type: string +Default: "" +User: greeter-c +Instance: */KDE_BINDIR "/preloadkde" +Comment: & +Description: + A program to run while the greeter is visible. It is supposed to preload + as much as possible of the session that is going to be started (most + probably). + +Key: UseTheme +Type: bool +Default: false +User: greeter +Instance: #*/true +Comment: & +Description: + Whether the greeter should be themed. + +Key: Theme +Type: string +Default: "" +User: greeter +Instance: */KDMDATA "/themes/circles" +Comment: & +Description: + The theme to use for the greeter. Can point to either a directory or an XML + file. diff --git a/kdm/configure.in.bot b/kdm/configure.in.bot new file mode 100644 index 000000000..838d29faa --- /dev/null +++ b/kdm/configure.in.bot @@ -0,0 +1,8 @@ +if $kdm_no_Xau; then + AC_MSG_WARN([Cannot build KDM! Make sure that libXau.a is installed!]) +fi +if $kdm_no_Xdmcp; then + AC_MSG_WARN([Cannot build KDM! Make sure that libXdmcp.a and Xdmcp.h +are installed or use --without-xdmcp to disable XDMCP support!]) +fi + diff --git a/kdm/configure.in.in b/kdm/configure.in.in new file mode 100644 index 000000000..4f3ff82d7 --- /dev/null +++ b/kdm/configure.in.in @@ -0,0 +1,243 @@ +KDE_FIND_PATH(xmkmf, XMKMF, [], [AC_MSG_ERROR([xmkmf/imake not found. Please make sure it's in PATH!])]) + +dnl ask imake about various X settings +AC_MSG_CHECKING([X paths]) +imkv=8 +test "$kde_cv_defines_imake_version" = $imkv || unset kde_cv_defines_imake +AC_CACHE_VAL(kde_cv_defines_imake, [ + rm -fr conftestdir + if mkdir conftestdir; then + cd conftestdir + cat > Imakefile <<'EOF'[ + +acimake: + @echo "XBINDIR=\"$(BINDIR)\" XLIBDIR=\"$(LIBDIR)\"" + +]EOF + if $XMKMF >&5 2>&1 && test -f Makefile; then + kde_cv_defines_imake=`${MAKE-make} acimake 2> /dev/null | grep -v "^make"` + kde_cv_defines_imake_version=$imkv + else + AC_MSG_RESULT([failed]) + AC_MSG_ERROR([$XMKMF (imake) failed. +Make sure you have all necessary X development packages installed. +On some systems a missing /lib/cpp symlink is at fault.]) + fi + cd .. + rm -fr conftestdir + else + AC_MSG_RESULT([failed]) + AC_MSG_ERROR([cannot create temporary directory]) + fi +]) +AC_MSG_RESULT([done]) +eval "$kde_cv_defines_imake" +AC_DEFINE_UNQUOTED(XBINDIR, "$XBINDIR", [X binaries directory]) +AC_DEFINE_UNQUOTED(XLIBDIR, "$XLIBDIR", [X libraries directory]) + +if test -f /etc/ttys; then + AC_DEFINE(BSD_INIT, 1, [Define if the system uses a BSD-style init]) +fi + +AC_CHECK_FUNCS([getttyent]) +case $host_os in + linux*) ac_cv_func_getutxent=no;; + darwin*) ac_cv_func_getutxent=no;; + kfreebsd*-gnu) ac_cv_func_getutxent=no;; + *) AC_CHECK_FUNC([getutxent]);; +esac +if test $ac_cv_func_getutxent = yes; then + AC_DEFINE(HAVE_UTMPX, 1, [Define if the system uses extended utmp]) +else + AC_CHECK_FUNC([getutent], , + [AC_DEFINE(BSD_UTMP, 1, [Define if the system has no getutent])]) +fi + +AC_CHECK_MEMBERS([struct utmp.ut_user], , , [#include <utmp.h>]) +AC_CHECK_MEMBERS([struct passwd.pw_expire], , , [#include <pwd.h>]) +AC_CHECK_MEMBERS([struct sockaddr_in.sin_len], , , [ +#include <sys/socket.h> +#include <netinet/in.h> +]) + +ac_save_libs=$LIBS +LIBS="$LIBS $LIBUTIL" +AC_CHECK_FUNCS([setlogin setusercontext getusershell login_getclass auth_timeok]) +LIBS=$ac_save_libs + +dnl is getifaddrs always available without additional libs? +AC_CHECK_FUNCS([mkstemp setproctitle sysinfo strnlen getifaddrs]) + +AC_CHECK_FUNCS([arc4random], , + [ +dnl assume that /dev/random is non-blocking if /dev/urandom does not exist +for i in urandom random; do + if test -c /dev/$i; then + AC_DEFINE_UNQUOTED(DEV_RANDOM, "/dev/$i", [Define the system's entropy device]) + break + fi +done + ]) + +AC_CHECK_FUNC(vsyslog, [ + AC_DEFINE(USE_SYSLOG, 1, [Define if kdm should be built with syslog support])]) + +kdm_no_Xau=false +kdm_no_Xdmcp=false + +AC_CHECK_LIB(Xau, main, [:], + [ + kdm_no_Xau=true + DO_NOT_COMPILE="$DO_NOT_COMPILE kdm" + ], + $X_LDFLAGS -lX11 $LIBSOCKET) + +AC_ARG_WITH(xdmcp, + AC_HELP_STRING([--without-xdmcp],[build kdm without xdmcp support [default=with xdmcp]]), , + [with_xdmcp=yes]) +if test "x$with_xdmcp" = xyes; then + AC_CHECK_LIB(Xdmcp, main, [LIBXDMCP="-lXdmcp"], , $X_LDFLAGS -lX11 $LIBSOCKET) + if test -n "$LIBXDMCP"; then + cppflags_safe=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $X_INCLUDES" + AC_CHECK_HEADER(X11/Xdmcp.h, [HAVE_X11_XDMCP_H=1], , [#include <X11/Xmd.h>]) + CPPFLAGS=$cppflags_safe + fi + if test -z "$HAVE_X11_XDMCP_H"; then + kdm_no_Xdmcp=true + DO_NOT_COMPILE="$DO_NOT_COMPILE kdm" + fi + AC_DEFINE(XDMCP, 1, [Define if kdm should be built with XDMCP support]) + ac_save_libs=$LIBS + LIBS="$LIBS $LIBXDMCP" + AC_CHECK_FUNC(XdmcpWrap, [ + AC_DEFINE(HASXDMAUTH, 1, [Define if kdm should be built with XDMAUTH support]) + ]) + LIBS=$ac_save_libs +fi +AC_SUBST(LIBXDMCP) + +KRB4_INCS= +KRB4_LIBS= +KRB4_RPATH= + +AC_MSG_CHECKING(whether to use Kerberos v4) +AC_ARG_WITH(krb4, +AC_HELP_STRING([--with-krb4=PATH],[Compile in Kerberos v4 support]), +[ test "x$with_krb4" = xyes && with_krb4=/usr/kerberos ], +[ with_krb4=no ] +) +case "$with_krb4" in +no) + AC_MSG_RESULT(no) + ;; +*) + AC_MSG_RESULT(yes) + AC_DEFINE_UNQUOTED(KRB4, 1, [define if you have Kerberos IV]) + KRB4_INCS="-I$with_krb4/include" + KRB4_LIBS="-L$with_krb4/lib -lkrb -ldes" + if test "$USE_RPATH" = "yes" ; then + KRB4_RPATH="-R $with_krb4/lib" + fi + AC_CHECK_LIB(resolv, dn_expand, KRB4_LIBS="$KRB4_LIBS -lresolv") + ;; +esac + +AC_MSG_CHECKING(whether to use AFS) +AC_ARG_WITH(afs, + AC_HELP_STRING([--with-afs],[Compile in AFS support (requires KTH krb4)]), , + [ with_afs=no ]) +if test "$with_afs" = no; then + AC_MSG_RESULT(no) +else + if test "$with_krb4" = no; then + AC_MSG_RESULT(no) + AC_MSG_WARN("AFS requires Kerberos v4 support.") + with_afs=no + else + AC_MSG_RESULT(yes) + AC_DEFINE_UNQUOTED(AFS, 1, [define if you have KTH Kerberos IV and AFS]) + KRB4_LIBS="$KRB4_LIBS -lkafs" + if test -n "$os_aix"; then + KRB4_LIBS="$KRB4_LIBS -lld" + fi + fi +fi + +AC_SUBST(KRB4_INCS) +AC_SUBST(KRB4_LIBS) +AC_SUBST(KRB4_RPATH) + +AC_CHECK_LIB(s, main, [LIB_LIBS="-ls"]) dnl for AIX +AC_SUBST(LIB_LIBS) + +AC_CHECK_LIB(posix4, sched_yield, [LIBPOSIX4=-lposix4]) +AC_SUBST(LIBPOSIX4) + +KRB5_INCS= +KRB5_LIBS= +KRB5_RPATH= + +AC_MSG_CHECKING([whether to use Kerberos5 for Xauth cookies in kdm]) +AC_ARG_WITH(krb5auth, + AC_HELP_STRING([--with-krb5auth=PATH],[Use Kerberos5 for Xauth cookies in kdm]), , + [ with_krb5auth=no ]) +if test "x$with_krb5auth" = xno; then + AC_MSG_RESULT(no) +else + AC_MSG_RESULT(yes) + if test "x$with_krb5auth" != xyes; then + KRB5_INCS="-I$with_krb5auth/include" + KRB5_LIBS="-L$with_krb5auth/lib" + if test "$USE_RPATH" = "yes" ; then + KRB5_RPATH="-R $with_krb5auth/lib" + fi + fi + KRB5_LIBS="$KRB5_LIBS -lkrb5" dnl -lk5crypto -lcom_err -lresolv + keepcflags=$CFLAGS + CFLAGS="$KRB5_INCS $CFLAGS" + AC_CHECK_HEADER(krb5/krb5.h, + [ AC_DEFINE(K5AUTH, 1, [Define if kdm should use Kerberos 5 for Xauth cookies.]) ], + [ AC_MSG_ERROR([--with-krb5auth requires Kerberos5 header files. +Due to a problem with X includes you probably have to run "ln -s . krb5" +in the directory where the krb5.h include resides to make things actually work.])]) + CFLAGS="$keepcflags" +fi + +AC_SUBST(KRB5_INCS) +AC_SUBST(KRB5_LIBS) +AC_SUBST(KRB5_RPATH) + +AC_MSG_CHECKING([whether to use Sun's secure RPC for Xauth cookies in kdm]) +AC_ARG_WITH(rpcauth, + AC_HELP_STRING([--with-rpcauth],[Use Sun's secure RPC for Xauth cookies in kdm.]), , + [ with_rpcauth=no ]) +if test "x$with_rpcauth" = xno; then + AC_MSG_RESULT(no) +else + AC_MSG_RESULT(yes) + AC_CHECK_HEADER(rpc/rpc.h, + [ AC_DEFINE(SECURE_RPC, 1, [Define if kdm should use Sun's secure RPC for Xauth cookies.]) ], + [ AC_MSG_ERROR([--with-rpcauth requires Sun RPC header files.])]) +fi + +if test "x$use_pam" = xyes; then + AC_DEFINE(USE_PAM, 1, [Define if kdm should use PAM]) +elif test "x$use_shadow" = xyes; then + AC_DEFINE(USESHADOW, 1, [Define if kdm should use shadow passwords]) +fi +if test "x$with_krb4" != xno; then + AC_DEFINE(KERBEROS, 1, [Define if kdm should use Kerberos IV]) + if test "x$with_afs" = xno; then + AC_DEFINE(NO_AFS, 1, [Define if kdm should not use AFS]) + fi +fi + +AC_ARG_WITH(kdm-xconsole, + AC_HELP_STRING([--with-kdm-xconsole],[build kdm with built-in xconsole [default=no]]), , + [with_kdm_xconsole=no]) +if test "x$with_kdm_xconsole" = xyes; then + AC_DEFINE(WITH_KDM_XCONSOLE, 1, [Build kdm with built-in xconsole]) +fi + +dnl AC_OUTPUT(kdm/kfrontend/sessions/kde.desktop) diff --git a/kdm/confproc.pl b/kdm/confproc.pl new file mode 100755 index 000000000..88be28847 --- /dev/null +++ b/kdm/confproc.pl @@ -0,0 +1,838 @@ +#! /usr/bin/perl -w +# +# Copyright 2004-2005 Oswald Buddenhagen <[email protected]> +# +# 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 holders 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 holders. +# + +use strict; +use Cwd 'abs_path'; + +sub pegout($) +{ + print STDERR $_[0]."\n"; + exit 1; +} + +sub relpath($$) +{ + my @src = split(/\//, abs_path(shift)); + my @dst = split(/\//, abs_path(shift)); + pop @dst; + while (@src && @dst && $src[0] eq $dst[0]) { + shift @src; + shift @dst; + } + return "../"x@dst . join("/", @src); +} + +sub getl() +{ + while (<INFILE>) { + next if (/^#/); + chop; +# print "read: ".$_."\n"; + return; + } + $_ = ""; +} + +sub dedb($) +{ + my $t = shift; + $t =~ s,</?(command|guilabel|quote)>,\",g; + $t =~ s,</?(acronym|envar|filename|option|systemitem( [^ >]+)?)>,,g; + $t =~ s,<emphasis>([^<]+)</emphasis>,uc($1),ge; + $t =~ s, , ,g; + $t =~ s,<,<,g; + $t =~ s,>,>,g; + $t =~ s,&kdm;,KDM,g; + $t =~ s,&XDMCP;,XDMCP,g; + $t =~ s,&X-Server;,X-server,g; + return $t; +} + +sub mkvname($) +{ + my $v = shift; + if ($v !~ /^[A-Z]{2}/) { + $v = lcfirst $v; + } + return $v; +} + +sub emit_conds($) +{ + my $ret = ""; + for my $c (keys %{$_[0]}) { + my ($then, $else) = ("", ""); + for my $d (@{${$_[0]}{$c}}) { + my $bas = "# define ".$d->[0]; + if ($d->[1] =~ /\n/) { + $then .= $bas." \\\n".$d->[1]."\n"; + } else { + $then .= $bas." ".$d->[1]."\n"; + } + $else .= $bas."\n"; + } + $ret .= "#if ".$c."\n".$then."#else\n".$else."#endif\n\n"; + } + return $ret; +} + +sub add_cond($$$$) +{ + if ($_[0]) { + my $vn = uc($_[1]); + for my $i (@{$_[2]}) { + push @{${$_[3]}{$_[0]}}, [ $vn."_".$i->[1], $i->[0] ]; + $i->[0] = $vn."_".$i->[1]; + } + } +} + +my $do_doc = 0; +if ($ARGV[0] eq "--doc") { + $do_doc = 1; + shift @ARGV; +} + +@ARGV != 2 && pegout("usage: $0 [--doc] <def-file> <out-file>"); + +open (INFILE, $ARGV[0]) || pegout("$0: cannot open definition file ".$ARGV[0]); + +my %ex_conds = (); +my %ex_sects = (); # $name -> $index +my @ex_config = (); # ($name, $comment, $entries) + +my $raw_out = ""; + +my %ov_enum_conds = (); +my $ov_enums = ""; +my $ov_enum_defs = ""; + +my $ov_defaults = ""; + +my %arr_ov_vars = (); +my $ov_rd_sects = ""; +my $ov_rd_ents = ""; + +my $ov_gen_sects = ""; +my $ov_gen_ents = ""; +my $max_prio = 0; + +my %ov_sec_conds = (); +my %ov_ent_conds = (); +my $ov_sect_defs = ""; +my $ov_sect_refs = ""; + +my %ov_glob_conds = (); +my $ov_globs = ""; +my %ov_loc_conds = (); +my $ov_locs = ""; + +my %ov_glob_decl_conds = (); +my $ov_glob_decls = ""; +my %ov_glob_def_conds = (); +my %ov_glob_defs = (); +my %ov_loc_def_conds = (); +my %ov_loc_defs = (); + +my %ov_greet_conds = (); +my $ov_greet_init = ""; +my @ov_greet_decls = (); +my %ov_greet_defs = (); + +my %ov_xm_conds = (); +my @ov_xm = ("", ""); + +my %ov_km_conds = (); +my %ov_km = (); + +my %sect_names = (); +my $sect = ""; +my $sect_if; + +my $key_if; + +my %key_names; + +my $kid_seq = 0x1000; + +my $doc = ""; +my $doc_ref = ""; + +sub emit_section() +{ + my $ts = $sect; + $ts =~ s/-/_/; + my @oa = ( + ["static Ent ents".$ts."[] = { \\\n".$ov_rd_ents."};", "ENTS"], + ["static Ent ents".$ts."[] = { \\\n".$ov_gen_ents."};", "GENS"], + ["sec".$ts." = { \"".$sect."\", ents".$ts.", as(ents".$ts.") },", "SEC"], + ["&sec".$ts.",", "SECS"] + ); + $ov_rd_ents = ""; + $ov_gen_ents = ""; + add_cond($sect_if, $ts, \@oa, \%ov_sec_conds); + $ov_rd_sects .= " \\\n".$oa[0][0]." \\\n"; + $ov_gen_sects .= " \\\n".$oa[1][0]." \\\n"; + $ov_sect_defs .= " ".$oa[2][0]." \\\n"; + $ov_sect_refs .= "\t".$oa[3][0]." \\\n"; + $doc_ref .= "</variablelist>\n</sect2>\n\n"; +} + +my %th = ( + "int" => [ "C_TYPE_INT", "", "int\t", "", "GetCfgInt", "" ], + "bool" => [ "C_TYPE_INT", " | C_BOOL", "int\t", "bool\t", "GetCfgInt", "GetCfgInt" ], + "enum" => [ "C_TYPE_INT", " | C_ENUM", "int\t", "", "GetCfgInt", "" ], + "group" => [ "C_TYPE_INT", " | C_GRP", "int\t", "", "GetCfgInt", "" ], + "string" => [ "C_TYPE_STR", "", "char\t*", "QString\t", "GetCfgStr", "GetCfgQStr" ], + "path" => [ "C_TYPE_STR", " | C_PATH", "char\t*", "QString\t", "GetCfgStr", "GetCfgQStr" ], + "list" => [ "C_TYPE_ARGV", "", "char\t**", "QStringList\t", "GetCfgStrArr", "GetCfgQStrList" ] +); + +my @tl = ("QFont\t", "QStringList\t", "QString\t", "char\t**", "char\t*", "int\t", "bool\t"); + +sub init_defs($) +{ + for my $t (@tl) { + $_[0]{$t} = ""; + } +} + +init_defs(\%ov_glob_defs); +init_defs(\%ov_loc_defs); +init_defs(\%ov_greet_defs); + +sub emit_defs($) +{ + my $ret = ""; + for my $t (@tl) { + $ret .= $_[0]{$t}; + } + return $ret; +} + +while (<INFILE>) { + chop; + if (/^<code>$/) { + while (<INFILE>) { + last if (/^<\/code>\n$/); + $raw_out .= $_; + } + } elsif (/^<kdmrc>$/) { + my $comm = ""; + while (<INFILE>) { + last if (/^<\/kdmrc>\n$/); + chop; + if (/^\[(.*)\]$/) { + defined($ex_sects{$1}) && + pegout("redefinition of example section [$1]"); + push @ex_config, [$1, dedb($comm), "", ""]; + $ex_sects{$1} = $#ex_config; + $comm = ""; + } else { + if (!$_) { + $comm .= " \\\n\"\\n\""; + } elsif ($_ eq " _") { + $comm .= " \\\n\"#\\n\""; + } else { + s/"/\\"/g; + $comm .= " \\\n\"#".$_."\\n\""; + } + } + } + } elsif (/^<docu>$/) { + while (<INFILE>) { + last if (/^<\/docu>\n$/); + $doc .= $_; + } + } elsif (/^<legacy>$/) { + while (<INFILE>) { + next if (/^($|#)/); + my ($proc, $kif); + if (/^If: (.+)$/) { + $kif = $1; + getl(); + } else { + $kif = ""; + } + if (/^Proc: (.+)$/) { + $proc = $1; + getl(); + } else { + pegout("expecting Proc keyword in legacy section"); + } + my $nsrc = 0; + my $mcnt = 0; + while (/^Source: (.+)$/) { + my $src = $1; + if ($src =~ /^xdm:(.*)$/) { + my $what = $1; + my $dsp = ($what =~ s/^\*\.//); + my @oa = ([ "{ \"".$what."\", (char *)-1, 0, ".$proc." },", "XMO" ]); + add_cond($kif, $what, \@oa, \%ov_xm_conds); + $ov_xm[$dsp] .= $oa[0][0]." \\\n"; + } elsif ($src =~ /^kdm:(.*)\/(.*)$/) { + my ($sec, $key) = ($1, $2); + my @oa = ([ "{ \"".$key."\", (char *)-1, 0, ".$proc." },", "KMO".($mcnt++) ]); + add_cond($kif, $key, \@oa, \%ov_km_conds); + $ov_km{$sec} .= $oa[0][0]." \\\n"; + } else { + pegout("invalid legacy option '$_'"); + } + $nsrc++; + getl(); + } + $nsrc || pegout("no sources for legacy processor ".$proc); + last if (/^<\/legacy>$/); + pegout("unidentified section body '".$_."' in legacy section") if ($_); + } + } else { + next if (/^($|#)/); + if (/^Key: (.+)$/) { + my $key = $1; + $sect || pegout("defining key ".$key." outside any section"); + defined($key_names{$key}) && + pegout("redefinition of key ".$key." in section [".$sect."]"); + $key_names{$key} = ""; + getl(); + if (/^If: (.+)$/) { + $key_if = $1; + getl(); + } else { + $key_if = ""; + } + my $kif = $sect_if ? + ($key_if ? "(".$sect_if.") && (".$key_if.")" : $sect_if) : $key_if; + my ($e_comm, $e_desc) = ("", ""); + my $type = ""; + if (/^Type: (.+)$/) { + $type = $1; + if ($type eq "enum") { + my $enum = "static const char *e".$key."[] = { "; + my $n_e_def = 0; + while (getl(), /^ ([A-Za-z]+)(\/([A-Z_]+))?: (.+)$/) { + my $e_nam = $1; + $enum .= "\"".$e_nam."\", "; + defined($3) && + ($ov_enum_defs .= "#define ".$3." ".($n_e_def++)."\n"); + my ($comm, $desc) = (dedb($4), $4); + $comm =~ s/\"/\\\"/g; + $e_comm .= " \\\n\"# \\\"".$e_nam."\\\" - ".$comm."\\n\""; + $e_desc .= + "<varlistentry>\n". + "<term><parameter>".$e_nam."</parameter></term>\n". + "<listitem><para>".$desc."</para></listitem>\n". + "</varlistentry>\n"; + } + $enum .= "0 };"; + my @oa = ( [ $enum, "ENUM" ] ); + add_cond($kif, $key, \@oa, \%ov_enum_conds); + $ov_enums .= $oa[0][0]." \\\n"; + $n_e_def && ($ov_enum_defs .= "\n"); + } elsif ($type =~ /^(int|bool|group|string|path|list)$/) { + getl(); + } else { + pegout("unknown Type ".$type." for key ".$key." in section [".$sect."]"); + } + } else { + pegout("expecting Type for key ".$key." in section [".$sect."]"); + } + my ($odflt, $dflt, $cdflt, $ddflt); + my $quot = ($type =~ /^(int|bool|enum|group)$/); + if (/^Default: (\*?)(.+)$/) { + my $defd = $1; + ($odflt, $dflt) = ($2, $2); + $quot && ($dflt = "\"".$dflt."\""); + $defd && ($ov_defaults .= "#define def_".$key." ".$dflt."\n"); + getl(); + } else { + pegout("expecting Default for key ".$key." in section [".$sect."]"); + } + if (/^CDefault: (.+)$/) { + if ($1 eq "-") { + $ddflt = + $cdflt = ""; + } else { + $ddflt = + $cdflt = $1; + $cdflt =~ s/"/\\"/g; + $cdflt = " \\\n\"# Default is ".$cdflt."\\n\""; + } + getl(); + } else { + $ddflt = $odflt; + if ($quot) { + $cdflt = " \\\n\"# Default is ".$odflt."\\n\""; + } else { + $cdflt = " \\\n\"# Default is \\\"\" ".$dflt." \"\\\"\\n\""; + } + } + if (/^DDefault: -$/) { + $ddflt = ""; + getl(); + } + my $pproc; + if (/^PostProc: (.+)$/) { + $pproc = $1; + getl(); + } else { + $pproc = ""; + } + my $nusers = 0; + my ($vname, $kid, $xkid, $ctype, $cpptype, $cget, $cppget); + while (/^User: (.+)$/) { + my $user = $1; + if ($user eq "dummy") { + $vname = "dummy"; + $kid = "C_INTERNAL | C_TYPE_STR"; + $xkid = ""; + } elsif ($user =~ s/^(core|greeter|greeter-c|dep|config)(\((.+)\))?(:font)?$/$1/) { + my ($hvn, $isfn) = (defined($3) ? $3 : mkvname($key), $4); + ($kid, $xkid, $ctype, $cpptype, $cget, $cppget) = @{$th{$type}}; + $kid = sprintf "%#x | %s", $kid_seq, $kid; + if ($user eq "dep") { + $vname = $hvn; + $xkid .= " | C_INTERNAL"; + } elsif ($user eq "config") { + $vname = $hvn; + $xkid .= " | C_INTERNAL | C_CONFIG"; + } else { + $vname = ""; + if ($user eq "core") { + if ($sect =~ /^-/) { + my @oa = ( + [ "{ ".$kid.", boffset(".$hvn.") },", "LOC" ], + [ $ctype.$hvn.";", "LDEF" ] + ); + add_cond($kif, $hvn, \@oa, \%ov_loc_conds); + $ov_locs .= " \\\n".$oa[0][0]; + $ov_loc_defs{$ctype} .= " \\\n\t".$oa[1][0]; + } else { + my @oa = ( + [ "{ ".$kid.", (char **) &".$hvn." },", "GLOB" ], + [ $ctype.$hvn.";", "GDEF" ], + [ "extern ".$ctype.$hvn.";", "GDECL" ] + ); + add_cond($kif, $hvn, \@oa, \%ov_glob_conds); + $ov_globs .= " \\\n".$oa[0][0]; + $ov_glob_defs{$ctype} .= " \\\n".$oa[1][0]; + $ov_glob_decls .= " \\\n".$oa[2][0]; + } + } else { # greeter(-c)? + my ($typ, $gtr, $isc); + if ($isfn) { + $typ = "QFont\t"; + $gtr = "Str2Font( GetCfgQStr( ".$kid." ) )"; + $isc = 0; + } elsif ($user eq "greeter" && $cppget) { + $typ = $cpptype; + $gtr = $cppget."( ".$kid." )"; + $isc = 0; + } else { + $typ = $ctype; + $gtr = $cget."( ".$kid.(($type eq "list") ? ", 0" : "")." )"; + $isc = 1; + } + my @oa = ( + [ "_".$hvn." = ".$gtr.";", "GRINIT" ], + [ $typ."_".$hvn.";", "GRDEF" ], + [ "extern ".$typ."_".$hvn.";", "GRDECL" ] + ); + add_cond($kif, $hvn, \@oa, \%ov_greet_conds); + $ov_greet_init .= " \\\n ".$oa[0][0]; + $ov_greet_defs{$typ} .= " \\\n".$oa[1][0]; + $ov_greet_decls[$isc] .= " \\\n".$oa[2][0]; + } + } + } else { + pegout("unrecognized User '".$user."' for key ".$key." in section [".$sect."]"); + } + $nusers++; + getl(); + } + $nusers || pegout("expecting User for key ".$key." in section [".$sect."]"); + my $ninsts = 0; + while (/^Instance: ?(.*)$/) { + my $inst = $1; + if ($inst ne "-") { + my $on = 1 - ($inst =~ s/^#//); + my $sec; + if ($sect =~ /^-/) { + ($inst =~ s/^([^\/]+)\///) || pegout("instance for key ".$key." in section [".$sect."] does not specify display"); + $sec = "X-".$1.$sect; + } else { + $sec = $sect; + } + if ($type eq "bool" && $inst eq "!") { + $inst = ($dflt eq "\"true\"") ? "\"false\"" : "\"true\""; + } elsif (!$inst) { + $inst = $dflt; + } else { + $quot && ($inst = "\"".$inst."\""); + } + defined($ex_sects{$sec}) || + pegout("instantiating key ".$key." in section [".$sect."] in undeclared section"); + my @oa = ( [ "{ \"".$key."\",\t".$inst.", ".$on." },", "INST" ] ); + add_cond($key_if, $key, \@oa, \%ex_conds); + $ex_config[$ex_sects{$sec}][2] .= $oa[0][0]." \\\n"; + $ex_config[$ex_sects{$sec}][3] = $sect_if; + } + $ninsts++; + getl(); + } + $ninsts || + print STDERR "Warning: key ".$key." in section [".$sect."] not instanciated\n"; + my ($update, $prio) = ("0", ""); + if (/^Update: ([^\/]+)(\/(\d+))?$/) { + ($update, $prio) = ($1, $3); + getl(); + } + if ($prio) { + ($max_prio < $prio) && ($max_prio = $prio); + } else { + $prio = 0; + } + my $mcnt = 0; + while (/^Merge: (.+)$/) { + my $merge = $1; + if ($merge =~ /^xdm(:([^\(]+))?(\((.+)\))?$/) { + my ($what, $proc) = ($2, $4); + my @oa = ( + [ "{ \"".($what ? $what : lcfirst($key))."\", ". + "\"".(($sect =~ /^-/) ? "X-%s" : "").$sect."\", ". + ($what ? "\"".$key."\"" : "0").", ". + ($proc ? $proc : "0")." },", "XM" ] + ); + add_cond($kif, $key, \@oa, \%ov_xm_conds); + $ov_xm[$sect =~ /^-/] .= $oa[0][0]." \\\n"; + } elsif ($merge =~ /^kdm:([^\(]+)(\((.+)\))?$/) { + my ($where, $func) = ($1, $3); + my $sec = ""; + ($where =~ s/^([^\/]+)\///) && ($sec = $1); + my @oa = ( + [ "{ \"".($where ? $where : $key)."\", ". + ($sec ? "\"".$sect."\"" : "0").", ". + ($where ? "\"".$key."\"" : "0").", ". + ($func ? $func : "0")." },", "KM".($mcnt++) ] + ); + add_cond($kif, $key, \@oa, \%ov_km_conds); + $ov_km{$sec ? $sec : $sect} .= $oa[0][0]." \\\n"; + } else { + pegout("bogus Merge '".$merge."' for key ".$key." in section [".$sect."]"); + } + getl(); + } + # todo: handle $func here, too + my @oa = ( [ "{ \"".$key."\", 0, 0, 0 },", "KM" ] ); + add_cond($kif, $key, \@oa, \%ov_km_conds); + $ov_km{$sect} .= $oa[0][0]." \\\n"; + my $comm = ""; + if (/^Comment:(( [-&])?)$/) { + if ($1 eq " &") { + $comm = "&"; + getl(); + } elsif ($1 ne " -") { + while (getl(), /^ (.*)$/) { + $comm .= $1."\n"; + } + $comm || + print STDERR "Warning: key ".$key." in section [".$sect."] has empty Comment\n"; + } else { + getl(); + } + } else { + print STDERR "Warning: key ".$key." in section [".$sect."] has no Comment\n"; + } + if (/^Description:(( [-!])?)$/) { + if ($1 ne " -") { + $doc_ref .= + "<varlistentry>\n". + "<term id=\"option-".lc($key)."\"><option>".$key."</option></term>\n". + "<listitem>\n"; + ($1 eq " !") && + ($e_desc = ""); + my $desc = ""; + while (getl(), /^ (_|(.*))$/) { + $desc .= $2."\n"; + } + $desc || + print STDERR "Warning: key ".$key." in section [".$sect."] has empty Description\n"; + ($comm eq "&") && + ($comm = $desc); + $desc = "<para>\n".$desc."</para>\n"; + if ($e_desc) { + $e_desc = "<variablelist>\n".$e_desc."</variablelist>\n"; + ($desc =~ s/%ENUM%/$e_desc/) || + ($desc .= $e_desc); + } + $doc_ref .= $desc; + if ($ddflt) { + if ($ddflt eq '""') { + $doc_ref .= "<para>Empty by default.</para>\n"; + } else { + $ddflt =~ s/\"//g; + $ddflt =~ s,KDMCONF ,\${<envar>kde_confdir</envar>}/kdm,; + $ddflt =~ s,KDMDATA ,\${<envar>kde_datadir</envar>}/kdm,; + $ddflt =~ s,XBINDIR ,\${<envar>x_bindir</envar>},; + $doc_ref .= "<para>The default is <quote>".$ddflt."</quote>.</para>\n"; + } + } + $doc_ref .= "</listitem>\n</varlistentry>\n\n"; + } else { + getl(); + } + } else { + print STDERR "Warning: key ".$key." in section [".$sect."] has no Description\n"; + } + pegout("unidentified section body '".$_."' in section [".$sect."]") if ($_); + if ($vname) { + ($vname ne "dummy") && + ($arr_ov_vars{$vname} = $kif); + $vname = "&V".$vname; + } elsif ($pproc) { + $vname = "(void *)".$pproc; + } elsif ($type eq "enum") { + $vname = "e".$key; + } else { + $vname = "0"; + } + $comm = dedb($comm); + $comm =~ s/"/\\"/g; + $comm =~ s/([^\n]*)\n/ \\\n\"# $1\\n\"/g; + @oa = ( + [ "{ \"".$key."\", ".$kid.$xkid.", ".$vname.", ".$dflt." },", "RENT" ], + [ "{ \"".$key."\", ".$prio.", ".$update.",".$comm.$e_comm.$cdflt." },", "GENT" ], + ); + add_cond($key_if, $key, \@oa, \%ov_ent_conds); + $ov_rd_ents .= $oa[0][0]." \\\n"; + $ov_gen_ents .= $oa[1][0]." \\\n"; + $kid_seq++; + } elsif (/^Section: (.+)$/) { + emit_section() if ($sect); + $sect = $1; + defined($sect_names{$sect}) && pegout("redefinition of section [".$sect."]"); + $sect_names{$sect} = ""; + %key_names = (); + getl(); + if (/^If: (.+)$/) { + $sect_if = $1; + getl(); + } else { + $sect_if = ""; + } + my ($sref, $stit, $sna); + if ($sect =~ /^-(.*)$/) { + $sref = lc($1); + $stit = "X-*-".$1; + $sna = "section class"; + } else { + $sref = lc($sect); + $stit = $sect; + $sna = "section"; + } + $doc_ref .= + "\n<sect2 id=\"kdmrc-".$sref."\">\n". + "<title>The [".$stit."] ".$sna." of &kdmrc;</title>\n\n"; + if (/^Description:(( -)?)$/) { + if ($1 ne " -") { + my $desc = 0; + $doc_ref .= "<para>\n"; + while (getl(), /^ (_|(.*))$/) { + $doc_ref .= $2."\n"; + $desc = 1; + } + $doc_ref .= "</para>\n"; + $desc || + print STDERR "Warning: section [".$sect."] has empty Description\n"; + } else { + getl(); + } + } else { + print STDERR "Warning: section [".$sect."] has no Description\n"; + } + $doc_ref .= "\n<variablelist>\n\n"; + pegout("unidentified section body '".$_."' in section [".$sect."]") if ($_); + } else { + pegout("invalid section leadin: '".$_."'"); + } + } +} +emit_section(); +close INFILE; + +my $srcf = relpath($ARGV[0], $ARGV[1]); +my $exen = relpath($0, $ARGV[1]); + +open (OUTFILE, ">".$ARGV[1]) || pegout("$0: cannot create output file ".$ARGV[1]); + +if (!$do_doc) { + +print OUTFILE + "/* generated from $srcf by $exen - DO NOT EDIT! */\n\n". + "#ifndef CONFIG_DEFS\n". + "#define CONFIG_DEFS\n\n". + $raw_out."\n\n". + $ov_enum_defs."\n". + $ov_defaults."\n\n"; + +my $ov_vars = ""; +my %ov_var_conds = (); +for my $v (keys %arr_ov_vars) { + my @oa = ( ["V".$v.",", "VAR"] ); + add_cond($arr_ov_vars{$v}, $v, \@oa, \%ov_var_conds); + $ov_vars .= " ".$oa[0][0]." \\\n"; +} +print OUTFILE + emit_conds(\%ov_var_conds). + "#define CONF_READ_VARS \\\n \\\n". + "static Value \\\n". + $ov_vars. + " Vdummy;\n\n\n"; + +print OUTFILE + emit_conds(\%ov_ent_conds). + emit_conds(\%ov_sec_conds). + "#define CONF_SECTS \\\n \\\n". + "static Sect \\\n". + $ov_sect_defs. + " *allSects[]\t= { \\\n". + $ov_sect_refs. + " };\n\n\n"; + +print OUTFILE + emit_conds(\%ov_enum_conds). + "#define CONF_READ_ENTRIES \\\n \\\n". + $ov_enums. + $ov_rd_sects." \\\n". + "CONF_SECTS\n\n\n"; + +print OUTFILE + "#define CONF_MAX_PRIO ".$max_prio."\n\n". + "#define CONF_GEN_ENTRIES \\\n". + $ov_gen_sects." \\\n". + "CONF_SECTS\n\n\n"; + +print OUTFILE + emit_conds(\%ov_glob_conds). + "#define CONF_CORE_GLOBALS \\\n". + $ov_globs."\n\n\n". + "#define CONF_CORE_GLOBAL_DECLS \\\n". + $ov_glob_decls."\n\n\n". + "#define CONF_CORE_GLOBAL_DEFS \\\n". + emit_defs(\%ov_glob_defs)."\n\n\n"; + +print OUTFILE + emit_conds(\%ov_loc_conds). + "#define CONF_CORE_LOCALS \\\n". + $ov_locs."\n\n\n". + "#define CONF_CORE_LOCAL_DEFS \\\n". + emit_defs(\%ov_loc_defs)."\n\n\n\n"; + +print OUTFILE + emit_conds(\%ov_greet_conds). + "#define CONF_GREET_INIT \\\n". + $ov_greet_init."\n\n\n". + "#define CONF_GREET_C_DECLS \\\n". + $ov_greet_decls[1]."\n\n\n". + "#define CONF_GREET_CPP_DECLS \\\n". + $ov_greet_decls[0]."\n\n\n". + "#define CONF_GREET_DEFS \\\n". + emit_defs(\%ov_greet_defs)."\n\n\n"; + +my ($ov1, $ov2) = ("", ""); +my %ex_sec_conds = (); +for my $i (@ex_config) { + my $vn; + if ($i->[0] =~ /^X-(.+)-(.+)$/) { + if ($1 eq "*") { + $vn = "dEntsAny".$2; + } elsif ($1 eq ":*") { + $vn = "dEntsLocal".$2; + } else { + my ($t1, $t2) = ($1, $2); + $t1 =~ s/[-:]/_/g; + $vn = "dEnts".$t1.$t2; + } + } else { + $vn = "dEnts".$i->[0]; + } + my @oa = ( + [ "static DEnt ".$vn."[] = { \\\n".$i->[2]."};", "DSEC" ], + [ "{ \"".$i->[0]."\",\t".$vn.",\tas(".$vn."),".$i->[1]." },", "DSECS" ] + ); + add_cond($i->[3], $vn, \@oa, \%ex_sec_conds); + $ov1 .= $oa[0][0]." \\\n \\\n"; + $ov2 .= $oa[1][0]." \\\n"; +} +print OUTFILE + emit_conds(\%ex_conds). + emit_conds(\%ex_sec_conds). + "#define CONF_GEN_EXAMPLE \\\n \\\n". + $ov1. + "static DSect dAllSects[] = { \\\n". + $ov2. + "};\n\n\n"; + +print OUTFILE + emit_conds(\%ov_xm_conds). + "#define CONF_GEN_XMERGE \\\n \\\n". + "XResEnt globents[] = { \\\n". + $ov_xm[0]. + "}, dpyents[] = { \\\n". + $ov_xm[1]. + "};\n\n\n"; + +my $ov_km_sects = ""; +my $ov_km_sect_refs = ""; +for my $s (keys %ov_km) { + my $ts = $s; + $ts =~ s/-/_/; + $ov_km_sects .= + "KUpdEnt upd".$ts."[] = { \\\n". + $ov_km{$s}. + "}; \\\n \\\n"; + $ov_km_sect_refs .= "{ \"".$s."\", upd".$ts.", as(upd".$ts.") }, \\\n"; +} +print OUTFILE + emit_conds(\%ov_km_conds). + "#define CONF_GEN_KMERGE \\\n \\\n". + $ov_km_sects. + "KUpdSec kupsects[] = { \\\n". + $ov_km_sect_refs. + "};\n"; +print OUTFILE + "\n#endif /* CONFIG_DEFS */\n"; + +} else { + +$doc =~ s/%REF%/$doc_ref/; +print OUTFILE + "<!-- generated from $srcf - DO NOT EDIT! -->\n\n". + $doc; + +} + +close OUTFILE; diff --git a/kdm/kfrontend/Makefile.am b/kdm/kfrontend/Makefile.am new file mode 100644 index 000000000..8f123509d --- /dev/null +++ b/kdm/kfrontend/Makefile.am @@ -0,0 +1,66 @@ +# use 'make GENKDMCONF_FLAGS=... install' to override +GENKDMCONF_FLAGS = + +SUBDIRS = themer themes pics sessions + +AM_CPPFLAGS = -I$(srcdir)/../backend -I.. -I$(top_srcdir)/kcontrol/background \ + -I$(top_srcdir)/kdmlib $(all_includes) + +bin_PROGRAMS = kdm_config kdm_greet krootimage genkdmconf kdmctl + +kdm_config_SOURCES = kdm_config.c +kdm_config_LDADD = $(LIBRESOLV) $(LIBSOCKET) $(LIBPOSIX4) + +kdm_greet_SOURCES = \ + kdm_greet.c \ + kdmconfig.cpp \ + kdmclock.cpp \ + kconsole.cpp \ + kfdialog.cpp \ + kgdialog.cpp \ + kchooser.cpp \ + kgverify.cpp \ + kdmshutdown.cpp \ + kgreeter.cpp \ + kgapp.cpp +kdm_greet_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kdm_greet_LDADD = themer/libkdmthemer.a $(LIB_KDEUI) $(XTESTLIB) $(LIBPOSIX4) + +krootimage_SOURCES = krootimage.cpp +krootimage_LDFLAGS = $(all_libraries) $(KDE_RPATH) +krootimage_LDADD = $(top_builddir)/kcontrol/background/libbgnd.la $(LIB_KIO) + +METASOURCES = AUTO + +genkdmconf_SOURCES = genkdmconf.c +genkdmconf_LDFLAGS = $(X_LDFLAGS) $(X_RPATH) +genkdmconf_LDADD = $(LIB_X11) + +kdmctl_SOURCES = kdmctl.c +kdmctl_LDADD = $(LIBSOCKET) + +install-data-local: genkdmconf + ./genkdmconf --in $(DESTDIR)$(kde_confdir)/kdm --no-in-notice --face-src $(srcdir)/pics $(GENKDMCONF_FLAGS) + +messages: + $(XGETTEXT) `find . -name "*.cpp"` -o $(podir)/kdmgreet.pot + +noinst_HEADERS = \ + kdm_greet.h \ + kdmconfig.h \ + kdmclock.h \ + kconsole.h \ + kfdialog.h \ + kgdialog.h \ + kchooser.h \ + kgverify.h \ + kdmshutdown.h \ + kgreeter.h \ + kgapp.h \ + \ + krootimage.h + +kdm_greet_COMPILE_FIRST = ../config.ci +kdm_config_COMPILE_FIRST = ../config.ci +genkdmconf_COMPILE_FIRST = ../config.ci + diff --git a/kdm/kfrontend/genkdmconf.c b/kdm/kfrontend/genkdmconf.c new file mode 100644 index 000000000..5c0cb91e0 --- /dev/null +++ b/kdm/kfrontend/genkdmconf.c @@ -0,0 +1,2899 @@ +/* + +Create a suitable configuration for kdm taking old xdm/kdm +installations into account + +Copyright (C) 2001-2005 Oswald Buddenhagen <[email protected]> + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include <X11/Xlib.h> +#include <X11/Xresource.h> + +#include <config.h> + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <stdarg.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <utime.h> +#include <dirent.h> +#include <errno.h> +#include <pwd.h> +#include <time.h> +#include <limits.h> +#include <sys/stat.h> +#include <sys/param.h> +#ifdef BSD +# include <utmp.h> +#endif + +#include "config.ci" + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) +# define ATTR_UNUSED __attribute__((unused)) +#else +# define ATTR_UNUSED +#endif + +#if defined(__sun) && !defined(__sun__) +# define __sun__ +#endif + +#define as(ar) ((int)(sizeof(ar)/sizeof(ar[0]))) + +#define __stringify(x) #x +#define stringify(x) __stringify(x) + +#define RCVERSTR stringify(RCVERMAJOR) "." stringify(RCVERMINOR) + +static int old_scripts, no_old_scripts, old_confs, no_old, + no_backup, no_in_notice, use_destdir, mixed_scripts; +static const char *newdir = KDMCONF, *facesrc = KDMDATA "/pics/users", + *oldxdm, *oldkde; + +static int oldver; + + +typedef struct StrList { + struct StrList *next; + const char *str; +} StrList; + + +static void * +mmalloc( size_t sz ) +{ + void *ptr; + + if (!(ptr = malloc( sz ))) { + fprintf( stderr, "Out of memory\n" ); + exit( 1 ); + } + return ptr; +} + +static void * +mcalloc( size_t sz ) +{ + void *ptr; + + if (!(ptr = calloc( 1, sz ))) { + fprintf( stderr, "Out of memory\n" ); + exit( 1 ); + } + return ptr; +} + +static void * +mrealloc( void *optr, size_t sz ) +{ + void *ptr; + + if (!(ptr = realloc( optr, sz ))) { + fprintf( stderr, "Out of memory\n" ); + exit( 1 ); + } + return ptr; +} + +static char * +mstrdup( const char *optr ) +{ + char *ptr; + + if (!optr) + return 0; + if (!(ptr = strdup( optr ))) { + fprintf( stderr, "Out of memory\n" ); + exit( 1 ); + } + return ptr; +} + + +#define NO_LOGGER +#define STATIC static +#include <printf.c> + +typedef struct { + char *buf; + int clen, blen, tlen; +} OCABuf; + +static void +OutCh_OCA( void *bp, char c ) +{ + OCABuf *ocabp = (OCABuf *)bp; + + ocabp->tlen++; + if (ocabp->clen >= ocabp->blen) { + ocabp->blen = ocabp->blen * 3 / 2 + 100; + ocabp->buf = mrealloc( ocabp->buf, ocabp->blen ); + } + ocabp->buf[ocabp->clen++] = c; +} + +static int +VASPrintf( char **strp, const char *fmt, va_list args ) +{ + OCABuf ocab = { 0, 0, 0, -1 }; + + DoPr( OutCh_OCA, &ocab, fmt, args ); + OutCh_OCA( &ocab, 0 ); + *strp = realloc( ocab.buf, ocab.clen ); + if (!*strp) + *strp = ocab.buf; + return ocab.tlen; +} + +static int +ASPrintf( char **strp, const char *fmt, ... ) +{ + va_list args; + int len; + + va_start( args, fmt ); + len = VASPrintf( strp, fmt, args ); + va_end( args ); + return len; +} + +static void +StrCat( char **strp, const char *fmt, ... ) +{ + char *str, *tstr; + va_list args; + int el; + + va_start( args, fmt ); + el = VASPrintf( &str, fmt, args ); + va_end( args ); + if (*strp) { + int ol = strlen( *strp ); + tstr = mmalloc( el + ol + 1 ); + memcpy( tstr, *strp, ol ); + memcpy( tstr + ol, str, el + 1 ); + free( *strp ); + free( str ); + *strp = tstr; + } else + *strp = str; +} + + +#define WANT_CLOSE 1 + +typedef struct File { + char *buf, *eof, *cur; +#if defined(HAVE_MMAP) && defined(WANT_CLOSE) + int ismapped; +#endif +} File; + +static int +readFile( File *file, const char *fn ) +{ + off_t flen; + int fd; + + if ((fd = open( fn, O_RDONLY )) < 0) + return 0; + + flen = lseek( fd, 0, SEEK_END ); +#ifdef HAVE_MMAP +# ifdef WANT_CLOSE + file->ismapped = 0; +# endif + file->buf = mmap( 0, flen + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0 ); +# ifdef WANT_CLOSE + if (file->buf) + file->ismapped = 1; + else +# else + if (!file->buf) +# endif +#endif + { + file->buf = mmalloc( flen + 1 ); + lseek( fd, 0, SEEK_SET ); + if (read( fd, file->buf, flen ) != flen) { + free( file->buf ); + close( fd ); + fprintf( stderr, "Cannot read file\n" ); + return 0; /* maybe better abort? */ + } + } + file->eof = file->buf + flen; + close( fd ); + return 1; +} + +#ifdef WANT_CLOSE +static void +freeBuf( File *file ) +{ +# ifdef HAVE_MMAP + if (file->ismapped) + munmap( file->buf, file->eof - file->buf ); + else +# endif + free( file->buf ); +} +#endif + +static int +isTrue( const char *val ) +{ + return !strcmp( val, "true" ) || + !strcmp( val, "yes" ) || + !strcmp( val, "on" ) || + atoi( val ); +} + +static int +mkdirp( const char *name, int mode, const char *what, int existok ) +{ + char *mfname = mstrdup( name ); + int i; + struct stat st; + + for (i = 1; mfname[i]; i++) + if (mfname[i] == '/') { + mfname[i] = 0; + if (stat( mfname, &st )) { + if (mkdir( mfname, 0755 )) { + fprintf( stderr, "Cannot create parent %s of %s directory %s: %s\n", + mfname, what, name, strerror( errno ) ); + free( mfname ); + return 0; + } + chmod( mfname, 0755 ); + } + mfname[i] = '/'; + } + free( mfname ); + if (stat( name, &st )) { + if (mkdir( name, mode )) { + fprintf( stderr, "Cannot create %s directory %s: %s\n", + what, name, strerror( errno ) ); + return 0; + } + chmod( name, mode ); + return 1; + } + return existok; +} + + +static void +displace( const char *fn ) +{ + if (!no_backup) { + char bn[PATH_MAX + 4]; + sprintf( bn, "%s.bak", fn ); /* won't overflow if only existing paths are passed */ + rename( fn, bn ); + } else + unlink( fn ); +} + + +static char * +locate( const char *exe ) +{ + int len; + char *path, *pathe, *name, *thenam, nambuf[PATH_MAX+1]; + + if (!(path = getenv( "PATH" ))) + return 0; + len = strlen( exe ); + name = nambuf + PATH_MAX - len; + memcpy( name, exe, len + 1 ); + *--name = '/'; + do { + if (!(pathe = strchr( path, ':' ))) + pathe = path + strlen( path ); + len = pathe - path; + if (len && !(len == 1 && *path == '.')) { + thenam = name - len; + if (thenam >= nambuf) { + memcpy( thenam, path, len ); + if (!access( thenam, X_OK )) + return mstrdup( thenam ); + } + } + path = pathe; + } while (*path++ != '\0'); + return 0; +} + + +/* + * target data to be written to kdmrc + */ + +typedef struct Entry { + struct Entry *next; + struct Ent *spec; + const char *value; + int active:1; + int written:1; +} Entry; + +typedef struct Section { + struct Section *next; + struct Sect *spec; + const char *name; + const char *comment; + Entry *ents; +} Section; + +static Section *config; /* the kdmrc data to be written */ + +/* + * Specification of the (currently possible) kdmrc entries + */ + +typedef struct Ent { + const char *key; + int prio; + void (*func)( Entry *ce, Section *cs ); + const char *comment; +} Ent; + +typedef struct Sect { + const char *name; + Ent *ents; + int nents; +} Sect; + +static Sect *findSect( const char *name ); +static Ent *findEnt( Sect *sect, const char *key ); + +/* + * Functions to manipulate the current kdmrc data + */ + +static const char * +getfqval( const char *sect, const char *key, const char *defval ) +{ + Section *cs; + Entry *ce; + + for (cs = config; cs; cs = cs->next) + if (!strcmp( cs->name, sect )) { + for (ce = cs->ents; ce; ce = ce->next) + if (!strcmp( ce->spec->key, key )) { + if (ce->active && ce->written) + return ce->value; + break; + } + break; + } + return defval; +} + +static void +putfqval( const char *sect, const char *key, const char *value ) +{ + Section *cs, **csp; + Entry *ce, **cep; + + if (!value) + return; + + for (csp = &config; (cs = *csp); csp = &(cs->next)) + if (!strcmp( sect, cs->name )) + goto havesec; + cs = mcalloc( sizeof(*cs) ); + ASPrintf( (char **)&cs->name, "%s", sect ); + cs->spec = findSect( sect ); + *csp = cs; + havesec: + + for (cep = &(cs->ents); (ce = *cep); cep = &(ce->next)) + if (!strcmp( key, ce->spec->key )) + goto haveent; + ce = mcalloc( sizeof(*ce) ); + ce->spec = findEnt( cs->spec, key ); + *cep = ce; + haveent: + ASPrintf( (char **)&ce->value, "%s", value ); + ce->written = ce->active = 1; +} + +static const char *csect; + +#define setsect(se) csect = se + +static void +putval( const char *key, const char *value ) +{ + putfqval( csect, key, value ); +} + + +static void +wrconf( FILE *f ) +{ + Section *cs; + Entry *ce; + StrList *sl = 0, *sp; + const char *cmt; + + putfqval( "General", "ConfigVersion", RCVERSTR ); + for (cs = config; cs; cs = cs->next) { + fprintf( f, "%s[%s]\n", + cs->comment ? cs->comment : "\n", cs->name ); + for (ce = cs->ents; ce; ce = ce->next) { + if (ce->spec->comment) { + cmt = ce->spec->comment; + for (sp = sl; sp; sp = sp->next) + if (sp->str == cmt) { + cmt = "# See above\n"; + goto havit; + } + if (!(sp = malloc( sizeof(*sp) ))) + fprintf( stderr, "Warning: Out of memory\n" ); + else { + sp->str = cmt; + sp->next = sl; sl = sp; + } + } else + cmt = ""; + havit: + fprintf( f, "%s%s%s=%s\n", + cmt, ce->active ? "" : "#", ce->spec->key, ce->value ); + } + } +} + + +/* + * defaults + */ +#ifdef XDMCP +static const char def_xaccess[] = +"# Xaccess - Access control file for XDMCP connections\n" +"#\n" +"# To control Direct and Broadcast access:\n" +"#\n" +"# pattern\n" +"#\n" +"# To control Indirect queries:\n" +"#\n" +"# pattern list of hostnames and/or macros ...\n" +"#\n" +"# To use the chooser:\n" +"#\n" +"# pattern CHOOSER BROADCAST\n" +"#\n" +"# or\n" +"#\n" +"# pattern CHOOSER list of hostnames and/or macros ...\n" +"#\n" +"# To define macros:\n" +"#\n" +"# %name list of hosts ...\n" +"#\n" +"# The first form tells xdm which displays to respond to itself.\n" +"# The second form tells xdm to forward indirect queries from hosts matching\n" +"# the specified pattern to the indicated list of hosts.\n" +"# The third form tells xdm to handle indirect queries using the chooser;\n" +"# the chooser is directed to send its own queries out via the broadcast\n" +"# address and display the results on the terminal.\n" +"# The fourth form is similar to the third, except instead of using the\n" +"# broadcast address, it sends DirectQuerys to each of the hosts in the list\n" +"#\n" +"# In all cases, xdm uses the first entry which matches the terminal;\n" +"# for IndirectQuery messages only entries with right hand sides can\n" +"# match, for Direct and Broadcast Query messages, only entries without\n" +"# right hand sides can match.\n" +"#\n" +"\n" +"* #any host can get a login window\n" +"\n" +"#\n" +"# To hardwire a specific terminal to a specific host, you can\n" +"# leave the terminal sending indirect queries to this host, and\n" +"# use an entry of the form:\n" +"#\n" +"\n" +"#terminal-a host-a\n" +"\n" +"\n" +"#\n" +"# The nicest way to run the chooser is to just ask it to broadcast\n" +"# requests to the network - that way new hosts show up automatically.\n" +"# Sometimes, however, the chooser can't figure out how to broadcast,\n" +"# so this may not work in all environments.\n" +"#\n" +"\n" +"* CHOOSER BROADCAST #any indirect host can get a chooser\n" +"\n" +"#\n" +"# If you'd prefer to configure the set of hosts each terminal sees,\n" +"# then just uncomment these lines (and comment the CHOOSER line above)\n" +"# and edit the %hostlist line as appropriate\n" +"#\n" +"\n" +"#%hostlist host-a host-b\n" +"\n" +"#* CHOOSER %hostlist #\n"; +#endif + +#ifdef XDMCP +static const char def_willing[] = +"#! /bin/sh\n" +"# The output of this script is displayed in the chooser window\n" +"# (instead of \"Willing to manage\").\n" +"\n" +"load=`uptime|sed -e 's/^.*load[^0-9]*//'`\n" +"nrusers=`who|cut -c 1-8|sort -u|wc -l|sed 's/^[ \t]*//'`\n" +"s=\"\"; [ \"$nrusers\" != 1 ] && s=s\n" +"\n" +"echo \"${nrusers} user${s}, load: ${load}\"\n"; +#endif + +static const char def_setup[] = +"#! /bin/sh\n" +"# Xsetup - run as root before the login dialog appears\n" +"\n" +"#xconsole -geometry 480x130-0-0 -notify -verbose -fn fixed -exitOnFail -file /dev/xconsole &\n"; + +static const char def_startup[] = +"#! /bin/sh\n" +"# Xstartup - run as root before session starts\n" +"\n" +"# By convention, both xconsole and xterm -C check that the\n" +"# console is owned by the invoking user and is readable before attaching\n" +"# the console output. This way a random user can invoke xterm -C without\n" +"# causing serious grief; still, it can cause havoc, so xconsole is started\n" +"# by Xsetup usually.\n" +"# This is not required if you use PAM with the pam_console module.\n" +"#\n" +"#chown $USER /dev/console\n" +"\n" +#ifdef _AIX +"# We create a pseudodevice for finger. (host:0 becomes xdm/host_0)\n" +"# Without it, finger errors out with \"Can't stat /dev/host:0\".\n" +"#\n" +"#devname=`echo $DISPLAY | cut -c1-8`\n" +"#if [ ! -d /dev/xdm ]; then\n" +"# mkdir /dev/xdm\n" +"# chmod 755 /dev/xdm\n" +"#fi\n" +"#touch /dev/xdm/$devname\n" +"#chmod 644 /dev/xdm/$devname\n" +"#exec sessreg -a -l xdm/$devname -h \"`echo $DISPLAY | cut -d: -f1`\"" +#else +"#exec sessreg -a -l $DISPLAY -h \"`echo $DISPLAY | cut -d: -f1`\"" +# ifdef BSD +" -x " KDMCONF "/Xservers" +# endif +#endif /* _AIX */ +" $USER\n" +"\n# NOTE: The session is aborted if the last command returns non-zero.\n"; + +static const char def_reset[] = +"#! /bin/sh\n" +"# Xreset - run as root after session exits\n" +"\n" +"# Reassign ownership of the console to root, this should disallow\n" +"# assignment of console output to any random users's xterm. See Xstartup.\n" +"#\n" +"#chown root /dev/console\n" +"#chmod 622 /dev/console\n" +"\n" +#ifdef _AIX +"#devname=`echo $DISPLAY | cut -c1-8`\n" +"#exec sessreg -d -l xdm/$devname -h \"`echo $DISPLAY | cut -d: -f1`\"" +#else +"#exec sessreg -d -l $DISPLAY -h \"`echo $DISPLAY | cut -d: -f1`\"" +# ifdef BSD +" -x " KDMCONF "/Xservers" +# endif +#endif /* _AIX */ +" $USER\n"; + +static const char def_session1[] = +"#! /bin/sh\n" +"# Xsession - run as user\n" +"\n" +"session=$1\n" +"\n" +"# Note that the respective logout scripts are not sourced.\n" +"case $SHELL in\n" +" */bash)\n" +" [ -z \"$BASH\" ] && exec $SHELL $0 \"$@\"\n" +" set +o posix\n" +" [ -f /etc/profile ] && . /etc/profile\n" +" if [ -f $HOME/.bash_profile ]; then\n" +" . $HOME/.bash_profile\n" +" elif [ -f $HOME/.bash_login ]; then\n" +" . $HOME/.bash_login\n" +" elif [ -f $HOME/.profile ]; then\n" +" . $HOME/.profile\n" +" fi\n" +" ;;\n" +" */zsh)\n" +" [ -z \"$ZSH_NAME\" ] && exec $SHELL $0 \"$@\"\n" +" emulate -R zsh\n" +" [ -d /etc/zsh ] && zdir=/etc/zsh || zdir=/etc\n" +" zhome=${ZDOTDIR:-$HOME}\n" +" # zshenv is always sourced automatically.\n" +" [ -f $zdir/zprofile ] && . $zdir/zprofile\n" +" [ -f $zhome/.zprofile ] && . $zhome/.zprofile\n" +" [ -f $zdir/zlogin ] && . $zdir/zlogin\n" +" [ -f $zhome/.zlogin ] && . $zhome/.zlogin\n" +" ;;\n" +" */csh|*/tcsh)\n" +" # [t]cshrc is always sourced automatically.\n" +" # Note that sourcing csh.login after .cshrc is non-standard.\n" +" xsess_tmp="; +static const char def_session2[] = +"\n" +" $SHELL -c \"if (-f /etc/csh.login) source /etc/csh.login; if (-f ~/.login) source ~/.login; /bin/sh -c export -p >! $xsess_tmp\"\n" +" . $xsess_tmp\n" +" rm -f $xsess_tmp\n" +" ;;\n" +" *) # Plain sh, ksh, and anything we don't know.\n" +" [ -f /etc/profile ] && . /etc/profile\n" +" [ -f $HOME/.profile ] && . $HOME/.profile\n" +" ;;\n" +"esac\n" +"\n" +"[ -f /etc/xprofile ] && . /etc/xprofile\n" +"[ -f $HOME/.xprofile ] && . $HOME/.xprofile\n" +"\n" +"case $session in\n" +" \"\")\n" +" exec xmessage -center -buttons OK:0 -default OK \"Sorry, $DESKTOP_SESSION is no valid session.\"\n" +" ;;\n" +" failsafe)\n" +" exec xterm -geometry 80x24-0-0\n" +" ;;\n" +" custom)\n" +" exec $HOME/.xsession\n" +" ;;\n" +" default)\n" +" exec " KDE_BINDIR "/startkde\n" +" ;;\n" +" *)\n" +" eval exec \"$session\"\n" +" ;;\n" +"esac\n" +"exec xmessage -center -buttons OK:0 -default OK \"Sorry, cannot execute $session. Check $DESKTOP_SESSION.desktop.\"\n"; + +static const char def_background[] = +"[Desktop0]\n" +"BackgroundMode=Flat\n" +"BlendBalance=100\n" +"BlendMode=NoBlending\n" +"ChangeInterval=60\n" +"Color1=0,0,200\n" +"Color2=192,192,192\n" +"CurrentWallpaper=0\n" +"LastChange=0\n" +"MinOptimizationDepth=1\n" +"MultiWallpaperMode=NoMulti\n" +"Pattern=fish\n" +"Program=\n" +"ReverseBlending=false\n" +"UseSHM=false\n" +"Wallpaper=default_blue.jpg\n" +"WallpaperList=\n" +"WallpaperMode=Scaled\n"; + +static char * +prepName( const char *fn ) +{ + const char *tname; + char *nname; + + tname = strrchr( fn, '/' ); + ASPrintf( &nname, "%s/%s", newdir, tname ? tname + 1 : fn ); + displace( nname ); + return nname; +} + +static FILE * +Create( const char *fn, int mode ) +{ + char *nname; + FILE *f; + + nname = prepName( fn ); + if (!(f = fopen( nname, "w" ))) { + fprintf( stderr, "Cannot create %s\n", nname ); + exit( 1 ); + } + chmod( nname, mode ); + free( nname ); + return f; +} + +static void +WriteOut( const char *fn, int mode, time_t stamp, const char *buf, size_t len ) +{ + char *nname; + int fd; + struct utimbuf utim; + + nname = prepName( fn ); + if ((fd = creat( nname, mode )) < 0) { + fprintf( stderr, "Cannot create %s\n", nname ); + exit( 1 ); + } + write( fd, buf, len ); + close( fd ); + if (stamp) { + utim.actime = utim.modtime = stamp; + utime( nname, &utim ); + } + free( nname ); +} + + +/* returns static array! */ +static const char * +resect( const char *sec, const char *name ) +{ + static char sname[64]; + char *p; + + if ((p = strrchr( sec, '-' ))) { + sprintf( sname, "%.*s-%s", p - sec, sec, name ); + return sname; + } else + return name; +} + +static int +inNewDir( const char *name ) +{ + return !memcmp( name, KDMCONF "/", sizeof(KDMCONF) ); +} + +static int +inList( StrList *sp, const char *s ) +{ + for (; sp; sp = sp->next) + if (!strcmp( sp->str, s )) + return 1; + return 0; +} + +static void +addStr( StrList **sp, const char *s ) +{ + for (; *sp; sp = &(*sp)->next) + if (!strcmp( (*sp)->str, s )) + return; + *sp = mcalloc( sizeof(**sp) ); + ASPrintf( (char **)&(*sp)->str, "%s", s ); +} + +StrList *aflist, *uflist, *eflist, *cflist, *lflist; + +/* file is part of new config */ +static void +addedFile( const char *fn ) +{ + addStr( &aflist, fn ); +} + +/* file from old config was parsed */ +static void +usedFile( const char *fn ) +{ + addStr( &uflist, fn ); +} + +/* file from old config was copied with slight modifications */ +static void +editedFile( const char *fn ) +{ + addStr( &eflist, fn ); +} + +/* file from old config was copied verbatim */ +static void +copiedFile( const char *fn ) +{ + addStr( &cflist, fn ); +} + +/* file from old config is still being used */ +static void +linkedFile( const char *fn ) +{ + addStr( &lflist, fn ); +} + +/* + * XXX this stuff is highly borked. it does not handle collisions at all. + */ +static int +copyfile( Entry *ce, const char *tname, int mode, int (*proc)( File * ) ) +{ + const char *tptr; + char *nname; + File file; + int rt; + + if (!*ce->value) + return 1; + + tptr = strrchr( tname, '/' ); + ASPrintf( &nname, KDMCONF "/%s", tptr ? tptr + 1 : tname ); + if (inList( cflist, ce->value ) || + inList( eflist, ce->value ) || + inList( lflist, ce->value )) + { + rt = 1; + goto doret; + } + if (!readFile( &file, ce->value )) { + fprintf( stderr, "Warning: cannot copy file %s\n", ce->value ); + rt = 0; + } else { + if (!proc || !proc( &file )) { + if (!use_destdir && !strcmp( ce->value, nname )) + linkedFile( nname ); + else { + struct stat st; + stat( ce->value, &st ); + WriteOut( nname, mode, st.st_mtime, file.buf, file.eof - file.buf ); + copiedFile( ce->value ); + } + } else { + WriteOut( nname, mode, 0, file.buf, file.eof - file.buf ); + editedFile( ce->value ); + } + if (strcmp( ce->value, nname ) && inNewDir( ce->value ) && !use_destdir) + displace( ce->value ); + addedFile( nname ); + rt = 1; + } + doret: + ce->value = nname; + return rt; +} + +static void +dlinkfile( const char *name ) +{ + File file; + + if (!readFile( &file, name )) { + fprintf( stderr, "Warning: cannot read file %s\n", name ); + return; + } + if (inNewDir( name ) && use_destdir) { + struct stat st; + stat( name, &st ); + WriteOut( name, st.st_mode, st.st_mtime, file.buf, file.eof - file.buf ); + copiedFile( name ); + } else + linkedFile( name ); + addedFile( name ); +} + +static void +linkfile( Entry *ce ) +{ + if (ce->written && *ce->value) + dlinkfile( ce->value ); +} + +static void +writefile( const char *tname, int mode, const char *cont ) +{ + WriteOut( tname, mode, 0, cont, strlen( cont ) ); + addedFile( tname ); +} + + +char *background; + +static void +handBgCfg( Entry *ce, Section *cs ATTR_UNUSED ) +{ + if (!ce->active) /* can be only the X-*-Greeter one */ + writefile( def_BackgroundCfg, 0644, + background ? background : def_background ); +#if 0 /* risk of kcontrol clobbering the original file */ + else if (old_confs) + linkfile( ce ); +#endif + else { + if (!copyfile( ce, ce->value, 0644, 0 )) { + if (!strcmp( cs->name, "X-*-Greeter" )) + writefile( def_BackgroundCfg, 0644, def_background ); + ce->active = 0; + } + } +} + + +#ifdef HAVE_VTS +static char * +mem_mem( char *mem, int lmem, const char *smem, int lsmem ) +{ + for (; lmem >= lsmem; mem++, lmem--) + if (!memcmp( mem, smem, lsmem )) + return mem + lsmem; + return 0; +} + +static int maxTTY, TTYmask; + +static void +getInitTab( void ) +{ + File it; + char *p, *eol, *ep; + int tty; + + if (maxTTY) + return; + if (readFile( &it, "/etc/inittab" )) { + usedFile( "/etc/inittab" ); + for (p = it.buf; p < it.eof; p = eol + 1) { + for (eol = p; eol < it.eof && *eol != '\n'; eol++); + if (*p != '#') { + if ((ep = mem_mem( p, eol - p, " tty", 4 )) && + ep < eol && isdigit( *ep )) + { + if (ep + 1 == eol || isspace( *(ep + 1) )) + tty = *ep - '0'; + else if (isdigit( *(ep + 1) ) && + (ep + 2 == eol || isspace( *(ep + 2) ))) + tty = (*ep - '0') * 10 + (*(ep + 1) - '0'); + else + continue; + TTYmask |= 1 << (tty - 1); + if (tty > maxTTY) + maxTTY = tty; + } + } + } + freeBuf( &it ); + } + if (!maxTTY) { + maxTTY = 6; + TTYmask = 0x3f; + } +} +#endif + + +/* TODO: handle solaris' local_uid specs */ + +static char * +ReadWord( File *file, int EOFatEOL ) +{ + char *wordp, *wordBuffer; + int quoted; + char c; + + rest: + wordp = wordBuffer = file->cur; + mloop: + quoted = 0; + qloop: + if (file->cur == file->eof) { + doeow: + if (wordp == wordBuffer) + return 0; + retw: + *wordp = '\0'; + return wordBuffer; + } + c = *file->cur++; + switch (c) { + case '#': + if (quoted) + break; + do { + if (file->cur == file->eof) + goto doeow; + c = *file->cur++; + } while (c != '\n'); + case '\0': + case '\n': + if (EOFatEOL && !quoted) { + file->cur--; + goto doeow; + } + if (wordp != wordBuffer) { + file->cur--; + goto retw; + } + goto rest; + case ' ': + case '\t': + if (wordp != wordBuffer) + goto retw; + goto rest; + case '\\': + if (!quoted) { + quoted = 1; + goto qloop; + } + break; + } + *wordp++ = c; + goto mloop; +} + +/* backslashes are double-escaped - for KConfig and for parseArgs */ +static const char * +joinArgs( StrList *argv ) +{ + StrList *av; + const char *s, *rs; + char *str; + int slen; + + if (!argv) + return ""; + for (slen = 0, av = argv; slen++, av; av = av->next) { + int nq = 0; + for (s = av->str; *s; s++, slen++) + if (isspace( *s ) || *s == '\'') + nq = 2; + else if (*s == '"') + slen += 2; + else if (*s == '\\') + slen += 3; + slen += nq; + } + rs = str = mmalloc( slen ); + for (av = argv; av; av = av->next) { + int nq = 0; + for (s = av->str; *s; s++) + if (isspace( *s ) || *s == '\'') + nq = 2; + if (av != argv) + *str++ = ' '; + if (nq) + *str++ = '"'; + for (s = av->str; *s; s++) { + if (*s == '\\') + *str++ = '\\'; + if (*s == '"' || *s == '\\') { + *str++ = '\\'; + *str++ = '\\'; + } + *str++ = *s; + } + if (nq) + *str++ = '"'; + } + *str = 0; + return rs; +} + +# define dLocation 1 +# define dLocal 0 +# define dForeign 1 + +static struct displayMatch { + const char *name; + int len, type; +} displayTypes[] = { + { "local", 5, dLocal }, + { "foreign", 7, dForeign }, +}; + +static int +parseDisplayType( const char *string, const char **atPos ) +{ + struct displayMatch *d; + + *atPos = 0; + for (d = displayTypes; d < displayTypes + as(displayTypes); d++) { + if (!memcmp( d->name, string, d->len ) && + (!string[d->len] || string[d->len] == '@')) + { + if (string[d->len] == '@' && string[d->len + 1]) + *atPos = string + d->len + 1; + return d->type; + } + } + return -1; +} + +typedef struct serverEntry { + struct serverEntry *next; + const char *name, *class2, *console, *argvs, *arglvs; + StrList *argv, *arglv; + int type, reserve, vt; +} ServerEntry; + +static void +absorb_xservers( const char *sect ATTR_UNUSED, char **value ) +{ + ServerEntry *se, *se1, *serverList, **serverPtr; + const char *word, *word2; + char *sdpys, *rdpys; + StrList **argp, **arglp, *ap, *ap2; + File file; + int nldpys = 0, nrdpys = 0, dpymask = 0; + int cpcmd, cpcmdl; +#ifdef HAVE_VTS + int dn, cpvt, mtty; +#endif + + if (**value == '/') { + if (!readFile( &file, *value )) + return; + usedFile( *value ); + } else { + file.buf = *value; + file.eof = *value + strlen( *value ); + } + file.cur = file.buf; + + serverPtr = &serverList; +#ifdef HAVE_VTS + bustd: +#endif + while ((word = ReadWord( &file, 0 ))) { + se = mcalloc( sizeof(*se) ); + se->name = word; + if (!(word = ReadWord( &file, 1 ))) + continue; + se->type = parseDisplayType( word, &se->console ); + if (se->type < 0) { + se->class2 = word; + if (!(word = ReadWord( &file, 1 ))) + continue; + se->type = parseDisplayType( word, &se->console ); + if (se->type < 0) { + while (ReadWord( &file, 1 )); + continue; + } + } + word = ReadWord( &file, 1 ); + if (word && !strcmp( word, "reserve" )) { + se->reserve = 1; + word = ReadWord( &file, 1 ); + } + if (((se->type & dLocation) == dLocal) != (word != 0)) + continue; + argp = &se->argv; + arglp = &se->arglv; + while (word) { +#ifdef HAVE_VTS + if (word[0] == 'v' && word[1] == 't') + se->vt = atoi( word + 2 ); + else if (!strcmp( word, "-crt" )) { /* SCO style */ + if (!(word = ReadWord( &file, 1 )) || + memcmp( word, "/dev/tty", 8 )) + goto bustd; + se->vt = atoi( word + 8 ); + } else +#endif + if (strcmp( word, se->name )) { + ap = mmalloc( sizeof(*ap) ); + ap->str = word; + if (!strcmp( word, "-nolisten" )) { + if (!(word2 = ReadWord( &file, 1 ))) + break; + ap2 = mmalloc( sizeof(*ap2) ); + ap2->str = word2; + ap->next = ap2; + if (!strcmp( word2, "unix" )) { + *argp = ap; + argp = &ap2->next; + } else { + *arglp = ap; + arglp = &ap2->next; + } + } else { + *argp = ap; + argp = &ap->next; + } + } + word = ReadWord( &file, 1 ); + } + *argp = *arglp = 0; + if ((se->type & dLocation) == dLocal) { + nldpys++; + dpymask |= 1 << atoi( se->name + 1 ); + if (se->reserve) + nrdpys++; + } + *serverPtr = se; + serverPtr = &se->next; + } + *serverPtr = 0; + +#ifdef HAVE_VTS + /* don't copy only if all local displays are ordered and have a vt */ + cpvt = 0; + getInitTab(); + for (se = serverList, mtty = maxTTY; se; se = se->next) + if ((se->type & dLocation) == dLocal) { + mtty++; + if (se->vt != mtty) { + cpvt = 1; + break; + } + } +#endif + + for (se = serverList; se; se = se->next) { + se->argvs = joinArgs( se->argv ); + se->arglvs = joinArgs( se->arglv ); + } + + se1 = 0, cpcmd = cpcmdl = 0; + for (se = serverList; se; se = se->next) + if ((se->type & dLocation) == dLocal) { + if (!se1) + se1 = se; + else { + if (strcmp( se1->argvs, se->argvs )) + cpcmd = 1; + if (strcmp( se1->arglvs, se->arglvs )) + cpcmdl = 1; + } + } + if (se1) { + putfqval( "X-:*-Core", "ServerCmd", se1->argvs ); + putfqval( "X-:*-Core", "ServerArgsLocal", se1->arglvs ); + for (se = serverList; se; se = se->next) + if ((se->type & dLocation) == dLocal) { + char sec[32]; + sprintf( sec, "X-%s-Core", se->name ); + if (cpcmd) + putfqval( sec, "ServerCmd", se->argvs ); + if (cpcmdl) + putfqval( sec, "ServerArgsLocal", se->arglvs ); +#ifdef HAVE_VTS + if (cpvt && se->vt) { + char vt[8]; + sprintf( vt, "%d", se->vt ); + putfqval( sec, "ServerVT", vt ); + } +#else + if (se->console) + putfqval( sec, "ServerTTY", se->console ); +#endif + } + } + + sdpys = rdpys = 0; + for (se = serverList; se; se = se->next) + StrCat( se->reserve ? &rdpys : &sdpys, + se->class2 ? ",%s_%s" : ",%s", se->name, se->class2 ); + +#ifdef HAVE_VTS + /* add reserve dpys */ + if (nldpys < 4 && nldpys && !nrdpys) + for (; nldpys < 4; nldpys++) { + for (dn = 0; dpymask & (1 << dn); dn++); + dpymask |= (1 << dn); + StrCat( &rdpys, ",:%d", dn ); + } +#endif + + putfqval( "General", "StaticServers", sdpys ? sdpys + 1 : "" ); + putfqval( "General", "ReserveServers", rdpys ? rdpys + 1 : "" ); + + if (**value == '/' && inNewDir( *value ) && !use_destdir) + displace( *value ); +} + +#ifdef HAVE_VTS +static void +upd_servervts( Entry *ce, Section *cs ATTR_UNUSED ) +{ + if (!ce->active) { /* there is only the Global one */ +#ifdef __linux__ /* XXX actually, sysvinit */ + getInitTab(); + ASPrintf( (char **)&ce->value, "-%d", maxTTY + 1 ); + ce->active = ce->written = 1; +#endif + } +} + +static void +upd_consolettys( Entry *ce, Section *cs ATTR_UNUSED ) +{ + if (!ce->active) { /* there is only the Global one */ +#ifdef __linux__ /* XXX actually, sysvinit */ + char *buf; + int i; + + getInitTab(); + for (i = 0, buf = 0; i < 16; i++) + if (TTYmask & (1 << i)) + StrCat( &buf, ",tty%d", i + 1 ); + if (buf) { + ce->value = buf + 1; + ce->active = ce->written = 1; + } +#endif + } +} +#endif + +#ifdef XDMCP +static void +cp_keyfile( Entry *ce, Section *cs ATTR_UNUSED ) +{ + if (!ce->active) /* there is only the Global one */ + return; + if (old_confs) + linkfile( ce ); + else + if (!copyfile( ce, "kdmkeys", 0600, 0 )) + ce->active = 0; +} + +static void +mk_xaccess( Entry *ce, Section *cs ATTR_UNUSED ) +{ + if (!ce->active) /* there is only the Global one */ + writefile( def_Xaccess, 0644, def_xaccess ); + else if (old_confs) + linkfile( ce ); + else + copyfile( ce, "Xaccess", 0644, 0 ); /* don't handle error, it will disable Xdmcp automatically */ +} + +static void +mk_willing( Entry *ce, Section *cs ATTR_UNUSED ) +{ + const char *fname; + + if (!ce->active) /* there is only the Global one */ + goto dflt; + else { + if (!(fname = strchr( ce->value, '/' ))) + return; /* obviously in-line (or empty) */ + if (old_scripts || inNewDir( fname )) + dlinkfile( fname ); + else { + dflt: + ce->value = KDMCONF "/Xwilling"; + ce->active = ce->written = 1; + writefile( ce->value, 0755, def_willing ); + } + } +} +#endif + +/* +static int +edit_resources( File *file ) +{ + // XXX remove any login*, chooser*, ... resources + return 0; +} +*/ + +static void +cp_resources( Entry *ce, Section *cs ATTR_UNUSED ) +{ + if (!ce->active) /* the X-*-Greeter one */ + return; + if (old_confs) + linkfile( ce ); + else + if (!copyfile( ce, ce->value, 0644, 0/*edit_resources*/ )) + ce->active = 0; +} + +static int +delstr( File *fil, const char *pat ) +{ + char *p, *pp, *bpp; + const char *pap, *paap; + + *fil->eof = 0; + for (p = fil->buf; *p; p++) { + for (pp = p, pap = pat; ; ) { + if (!*pap) { + *p = '\n'; + memcpy( p + 1, pp, fil->eof - pp + 1 ); + fil->eof -= pp - p - 1; + return 1; + } else if (!memcmp( pap, "*/", 2 )) { + paap = pap += 2; + while (!isspace( *pap )) + pap++; + if (*pp != '/') + break; + for (;;) + for (bpp = ++pp; *pp != '/'; pp++) + if (!*pp || isspace( *pp )) + goto wbrk; + wbrk: + if ((pp - bpp != pap - paap) || memcmp( bpp, paap, pap - paap )) + break; + } else if (*pap == '\t') { + pap++; + while (*pp == ' ' || *pp == '\t') + pp++; + } else if (*pap == '[') { + pap++; + for (;;) { + if (!*pap) { + fprintf( stderr, "Internal error: unterminated char set\n" ); + exit( 1 ); + } + if (*pap == *pp) { + while (*++pap != ']') + if (!*pap) { + fprintf( stderr, "Internal error: unterminated char set\n" ); + exit( 1 ); + } + pap++; + pp++; + break; + } + if (*++pap == ']') + goto no; + } + } else { + if (*pap == '\n') + while (*pp == ' ' || *pp == '\t') + pp++; + if (*pap != *pp) + break; + pap++; + pp++; + } + } + no: ; + } + return 0; +} + +/* XXX + the UseBackground voodoo will horribly fail, if multiple sections link + to the same Xsetup file +*/ + +static int mod_usebg; + +static int +edit_setup( File *file ) +{ + int chg = + delstr( file, "\n" + "(\n" + " PIDFILE=/var/run/kdmdesktop-$DISPLAY.pid\n" + " */kdmdesktop\t&\n" + " echo $! >$PIDFILE\n" + " wait $!\n" + " rm $PIDFILE\n" + ")\t&\n" ) | + delstr( file, "\n" + "*/kdmdesktop\t&\n" ) | + delstr( file, "\n" + "kdmdesktop\t&\n" ) | + delstr( file, "\n" + "kdmdesktop\n" ); + putval( "UseBackground", chg ? "true" : "false" ); + return chg; +} + +static void +mk_setup( Entry *ce, Section *cs ) +{ + setsect( resect( cs->name, "Greeter" ) ); + if (old_scripts || mixed_scripts) { + if (mod_usebg && *ce->value) + putval( "UseBackground", "false" ); + linkfile( ce ); + } else { + if (ce->active && inNewDir( ce->value )) { + if (mod_usebg) + copyfile( ce, ce->value, 0755, edit_setup ); + else + linkfile( ce ); + } else { + ce->value = KDMCONF "/Xsetup"; + ce->active = ce->written = 1; + writefile( ce->value, 0755, def_setup ); + } + } +} + +static int +edit_startup( File *file ) +{ + int chg1 = 0, chg2 = 0; + + if (mod_usebg && + (delstr( file, "\n" + "PIDFILE=/var/run/kdmdesktop-$DISPLAY.pid\n" + "if [[] -f $PIDFILE ] ; then\n" + " kill `cat $PIDFILE`\n" + "fi\n" ) || + delstr( file, "\n" + "PIDFILE=/var/run/kdmdesktop-$DISPLAY.pid\n" + "test -f $PIDFILE && kill `cat $PIDFILE`\n" ))) + chg1 = 1; + if (oldver < 0x0203) { + chg2 = +#ifdef _AIX + delstr( file, "\n" +"# We create a pseudodevice for finger. (host:0 becomes [kx]dm/host_0)\n" ); +"# Without it, finger errors out with \"Can't stat /dev/host:0\".\n" +"#\n" +"if [[] -f /usr/lib/X11/xdm/sessreg ]; then\n" +" devname=`echo $DISPLAY | /usr/bin/sed -e 's/[[]:\\.]/_/g' | /usr/bin/cut -c1-8`\n" +" hostname=`echo $DISPLAY | /usr/bin/cut -d':' -f1`\n" +"\n" +" if [[] -z \"$devname\" ]; then\n" +" devname=\"unknown\"\n" +" fi\n" +" if [[] ! -d /dev/[kx]dm ]; then\n" +" /usr/bin/mkdir /dev/[kx]dm\n" +" /usr/bin/chmod 755 /dev/[kx]dm\n" +" fi\n" +" /usr/bin/touch /dev/[kx]dm/$devname\n" +" /usr/bin/chmod 644 /dev/[kx]dm/$devname\n" +"\n" +" if [[] -z \"$hostname\" ]; then\n" +" exec /usr/lib/X11/xdm/sessreg -a -l [kx]dm/$devname $USER\n" +" else\n" +" exec /usr/lib/X11/xdm/sessreg -a -l [kx]dm/$devname -h $hostname $USER\n" +" fi\n" +"fi\n") | +#else +# ifdef BSD + delstr( file, "\n" +"exec sessreg -a -l $DISPLAY -x */Xservers -u " _PATH_UTMP " $USER\n" ) | +# endif +#endif /* _AIX */ + delstr( file, "\n" +"exec sessreg -a -l $DISPLAY" +#ifdef BSD +" -x */Xservers" +#endif +" $USER\n" ) | + delstr( file, "\n" +"exec sessreg -a -l $DISPLAY -u /var/run/utmp -x */Xservers $USER\n" ); + putval( "UseSessReg", chg2 ? "true" : "false"); + } + return chg1 | chg2; +} + +static void +mk_startup( Entry *ce, Section *cs ) +{ + setsect( cs->name ); + if (old_scripts || mixed_scripts) + linkfile( ce ); + else { + if (ce->active && inNewDir( ce->value )) { + if (mod_usebg || oldver < 0x0203) + copyfile( ce, ce->value, 0755, edit_startup ); + else + linkfile( ce ); + } else { + ce->value = KDMCONF "/Xstartup"; + ce->active = ce->written = 1; + writefile( ce->value, 0755, def_startup ); + } + } +} + +static int +edit_reset( File *file ) +{ + return +#ifdef _AIX + delstr( file, "\n" +"if [[] -f /usr/lib/X11/xdm/sessreg ]; then\n" +" devname=`echo $DISPLAY | /usr/bin/sed -e 's/[[]:\\.]/_/g' | /usr/bin/cut -c1-8`\n" +" exec /usr/lib/X11/xdm/sessreg -d -l [kx]dm/$devname $USER\n" +"fi\n" ) | +#else +# ifdef BSD + delstr( file, "\n" +"exec sessreg -d -l $DISPLAY -x */Xservers -u " _PATH_UTMP " $USER\n" ) | +# endif +#endif /* _AIX */ + delstr( file, "\n" +"exec sessreg -d -l $DISPLAY" +# ifdef BSD +" -x */Xservers" +# endif +" $USER\n" ) | + delstr( file, "\n" +"exec sessreg -d -l $DISPLAY -u /var/run/utmp -x */Xservers $USER\n" ); +} + +static void +mk_reset( Entry *ce, Section *cs ATTR_UNUSED ) +{ + if (old_scripts || mixed_scripts) + linkfile( ce ); + else { + if (ce->active && inNewDir( ce->value )) { + if (oldver < 0x0203) + copyfile( ce, ce->value, 0755, edit_reset ); + else + linkfile( ce ); + } else { + ce->value = KDMCONF "/Xreset"; + ce->active = ce->written = 1; + writefile( ce->value, 0755, def_reset ); + } + } +} + +static void +mk_session( Entry *ce, Section *cs ATTR_UNUSED ) +{ + char *def_session; + const char *tmpf; + + if ((old_scripts || (ce->active && inNewDir( ce->value ))) && + oldver >= 0x202) + linkfile( ce ); + else { + tmpf = locate( "mktemp" ) ? + "`mktemp /tmp/xsess-env-XXXXXX`" : + locate( "tempfile" ) ? + "`tempfile`" : + "$HOME/.xsession-env-$DISPLAY"; + ASPrintf( &def_session, "%s%s%s", def_session1, tmpf, def_session2 ); + ce->value = KDMCONF "/Xsession"; + ce->active = ce->written = 1; + writefile( ce->value, 0755, def_session ); + } +} + +static void +upd_language( Entry *ce, Section *cs ATTR_UNUSED ) +{ + if (!strcmp( ce->value, "C" )) + ce->value = (char *)"en_US"; +} + +static void +upd_guistyle( Entry *ce, Section *cs ATTR_UNUSED ) +{ + if (!strcmp( ce->value, "Motif+" )) + ce->value = (char *)"MotifPlus"; + else if (!strcmp( ce->value, "KDE" )) + ce->value = (char *)"Default"; +} + +static void +upd_showusers( Entry *ce, Section *cs ) +{ + if (!strcmp( ce->value, "All" )) + ce->value = (char *)"NotHidden"; + else if (!strcmp( ce->value, "None" )) { + if (ce->active) + putfqval( cs->name, "UserList", "false" ); + ce->value = (char *)"Selected"; + ce->active = 0; + ce->written = 1; + } +} + +static const char *defminuid, *defmaxuid; + +static void +upd_minshowuid( Entry *ce, Section *cs ATTR_UNUSED ) +{ + if (!ce->active) { + ce->value = defminuid; + ce->active = ce->written = 1; + } +} + +static void +upd_maxshowuid( Entry *ce, Section *cs ATTR_UNUSED ) +{ + if (!ce->active) { + ce->value = defmaxuid; + ce->active = ce->written = 1; + } +} + +static void +upd_hiddenusers( Entry *ce, Section *cs ATTR_UNUSED ) +{ + char *nv; + const char *msu, *pt, *et; + struct passwd *pw; + unsigned minuid, maxuid; + char nbuf[128]; + + if (!ce->active) + return; + + msu = getfqval( cs->name, "MinShowUID", "0" ); + sscanf( msu, "%u", &minuid ); + msu = getfqval( cs->name, "MaxShowUID", "65535" ); + sscanf( msu, "%u", &maxuid ); + + nv = 0; + pt = ce->value; + for (;;) { + et = strpbrk( pt, ";," ); + if (et) { + memcpy( nbuf, pt, et - pt ); + nbuf[et - pt] = 0; + } else + strcpy( nbuf, pt ); + if ((pw = getpwnam( nbuf ))) { + if (!pw->pw_uid || + (pw->pw_uid >= minuid && pw->pw_uid <= maxuid)) + { + if (nv) + StrCat( &nv, ",%s", nbuf ); + else + nv = mstrdup( nbuf ); + } + } + if (!et) + break; + pt = et + 1; + } + ce->value = nv ? nv : ""; +} + +static void +upd_forgingseed( Entry *ce, Section *cs ATTR_UNUSED ) +{ + if (!ce->active) { + ASPrintf( (char **)&ce->value, "%d", time( 0 ) ); + ce->active = ce->written = 1; + } +} + +static void +upd_fifodir( Entry *ce, Section *cs ATTR_UNUSED ) +{ + const char *dir; + struct stat st; + + if (use_destdir) + return; + dir = ce->active ? ce->value : def_FifoDir; + stat( dir, &st ); + chmod( dir, st.st_mode | 0755 ); +} + +static void +upd_datadir( Entry *ce, Section *cs ATTR_UNUSED ) +{ + char *oldsts, *newsts; + const char *dir; + + if (use_destdir) + return; + dir = ce->active ? ce->value : def_DataDir; + if (mkdirp( dir, 0755, "data", 0 ) && oldkde) { + ASPrintf( &oldsts, "%s/kdm/kdmsts", oldkde ); + ASPrintf( &newsts, "%s/kdmsts", dir ); + rename( oldsts, newsts ); + } +} + +static void +CopyFile( const char *from, const char *to ) +{ + File file; + int fd; + + if (readFile( &file, from )) { + if ((fd = creat( to, 0644 )) >= 0) { + write( fd, file.buf, file.eof - file.buf ); + close( fd ); + } + freeBuf( &file ); + } +} + +static void +upd_facedir( Entry *ce, Section *cs ATTR_UNUSED ) +{ + char *oldpic, *newpic, *defpic, *rootpic; + const char *dir; + struct passwd *pw; + + if (use_destdir) + return; + dir = ce->active ? ce->value : def_FaceDir; + if (mkdirp( dir, 0755, "user face", 0 )) { + ASPrintf( &defpic, "%s/.default.face.icon", dir ); + ASPrintf( &rootpic, "%s/root.face.icon", dir ); + if (oldkde) { + setpwent(); + while ((pw = getpwent())) + if (strcmp( pw->pw_name, "root" )) { + ASPrintf( &oldpic, "%s/../apps/kdm/pics/users/%s.png", + oldkde, pw->pw_name ); + ASPrintf( &newpic, "%s/%s.face.icon", dir, pw->pw_name ); + rename( oldpic, newpic ); + free( newpic ); + free( oldpic ); + } + endpwent(); + ASPrintf( &oldpic, "%s/../apps/kdm/pics/users/default.png", oldkde ); + if (!rename( oldpic, defpic )) + defpic = 0; + ASPrintf( &oldpic, "%s/../apps/kdm/pics/users/root.png", oldkde ); + if (!rename( oldpic, rootpic )) + rootpic = 0; + } + if (defpic) { + ASPrintf( &oldpic, "%s/default1.png", facesrc ); + CopyFile( oldpic, defpic ); + } + if (rootpic) { + ASPrintf( &oldpic, "%s/root1.png", facesrc ); + CopyFile( oldpic, rootpic ); + } + } +} + +CONF_GEN_ENTRIES + +static Sect * +findSect( const char *name ) +{ + const char *p; + int i; + + p = strrchr( name, '-' ); + if (!p) + p = name; + for (i = 0; i < as(allSects); i++) + if (!strcmp( allSects[i]->name, p )) + return allSects[i]; + fprintf( stderr, "Internal error: unknown section %s\n", name ); + exit( 1 ); +} + +static Ent * +findEnt( Sect *sect, const char *key ) +{ + int i; + + for (i = 0; i < sect->nents; i++) + if (!strcmp( sect->ents[i].key, key )) + return sect->ents + i; + fprintf( stderr, "Internal error: unknown key %s in section %s\n", + key, sect->name ); + exit( 1 ); +} + + +/* + * defaults + */ + +typedef struct DEnt { + const char *key; + const char *value; + int active; +} DEnt; + +typedef struct DSect { + const char *name; + DEnt *ents; + int nents; + const char *comment; +} DSect; + +CONF_GEN_EXAMPLE + +static void +mkdefconf( void ) +{ + Section *cs, **csp; + Entry *ce, **cep; + int sc, ec; + + for (csp = &config, sc = 0; sc < as(dAllSects); csp = &(cs->next), sc++) { + cs = mcalloc( sizeof(*cs) ); + *csp = cs; + cs->spec = findSect( dAllSects[sc].name ); + cs->name = dAllSects[sc].name; + cs->comment = dAllSects[sc].comment; + for (cep = &(cs->ents), ec = 0; ec < dAllSects[sc].nents; + cep = &(ce->next), ec++) + { + ce = mcalloc( sizeof(*ce) ); + *cep = ce; + ce->spec = findEnt( cs->spec, dAllSects[sc].ents[ec].key ); + ce->value = dAllSects[sc].ents[ec].value; + ce->active = dAllSects[sc].ents[ec].active; + } + } +} + + +/* + * read rc file structure + */ + +typedef struct REntry { + struct REntry *next; + const char *key; + char *value; +} REntry; + +typedef struct RSection { + struct RSection *next; + const char *name; + REntry *ents; +} RSection; + +static RSection * +ReadConf( const char *fname ) +{ + char *nstr; + char *s, *e, *st, *en, *ek, *sl; + RSection *rootsec = 0, *cursec; + REntry *curent; + int nlen; + int line, sectmoan; + File file; + + if (!readFile( &file, fname )) + return 0; + usedFile( fname ); + + for (s = file.buf, line = 0, cursec = 0, sectmoan = 1; s < file.eof; s++) { + line++; + + while ((s < file.eof) && isspace( *s ) && (*s != '\n')) + s++; + + if ((s < file.eof) && ((*s == '\n') || (*s == '#'))) { + sktoeol: + while ((s < file.eof) && (*s != '\n')) + s++; + continue; + } + sl = s; + + if (*s == '[') { + while ((s < file.eof) && (*s != '\n')) + s++; + e = s - 1; + while ((e > sl) && isspace( *e )) + e--; + if (*e != ']') { + fprintf( stderr, "Invalid section header at %s:%d\n", + fname, line ); + continue; + } + sectmoan = 0; + nstr = sl + 1; + nlen = e - nstr; + for (cursec = rootsec; cursec; cursec = cursec->next) + if (!memcmp( nstr, cursec->name, nlen ) && + !cursec->name[nlen]) + { +#if 0 /* not our business ... */ + fprintf( stderr, "Warning: Multiple occurrences of section " + "[%.*s] in %s. Consider merging them.\n", + nlen, nstr, fname ); +#endif + goto secfnd; + } + cursec = mmalloc( sizeof(*cursec) ); + ASPrintf( (char **)&cursec->name, "%.*s", nlen, nstr ); + cursec->ents = 0; + cursec->next = rootsec; + rootsec = cursec; + secfnd: + continue; + } + + if (!cursec) { + if (sectmoan) { + sectmoan = 0; + fprintf( stderr, "Entry outside any section at %s:%d", + fname, line ); + } + goto sktoeol; + } + + for (; (s < file.eof) && (*s != '\n'); s++) + if (*s == '=') + goto haveeq; + fprintf( stderr, "Invalid entry (missing '=') at %s:%d\n", fname, line ); + continue; + + haveeq: + for (ek = s - 1;; ek--) { + if (ek < sl) { + fprintf( stderr, "Invalid entry (empty key) at %s:%d\n", + fname, line ); + goto sktoeol; + } + if (!isspace( *ek )) + break; + } + + s++; + while ((s < file.eof) && isspace( *s ) && (*s != '\n')) + s++; + st = s; + while ((s < file.eof) && (*s != '\n')) + s++; + for (en = s - 1; en >= st && isspace( *en ); en--); + + nstr = sl; + nlen = ek - sl + 1; + for (curent = cursec->ents; curent; curent = curent->next) + if (!memcmp( nstr, curent->key, nlen ) && + !curent->key[nlen]) { + fprintf( stderr, "Multiple occurrences of key '%s' in section " + "[%s] of %s.\n", curent->key, cursec->name, fname ); + goto keyfnd; + } + curent = mmalloc( sizeof(*curent) ); + ASPrintf( (char **)&curent->key, "%.*s", nlen, nstr ); + ASPrintf( (char **)&curent->value, "%.*s", en - st + 1, st ); + curent->next = cursec->ents; + cursec->ents = curent; + keyfnd: + continue; + } + return rootsec; +} + + +static int +mergeKdmRcOld( const char *path ) +{ + char *p; + struct stat st; + + ASPrintf( &p, "%s/kdmrc", path ); + if (stat( p, &st )) { + free( p ); + return 0; + } + printf( "Information: ignoring old kdmrc %s from kde < 2.2\n", p ); + free( p ); + return 1; +} + +typedef struct { + const char *sect, *key, *def; + int (*cond)( void ); +} FDefs; + +static void +applydefs( FDefs *chgdef, int ndefs, const char *path ) +{ + char *p; + int i; + + for (i = 0; i < ndefs; i++) + if (!getfqval( chgdef[i].sect, chgdef[i].key, 0 ) && + (!chgdef[i].cond || chgdef[i].cond())) + { + ASPrintf( &p, chgdef[i].def, path ); + putfqval( chgdef[i].sect, chgdef[i].key, p ); + free( p ); + } +} + +#ifdef XDMCP +static FDefs kdmdefs_all[] = { +{ "Xdmcp", "Xaccess", "%s/kdm/Xaccess", 0 }, +{ "Xdmcp", "Willing", "", 0 }, +}; +#endif + +static FDefs kdmdefs_eq_22[] = { +{ "General", "PidFile", "/var/run/xdm.pid", 0 }, +{ "X-*-Core", "Setup", "%s/kdm/Xsetup", 0 }, +{ "X-*-Core", "Startup", "%s/kdm/Xstartup", 0 }, +{ "X-*-Core", "Reset", "%s/kdm/Xreset", 0 }, +{ "X-*-Core", "Session", "%s/kdm/Xsession", 0 }, +}; + +#ifdef XDMCP +static int +if_xdmcp (void) +{ + return isTrue( getfqval( "Xdmcp", "Enable", "true" ) ); +} + +static FDefs kdmdefs_le_30[] = { +{ "Xdmcp", "KeyFile", "%s/kdm/kdmkeys", if_xdmcp }, +}; +#endif + +/* HACK: misused by is22conf() below */ +static FDefs kdmdefs_ge_30[] = { +{ "X-*-Core", "Setup", "", 0 }, +{ "X-*-Core", "Startup", "", 0 }, +{ "X-*-Core", "Reset", "", 0 }, +{ "X-*-Core", "Session", XBINDIR "/xterm -ls -T", 0 }, +}; + +static int +if_usebg (void) +{ + return isTrue( getfqval( "X-*-Greeter", "UseBackground", "true" ) ); +} + +static FDefs kdmdefs_ge_31[] = { +{ "X-*-Greeter","BackgroundCfg","%s/kdm/backgroundrc", if_usebg }, +}; + +static int +is22conf( const char *path ) +{ + char *p; + const char *val; + int i, sl; + + sl = ASPrintf( &p, "%s/kdm/", path ); + /* safe bet, i guess ... */ + for (i = 0; i < 4; i++) { + val = getfqval( "X-*-Core", kdmdefs_ge_30[i].key, 0 ); + if (val && !memcmp( val, p, sl )) { + free( p ); + return 0; + } + } + free( p ); + return 1; +} + +typedef struct KUpdEnt { + const char *okey, *nsec, *nkey; + void (*func)( const char *sect, char **value ); +} KUpdEnt; + +typedef struct KUpdSec { + const char *osec; + KUpdEnt *ents; + int nents; +} KUpdSec; + +#ifdef XDMCP +static void +P_EnableChooser( const char *sect ATTR_UNUSED, char **value ) +{ + *value = (char *)(isTrue( *value ) ? "DefaultLocal" : "LocalOnly"); +} +#endif + +static void +P_UseLilo( const char *sect ATTR_UNUSED, char **value ) +{ + *value = (char *)(isTrue( *value ) ? "Lilo" : "None"); +} + +CONF_GEN_KMERGE + +static int +mergeKdmRcNewer( const char *path ) +{ + char *p; + const char *cp, *sec, *key; + RSection *rootsect, *cs; + REntry *ce; + int i, j; + static char sname[64]; + + ASPrintf( &p, "%s/kdm/kdmrc", path ); + if (!(rootsect = ReadConf( p ))) { + free( p ); + return 0; + } + printf( "Information: reading current kdmrc %s (from kde >= 2.2.x)\n", p ); + free( p ); + + for (cs = rootsect; cs; cs = cs->next) { + if (!strcmp( cs->name, "Desktop0" )) { + background = mstrdup( "[Desktop0]\n" ); + for (ce = cs->ents; ce; ce = ce->next) + StrCat( &background, "%s=%s\n", ce->key, ce->value ); + } else { + cp = strrchr( cs->name, '-' ); + if (!cp) + cp = cs->name; + else if (cs->name[0] != 'X' || cs->name[1] != '-') + goto dropsec; + for (i = 0; i < as(kupsects); i++) + if (!strcmp( cp, kupsects[i].osec )) { + for (ce = cs->ents; ce; ce = ce->next) { + for (j = 0; j < kupsects[i].nents; j++) + if (!strcmp( ce->key, kupsects[i].ents[j].okey )) { + if (kupsects[i].ents[j].nsec == (char *)-1) { + kupsects[i].ents[j].func( 0, &ce->value ); + goto gotkey; + } + if (!kupsects[i].ents[j].nsec) + sec = cs->name; + else { + sec = sname; + sprintf( sname, "%.*s-%s", cp - cs->name, cs->name, + kupsects[i].ents[j].nsec ); + } + if (!kupsects[i].ents[j].nkey) + key = ce->key; + else + key = kupsects[i].ents[j].nkey; + if (kupsects[i].ents[j].func) + kupsects[i].ents[j].func( sec, &ce->value ); + putfqval( sec, key, ce->value ); + goto gotkey; + } + printf( "Information: dropping key %s from section [%s]\n", + ce->key, cs->name ); + gotkey: ; + } + goto gotsec; + } + dropsec: + printf( "Information: dropping section [%s]\n", cs->name ); + gotsec: ; + } + } + +#ifdef XDMCP + applydefs( kdmdefs_all, as(kdmdefs_all), path ); +#endif + if (!*(cp = getfqval( "General", "ConfigVersion", "" ))) { /* < 3.1 */ + mod_usebg = 1; + if (is22conf( path )) { + /* work around 2.2.x defaults borkedness */ + applydefs( kdmdefs_eq_22, as(kdmdefs_eq_22), path ); + printf( "Information: current kdmrc is from kde 2.2\n" ); + } else { + applydefs( kdmdefs_ge_30, as(kdmdefs_ge_30), path ); + printf( "Information: current kdmrc is from kde 3.0\n" ); + } +#ifdef XDMCP + /* work around minor <= 3.0.x defaults borkedness */ + applydefs( kdmdefs_le_30, as(kdmdefs_le_30), path ); +#endif + } else { + int ma, mi; + sscanf( cp, "%d.%d", &ma, &mi ); + oldver = (ma << 8) | mi; + printf( "Information: current kdmrc is from kde >= 3.1 (config version %d.%d)\n", ma, mi ); + applydefs( kdmdefs_ge_30, as(kdmdefs_ge_30), path ); + applydefs( kdmdefs_ge_31, as(kdmdefs_ge_31), path ); + } + + return 1; +} + + +typedef struct XResEnt { + const char *xname; + const char *ksec, *kname; + void (*func)( const char *sect, char **value ); +} XResEnt; + +static void +handleXdmVal( const char *dpy, const char *key, char *value, + const XResEnt *ents, int nents ) +{ + const char *kname; + int i; + char knameb[80], sname[80]; + + for (i = 0; i < nents; i++) + if (!strcmp( key, ents[i].xname ) || + (key[0] == toupper( ents[i].xname[0] ) && + !strcmp( key + 1, ents[i].xname + 1 ))) + { + if (ents[i].ksec == (char *)-1) { + ents[i].func (0, &value); + break; + } + sprintf( sname, ents[i].ksec, dpy ); + if (ents[i].kname) + kname = ents[i].kname; + else { + kname = knameb; + sprintf( knameb, "%c%s", + toupper( ents[i].xname[0] ), ents[i].xname + 1 ); + } + if (ents[i].func) + ents[i].func( sname, &value ); + putfqval( sname, kname, value ); + break; + } +} + +static void +P_List( const char *sect ATTR_UNUSED, char **value ) +{ + int is, d, s; + char *st; + + for (st = *value, is = d = s = 0; st[s]; s++) + if (st[s] == ' ' || st[s] == '\t') { + if (!is) + st[d++] = ','; + is = 1; + } else { + st[d++] = st[s]; + is = 0; + } + st[d] = 0; +} + +static void +P_authDir( const char *sect ATTR_UNUSED, char **value ) +{ + int l; + + l = strlen( *value ); + if (l < 4) { + *value = 0; + return; + } + if ((*value)[l-1] == '/') + (*value)[--l] = 0; + if (!strncmp( *value, "/tmp/", 5 ) || + !strncmp( *value, "/var/tmp/", 9 )) + { + printf( "Warning: Resetting inappropriate value %s for AuthDir to default\n", + *value ); + *value = 0; + return; + } + if ((l >= 4 && !strcmp( *value + l - 4, "/tmp" )) || + (l >= 6 && !strcmp( *value + l - 6, "/xauth" )) || + (l >= 8 && !strcmp( *value + l - 8, "/authdir" )) || + (l >= 10 && !strcmp( *value + l - 10, "/authfiles" ))) + return; + ASPrintf( value, "%s/authdir", *value ); +} + +static void +P_openDelay( const char *sect, char **value ) +{ + putfqval( sect, "ServerTimeout", *value ); +} + +static void +P_noPassUsers( const char *sect, char **value ATTR_UNUSED ) +{ + putfqval( sect, "NoPassEnable", "true" ); +} + +static void +P_autoUser( const char *sect, char **value ATTR_UNUSED ) +{ + putfqval( sect, "AutoLoginEnable", "true" ); +} + +#ifdef XDMCP +static void +P_requestPort( const char *sect, char **value ) +{ + if (!strcmp( *value, "0" )) { + *value = 0; + putfqval( sect, "Enable", "false" ); + } else + putfqval( sect, "Enable", "true" ); +} +#endif + +static int kdmrcmode = 0644; + +static void +P_autoPass( const char *sect ATTR_UNUSED, char **value ATTR_UNUSED ) +{ + kdmrcmode = 0600; +} + +CONF_GEN_XMERGE + +static XrmQuark XrmQString, empty = NULLQUARK; + +static Bool +DumpEntry( XrmDatabase *db ATTR_UNUSED, + XrmBindingList bindings, + XrmQuarkList quarks, + XrmRepresentation *type, + XrmValuePtr value, + XPointer data ATTR_UNUSED ) +{ + const char *dpy, *key; + int el, hasu; + char dpybuf[80]; + + if (*type != XrmQString) + return False; + if (*bindings == XrmBindLoosely || + strcmp( XrmQuarkToString (*quarks), "DisplayManager" )) + return False; + bindings++, quarks++; + if (!*quarks) + return False; + if (*bindings != XrmBindLoosely && !quarks[1]) { /* DM.foo */ + key = XrmQuarkToString (*quarks); + handleXdmVal( 0, key, value->addr, globents, as(globents) ); + return False; + } else if (*bindings == XrmBindLoosely && !quarks[1]) { /* DM*bar */ + dpy = "*"; + key = XrmQuarkToString (*quarks); + } else if (*bindings != XrmBindLoosely && quarks[1] && + *bindings != XrmBindLoosely && !quarks[2]) + { /* DM.foo.bar */ + dpy = dpybuf + 4; + strcpy( dpybuf + 4, XrmQuarkToString (*quarks) ); + for (hasu = 0, el = 4; dpybuf[el]; el++) + if (dpybuf[el] == '_') + hasu = 1; + if (!hasu/* && isupper (dpy[0])*/) { + dpy = dpybuf; + memcpy( dpybuf, "*:*_", 4 ); + } else { + for (; --el >= 0; ) + if (dpybuf[el] == '_') { + dpybuf[el] = ':'; + for (; --el >= 4; ) + if (dpybuf[el] == '_') + dpybuf[el] = '.'; + break; + } + } + key = XrmQuarkToString (quarks[1]); + } else + return False; + handleXdmVal( dpy, key, value->addr, dpyents, as(dpyents) ); + return False; +} + +static FDefs xdmdefs[] = { +#ifdef XDMCP +{ "Xdmcp", "Xaccess", "%s/Xaccess", 0 }, +{ "Xdmcp", "Willing", "", 0 }, +#endif +{ "X-*-Core", "Setup", "", 0 }, +{ "X-*-Core", "Startup", "", 0 }, +{ "X-*-Core", "Reset", "", 0 }, +{ "X-*-Core", "Session", "", 0 }, +}; + +static int +mergeXdmCfg( const char *path ) +{ + char *p; + XrmDatabase db; + + ASPrintf( &p, "%s/xdm-config", path ); + if ((db = XrmGetFileDatabase( p ))) { + printf( "Information: reading current xdm config file %s\n", p ); + usedFile( p ); + free( p ); + XrmEnumerateDatabase( db, &empty, &empty, XrmEnumAllLevels, + DumpEntry, (XPointer)0 ); + applydefs( xdmdefs, as(xdmdefs), path ); + mod_usebg = 1; + return 1; + } + free( p ); + return 0; +} + +static void +fwrapprintf( FILE *f, const char *msg, ... ) +{ + char *txt, *ftxt, *line; + va_list ap; + int col, lword, fspace; + + va_start( ap, msg ); + VASPrintf( &txt, msg, ap ); + va_end( ap ); + ftxt = 0; + for (line = txt, col = 0, lword = fspace = -1; line[col]; ) { + if (line[col] == '\n') { + StrCat( &ftxt, "%.*s", ++col, line ); + line += col; + col = 0; + lword = fspace = -1; + continue; + } else if (line[col] == ' ') { + if (lword >= 0) { + fspace = col; + lword = -1; + } + } else { + if (lword < 0) + lword = col; + if (col >= 78 && fspace >= 0) { + StrCat( &ftxt, "%.*s\n", fspace, line ); + line += lword; + col -= lword; + lword = 0; + fspace = -1; + } + } + col++; + } + free( txt ); + fputs( ftxt, f ); + free( ftxt ); +} + + +static const char *oldkdes[] = { + KDE_CONFDIR, + "/opt/kde3/share/config", + "/usr/local/kde3/share/config", + + "/opt/kde/share/config", + "/usr/local/kde/share/config", + "/usr/local/share/config", + "/usr/share/config", + + "/opt/kde2/share/config", + "/usr/local/kde2/share/config", +}; + +static const char *oldxdms[] = { + "/etc/X11/xdm", + XLIBDIR "/xdm", +}; + +int main( int argc, char **argv ) +{ + const char **where; + char *newkdmrc; + FILE *f; + StrList *fp; + Section *cs; + Entry *ce, **cep; + int i, ap, newer, locals, foreigns; + int no_old_xdm = 0, no_old_kde = 0; + struct stat st; + char *nname; + + for (ap = 1; ap < argc; ap++) { + if (!strcmp( argv[ap], "--help" )) { + printf( +"genkdmconf - generate configuration files for kdm\n" +"\n" +"If an older xdm/kdm configuration is found, its config files are \"absorbed\";\n" +"if it lives in the new target directory, its scripts are reused (and possibly\n" +"modified) as well, otherwise the scripts are ignored and default scripts are\n" +"installed.\n" +"\n" +"options:\n" +" --in /path/to/new/kdm-config-dir\n" +" In which directory to put the new configuration. You can use this\n" +" to support a $(DESTDIR), but not to change the final location of\n" +" the installation - the paths inside the files are not affected.\n" +" Default is " KDMCONF ".\n" +" --old-xdm /path/to/old/xdm-dir\n" +" Where to look for the config files of an xdm/older kdm.\n" +" Default is to scan /etc/X11/kdm, $XLIBDIR/kdm, /etc/X11/xdm,\n" +" $XLIBDIR/xdm; there in turn look for kdm-config and xdm-config.\n" +" Note that you possibly need to use --no-old-kde to make this take effect.\n" +" --old-kde /path/to/old/kde-config-dir\n" +" Where to look for the kdmrc of an older kdm.\n" +" Default is to scan " KDE_CONFDIR " and\n" +" {/usr,/usr/local,{/opt,/usr/local}/{kde3,kde,kde2,kde1}}/share/config.\n" +" --no-old\n" +" Don't look at older xdm/kdm configurations, just create default config.\n" +" --no-old-xdm\n" +" Don't look at older xdm configurations.\n" +" --no-old-kde\n" +" Don't look at older kdm configurations.\n" +" --old-scripts\n" +" Directly use all scripts from the older xdm/kdm configuration.\n" +" --no-old-scripts\n" +" Don't use scripts from the older xdm/kdm configuration even if it lives\n" +" in the new target directory.\n" +" --old-confs\n" +" Directly use all ancillary config files from the older xdm/kdm\n" +" configuration. This is usually a bad idea.\n" +" --no-backup\n" +" Overwrite/delete old config files instead of backing them up.\n" +" --no-in-notice\n" +" Don't put the notice about --in being used into the generated README.\n" +); + exit( 0 ); + } + if (!strcmp( argv[ap], "--no-old" )) { + no_old = 1; + continue; + } + if (!strcmp( argv[ap], "--old-scripts" )) { + old_scripts = 1; + continue; + } + if (!strcmp( argv[ap], "--no-old-scripts" )) { + no_old_scripts = 1; + continue; + } + if (!strcmp( argv[ap], "--old-confs" )) { + old_confs = 1; + continue; + } + if (!strcmp( argv[ap], "--no-old-xdm" )) { + no_old_xdm = 1; + continue; + } + if (!strcmp( argv[ap], "--no-old-kde" )) { + no_old_kde = 1; + continue; + } + if (!strcmp( argv[ap], "--no-backup" )) { + no_backup = 1; + continue; + } + if (!strcmp( argv[ap], "--no-in-notice" )) { + no_in_notice = 1; + continue; + } + where = 0; + if (!strcmp( argv[ap], "--in" )) + where = &newdir; + else if (!strcmp( argv[ap], "--old-xdm" )) + where = &oldxdm; + else if (!strcmp( argv[ap], "--old-kde" )) + where = &oldkde; + else if (!strcmp( argv[ap], "--face-src" )) + where = &facesrc; + else { + fprintf( stderr, "Unknown command line option '%s', try --help\n", argv[ap] ); + exit( 1 ); + } + if (ap + 1 == argc || argv[ap + 1][0] == '-') { + fprintf( stderr, "Missing argument to option '%s', try --help\n", argv[ap] ); + exit( 1 ); + } + *where = argv[++ap]; + } + if (memcmp( newdir, KDMCONF, sizeof(KDMCONF) )) + use_destdir = 1; + + if (!mkdirp( newdir, 0755, "target", 1 )) + exit( 1 ); + + mkdefconf(); + newer = 0; + if (no_old) { + DIR *dir; + if ((dir = opendir( newdir ))) { + struct dirent *ent; + char bn[PATH_MAX]; + while ((ent = readdir( dir ))) { + int l; + if (!strcmp( ent->d_name, "." ) || !strcmp( ent->d_name, ".." )) + continue; + l = sprintf( bn, "%s/%s", newdir, ent->d_name ); /* cannot overflow (kernel would not allow the creation of a longer path) */ + if (!stat( bn, &st ) && !S_ISREG( st.st_mode )) + continue; + if (no_backup || !memcmp( bn + l - 4, ".bak", 5 )) + unlink( bn ); + else + displace( bn ); + } + closedir( dir ); + } + } else { + if (oldkde) { + if (!(newer = mergeKdmRcNewer( oldkde )) && !mergeKdmRcOld( oldkde )) + fprintf( stderr, + "Cannot read old kdmrc at specified location\n" ); + } else if (!no_old_kde) { + for (i = 0; i < as(oldkdes); i++) { + if ((newer = mergeKdmRcNewer( oldkdes[i] )) || + mergeKdmRcOld( oldkdes[i] )) { + oldkde = oldkdes[i]; + break; + } + } + } + if (!newer && !no_old_xdm) { + XrmInitialize(); + XrmQString = XrmPermStringToQuark( "String" ); + if (oldxdm) { + if (!mergeXdmCfg( oldxdm )) + fprintf( stderr, + "Cannot read old kdm-config/xdm-config at specified location\n" ); + } else + for (i = 0; i < as(oldxdms); i++) + if (mergeXdmCfg( oldxdms[i] )) { + oldxdm = oldxdms[i]; + break; + } + } else + oldxdm = 0; + } + if (no_old_scripts) + goto no_old_s; + if (!old_scripts) { + locals = foreigns = 0; + for (cs = config; cs; cs = cs->next) + if (!strcmp( cs->spec->name, "-Core" )) { + for (ce = cs->ents; ce; ce = ce->next) + if (ce->active && + (!strcmp( ce->spec->key, "Setup" ) || + !strcmp( ce->spec->key, "Startup" ) || + !strcmp( ce->spec->key, "Reset" ))) + { + if (inNewDir( ce->value )) + locals = 1; + else + foreigns = 1; + } + } + if (foreigns) { + if (locals) { + fprintf( stderr, + "Warning: both local and foreign scripts referenced. " + "Won't touch any.\n" ); + mixed_scripts = 1; + } else { + no_old_s: + for (cs = config; cs; cs = cs->next) { + if (!strcmp( cs->spec->name, "Xdmcp" )) { + for (ce = cs->ents; ce; ce = ce->next) + if (!strcmp( ce->spec->key, "Willing" )) + ce->active = ce->written = 0; + } else if (!strcmp( cs->spec->name, "-Core" )) { + for (cep = &cs->ents; (ce = *cep); ) { + if (ce->active && + (!strcmp( ce->spec->key, "Setup" ) || + !strcmp( ce->spec->key, "Startup" ) || + !strcmp( ce->spec->key, "Reset" ) || + !strcmp( ce->spec->key, "Session" ))) + { + if (!memcmp( cs->name, "X-*-", 4 )) + ce->active = ce->written = 0; + else { + *cep = ce->next; + free( ce ); + continue; + } + } + cep = &ce->next; + } + } + } + } + } + } +#ifdef __linux__ + if (!stat( "/etc/debian_version", &st )) { /* debian */ + defminuid = "1000"; + defmaxuid = "29999"; + } else if (!stat( "/usr/portage", &st )) { /* gentoo */ + defminuid = "1000"; + defmaxuid = "65000"; + } else if (!stat( "/etc/mandrake-release", &st )) { /* mandrake - check before redhat! */ + defminuid = "500"; + defmaxuid = "65000"; + } else if (!stat( "/etc/redhat-release", &st )) { /* redhat */ + defminuid = "100"; + defmaxuid = "65000"; + } else /* if (!stat( "/etc/SuSE-release", &st )) */ { /* suse */ + defminuid = "500"; + defmaxuid = "65000"; + } +#else + defminuid = "1000"; + defmaxuid = "65000"; +#endif + for (i = 0; i < CONF_MAX_PRIO; i++) + for (cs = config; cs; cs = cs->next) + for (ce = cs->ents; ce; ce = ce->next) + if (ce->spec->func && i == ce->spec->prio) + ce->spec->func( ce, cs ); + ASPrintf( &newkdmrc, "%s/kdmrc", newdir ); + f = Create( newkdmrc, kdmrcmode ); + wrconf( f ); + fclose( f ); + + ASPrintf( &nname, "%s/README", newdir ); + f = Create( nname, 0644 ); + fprintf( f, +"This automatically generated configuration consists of the following files:\n" ); + fprintf( f, "- " KDMCONF "/kdmrc\n" ); + for (fp = aflist; fp; fp = fp->next) + fprintf( f, "- %s\n", fp->str ); + if (use_destdir && !no_in_notice) + fwrapprintf( f, +"All files destined for " KDMCONF " were actually saved in %s; " +"this config won't be workable until moved in place.\n", newdir ); + if (uflist || eflist || cflist || lflist) { + fprintf( f, +"\n" +"This config was derived from existing files. As the used algorithms are\n" +"pretty dumb, it may be broken.\n" ); + if (uflist) { + fprintf( f, +"Information from these files was extracted:\n" ); + for (fp = uflist; fp; fp = fp->next) + fprintf( f, "- %s\n", fp->str ); + } + if (lflist) { + fprintf( f, +"These files were directly incorporated:\n" ); + for (fp = lflist; fp; fp = fp->next) + fprintf( f, "- %s\n", fp->str ); + } + if (cflist) { + fprintf( f, +"These files were copied verbatim:\n" ); + for (fp = cflist; fp; fp = fp->next) + fprintf( f, "- %s\n", fp->str ); + } + if (eflist) { + fprintf( f, +"These files were copied with modifications:\n" ); + for (fp = eflist; fp; fp = fp->next) + fprintf( f, "- %s\n", fp->str ); + } + if (!no_backup && !use_destdir) + fprintf( f, +"Old files that would have been overwritten were renamed to <oldname>.bak.\n" ); + } + fprintf( f, +"\nTry 'genkdmconf --help' if you want to generate another configuration.\n" +"\nYou may delete this README.\n" ); + fclose( f ); + + return 0; +} diff --git a/kdm/kfrontend/kchooser.cpp b/kdm/kfrontend/kchooser.cpp new file mode 100644 index 000000000..6d0b20350 --- /dev/null +++ b/kdm/kfrontend/kchooser.cpp @@ -0,0 +1,227 @@ +/* + +chooser widget for KDM + +Copyright (C) 2002-2003 Oswald Buddenhagen <[email protected]> +based on the chooser (C) 1999 by Harald Hoyer <[email protected]> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include <config.h> + +#ifdef XDMCP + +#include "kchooser.h" +#include "kconsole.h" +#include "kdmconfig.h" +#include "kdm_greet.h" + +#include <klocale.h> + +#include <qlayout.h> +#include <qlabel.h> +#include <qpushbutton.h> +#include <qpopupmenu.h> +#include <qsocketnotifier.h> +#include <qlistview.h> +#include <qlineedit.h> + +#include <stdlib.h> // for free() + +class ChooserListViewItem : public QListViewItem { + public: + ChooserListViewItem( QListView* parent, int _id, const QString& nam, const QString& sts ) + : QListViewItem( parent, nam, sts ) { id = _id; }; + + int id; +}; + + +ChooserDlg::ChooserDlg() + : inherited() +{ + completeMenu( LOGIN_REMOTE_ONLY, ex_greet, i18n("&Local Login"), ALT+Key_L ); + + QBoxLayout *vbox = new QVBoxLayout( this, 10, 10 ); + + QLabel *title = new QLabel( i18n("XDMCP Host Menu"), this ); + title->setAlignment( AlignCenter ); + vbox->addWidget( title ); + + host_view = new QListView( this, "hosts" ); + host_view->addColumn( i18n("Hostname") ); + host_view->setColumnWidth( 0, fontMetrics().width( "login.crap.net" ) ); + host_view->addColumn( i18n("Status") ); + host_view->setMinimumWidth( fontMetrics().width( "login.crap.com Display not authorized to connect this server" ) ); + host_view->setResizeMode( QListView::LastColumn ); + host_view->setAllColumnsShowFocus( true ); + vbox->addWidget( host_view ); + + iline = new QLineEdit( this ); + iline->setEnabled( TRUE ); + QLabel *itxt = new QLabel( iline, i18n("Hos&t:"), this ); + QPushButton *addButton = new QPushButton( i18n("A&dd"), this ); + connect( addButton, SIGNAL(clicked()), SLOT(addHostname()) ); + QBoxLayout *hibox = new QHBoxLayout( vbox, 10 ); + hibox->addWidget( itxt ); + hibox->addWidget( iline ); + hibox->addWidget( addButton ); + + // Buttons + QPushButton *acceptButton = new QPushButton( i18n("&Accept"), this ); + acceptButton->setDefault( true ); + QPushButton *pingButton = new QPushButton( i18n("&Refresh"), this ); + + QBoxLayout *hbox = new QHBoxLayout( vbox, 20 ); + hbox->addWidget( acceptButton ); + hbox->addWidget( pingButton ); + hbox->addStretch( 1 ); + + if (optMenu) { + QPushButton *menuButton = new QPushButton( i18n("&Menu"), this ); + menuButton->setPopup( optMenu ); + hbox->addWidget( menuButton ); + hbox->addStretch( 1 ); + } + +// QPushButton *helpButton = new QPushButton( i18n("&Help"), this ); +// hbox->addWidget( helpButton ); + +#ifdef WITH_KDM_XCONSOLE + if (consoleView) + vbox->addWidget( consoleView ); +#endif + + sn = new QSocketNotifier( rfd, QSocketNotifier::Read, this ); + connect( sn, SIGNAL(activated( int )), SLOT(slotReadPipe()) ); + + connect( pingButton, SIGNAL(clicked()), SLOT(pingHosts()) ); + connect( acceptButton, SIGNAL(clicked()), SLOT(accept()) ); +// connect( helpButton, SIGNAL(clicked()), SLOT(slotHelp()) ); + connect( host_view, SIGNAL(doubleClicked(QListViewItem *)), SLOT(accept()) ); + + adjustGeometry(); +} + +/* +void ChooserDlg::slotHelp() +{ + KMessageBox::information(0, + i18n("Choose a host, you want to work on,\n" + "in the list or add one.\n\n" + "After this box, you must press cancel\n" + "in the Host Menu to enter a host. :(")); + iline->setFocus(); +} +*/ + +void ChooserDlg::addHostname() +{ + if (!iline->text().isEmpty()) { + GSendInt( G_Ch_RegisterHost ); + GSendStr( iline->text().latin1() ); + iline->clear(); + } +} + +void ChooserDlg::pingHosts() +{ + GSendInt( G_Ch_Refresh ); +} + +void ChooserDlg::accept() +{ + if (focusWidget() == iline) { + if (!iline->text().isEmpty()) { + GSendInt( G_Ch_DirectChoice ); + GSendStr( iline->text().latin1() ); + iline->clear(); + } + return; + } else /*if (focusWidget() == host_view)*/ { + QListViewItem *item = host_view->currentItem(); + if (item) { + GSendInt( G_Ready ); + GSendInt( ((ChooserListViewItem *)item)->id ); + ::exit( EX_NORMAL ); + } + } +} + +void ChooserDlg::reject() +{ +} + +QString ChooserDlg::recvStr() +{ + char *arr = GRecvStr(); + if (arr) { + QString str = QString::fromLatin1( arr ); + free( arr ); + return str; + } else + return i18n("<unknown>"); +} + +QListViewItem *ChooserDlg::findItem( int id ) +{ + QListViewItem *itm; + for (QListViewItemIterator it( host_view ); (itm = it.current()); ++it) + if (((ChooserListViewItem *)itm)->id == id) + return itm; + return 0; +} + +void ChooserDlg::slotReadPipe() +{ + int id; + QString nam, sts; + + int cmd = GRecvInt(); + switch (cmd) { + case G_Ch_AddHost: + case G_Ch_ChangeHost: + id = GRecvInt(); + nam = recvStr(); + sts = recvStr(); + GRecvInt(); /* swallow willing for now */ + if (cmd == G_Ch_AddHost) + host_view->insertItem( + new ChooserListViewItem( host_view, id, nam, sts ) ); + else { + QListViewItem *itm = findItem( id ); + itm->setText( 0, nam ); + itm->setText( 1, sts ); + } + break; + case G_Ch_RemoveHost: + delete findItem( GRecvInt() ); + break; + case G_Ch_BadHost: + KFMsgBox::box( this, QMessageBox::Warning, i18n("Unknown host %1").arg( recvStr() ) ); + break; + case G_Ch_Exit: + done( ex_exit ); + break; + default: /* XXX huuh ...? */ + break; + } +} + +#include "kchooser.moc" + +#endif diff --git a/kdm/kfrontend/kchooser.h b/kdm/kfrontend/kchooser.h new file mode 100644 index 000000000..93ede2694 --- /dev/null +++ b/kdm/kfrontend/kchooser.h @@ -0,0 +1,59 @@ +/* + +chooser widget for KDM + +Copyright (C) 2002-2003 Oswald Buddenhagen <[email protected]> +based on the chooser (C) 1999 by Harald Hoyer <[email protected]> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#ifndef KCHOOSER_H +#define KCHOOSER_H + +#include "kgdialog.h" + +class QSocketNotifier; +class QPopupMenu; +class QLineEdit; +class QListView; +class QListViewItem; + +class ChooserDlg : public KGDialog { + Q_OBJECT + typedef KGDialog inherited; + + public: + ChooserDlg(); + + public slots: + void slotReadPipe(); + void addHostname(); +// void slotHelp(); + void pingHosts(); + void accept(); + void reject(); + + private: + QString recvStr(); + QListViewItem *findItem( int id ); + + QListView *host_view; + QLineEdit *iline; + QSocketNotifier *sn; +}; + +#endif /* KCHOOSER_H */ diff --git a/kdm/kfrontend/kconsole.cpp b/kdm/kfrontend/kconsole.cpp new file mode 100644 index 000000000..b101712da --- /dev/null +++ b/kdm/kfrontend/kconsole.cpp @@ -0,0 +1,183 @@ +/* + +xconsole widget for KDM + +Copyright (C) 2002-2003 Oswald Buddenhagen <[email protected]> + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include <config.h> + +#ifdef WITH_KDM_XCONSOLE + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/ioctl.h> + +#ifdef HAVE_TERMIOS_H +/* for HP-UX (some versions) the extern C is needed, and for other + platforms it doesn't hurt */ +extern "C" { +#include <termios.h> +} +#endif +#if !defined(__osf__) +#ifdef HAVE_TERMIO_H +/* needed at least on AIX */ +#include <termio.h> +#endif +#endif + +#if defined (_HPUX_SOURCE) +#define _TERMIOS_INCLUDED +#include <bsdtty.h> +#endif + + +#include "kconsole.h" +#include "kdmconfig.h" +#include "kdm_greet.h" + +#include <klocale.h> +#include <kpty.h> + +#include <qsocketnotifier.h> + +KConsole::KConsole( QWidget *_parent ) + : inherited( _parent ) + , pty( 0 ) + , notifier( 0 ) + , fd( -1 ) +{ + setReadOnly( true ); + setWordWrap( NoWrap ); + setTextFormat( PlainText ); + + if (!OpenConsole()) + append( i18n("Cannot open console") ); +} + +KConsole::~KConsole() +{ + CloseConsole(); +} + +int +KConsole::OpenConsole() +{ +#ifdef TIOCCONS + static const char on = 1; +#endif + + if (*_logSource) { + if ((fd = open( _logSource, O_RDONLY | O_NONBLOCK )) >= 0) + goto gotcon; + LogError( "Cannot open log source %s, " + "falling back to /dev/console.\n", _logSource ); + } + + pty = new KPty; + if (!pty->open()) { + delete pty; + pty = 0; + return 0; + } + +#ifdef TIOCCONS + if (ioctl( pty->slaveFd(), TIOCCONS, &on ) < 0) { + perror( "ioctl TIOCCONS" ); + delete pty; + pty = 0; + return 0; + } +#else + int consfd; + if ((consfd = open( "/dev/console", O_RDONLY )) < 0) { + perror( "opening /dev/console" ); + delete pty; + pty = 0; + return 0; + } + if (ioctl( consfd, SRIOCSREDIR, slave_fd ) < 0) { + perror( "ioctl SRIOCSREDIR" ); + ::close( consfd ); + delete pty; + pty = 0; + return 0; + } + ::close( consfd ); +#endif + fd = pty->masterFd(); + + gotcon: + notifier = new QSocketNotifier( fd, QSocketNotifier::Read, this ); + connect( notifier, SIGNAL(activated( int )), SLOT(slotData()) ); + return 1; +} + +void +KConsole::CloseConsole() +{ + delete notifier; + notifier = 0; + if (pty) { + delete pty; + pty = 0; + } else + ::close( fd ); + fd = -1; +} + +void +KConsole::slotData() +{ + int n; + char buffer[1024]; + + if ((n = read( fd, buffer, sizeof(buffer) )) <= 0) { + CloseConsole(); + if (!n) + if (!OpenConsole()) + append( i18n("\n*** Cannot open console log source ***") ); + } else { + bool as = !verticalScrollBar()->isVisible() || + (verticalScrollBar()->value() == + verticalScrollBar()->maxValue()); + QString str( QString::fromLocal8Bit( buffer, n ).remove( '\r' ) ); + int pos, opos; + for (opos = 0; (pos = str.find( '\n', opos )) >= 0; opos = pos + 1) { + if (paragraphs() == 100) + removeParagraph( 0 ); + if (!leftover.isEmpty()) { + append( leftover + str.mid( opos, pos - opos ) ); + leftover = QString::null; + } else + append( str.mid( opos, pos - opos ) ); + } + leftover += str.mid( opos ); + if (as) + scrollToBottom(); + } +} + +#include "kconsole.moc" + +#endif diff --git a/kdm/kfrontend/kconsole.h b/kdm/kfrontend/kconsole.h new file mode 100644 index 000000000..8a3515173 --- /dev/null +++ b/kdm/kfrontend/kconsole.h @@ -0,0 +1,53 @@ +/* + +xconsole widget for KDM + +Copyright (C) 2002-2003 Oswald Buddenhagen <[email protected]> + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#ifndef KCONSOLE_H +#define KCONSOLE_H + +#include <qtextedit.h> + +class QSocketNotifier; +class KPty; + +class KConsole : public QTextEdit { + Q_OBJECT + typedef QTextEdit inherited; + + public: + KConsole( QWidget *_parent = 0 ); + ~KConsole(); + + private slots: + void slotData(); + + private: + int OpenConsole(); + void CloseConsole(); + + KPty *pty; + QSocketNotifier *notifier; + QString leftover; + int fd; +}; + +#endif // KCONSOLE_H diff --git a/kdm/kfrontend/kdm_config.c b/kdm/kfrontend/kdm_config.c new file mode 100644 index 000000000..5d188e33d --- /dev/null +++ b/kdm/kfrontend/kdm_config.c @@ -0,0 +1,1470 @@ +/* + +Read options from kdmrc + +Copyright (C) 2001-2005 Oswald Buddenhagen <[email protected]> + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include <config.h> + +#include <stdio.h> +#include <unistd.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#include <netinet/in.h> +#include <grp.h> +#ifdef _POSIX_PRIORITY_SCHEDULING +# include <sched.h> +#endif + +#include <X11/X.h> +#ifdef FamilyInternet6 +# define IPv6 +#endif + +#include <greet.h> +#include <config.ci> + +/* + * Section/Entry definition structs + */ + +typedef struct Ent { + const char *name; + int id; + void *ptr; + const char *def; +} Ent; + +typedef struct Sect { + const char *name; + Ent *ents; + int numents; +} Sect; + +/* + * Parsed ini file structs + */ + +typedef struct Entry { + struct Entry *next; + const char *val; + Ent *ent; + int vallen; + int line; +} Entry; + +typedef struct Section { + struct Section *next; + Entry *entries; + Sect *sect; + const char *name, *dname, *dhost, *dnum, *dclass; + int nlen, dlen, dhostl, dnuml, dclassl; +} Section; + + +/* + * Split up display-name/-class for fast comparison + */ +typedef struct DSpec { + const char *dhost, *dnum, *dclass; + int dhostl, dnuml, dclassl; +} DSpec; + + +/* + * Config value storage structures + */ + +typedef struct Value { + const char *ptr; + int len; +} Value; + +typedef struct Val { + Value val; + int id; +} Val; + +typedef struct ValArr { + Val *ents; + int nents, esiz, nchars, nptrs; +} ValArr; + + +static void *Malloc( size_t size ); +static void *Realloc( void *ptr, size_t size ); + +#define PRINT_QUOTES +#define LOG_NAME "kdm_config" +#define LOG_DEBUG_MASK DEBUG_CONFIG +#define LOG_PANIC_EXIT 1 +#define STATIC static +#include <printf.c> + + +static void * +Malloc( size_t size ) +{ + void *ret; + + if (!(ret = malloc( size ))) + LogOutOfMem(); + return ret; +} + +static void * +Realloc( void *ptr, size_t size ) +{ + void *ret; + + if (!(ret = realloc( ptr, size )) && size) + LogOutOfMem(); + return ret; +} + + +static void +MkDSpec( DSpec *spec, const char *dname, const char *dclass ) +{ + spec->dhost = dname; + for (spec->dhostl = 0; dname[spec->dhostl] != ':'; spec->dhostl++); + spec->dnum = dname + spec->dhostl + 1; + spec->dnuml = strlen( spec->dnum ); + spec->dclass = dclass; + spec->dclassl = strlen( dclass ); +} + + +static int rfd, wfd; + +static int +Reader( void *buf, int count ) +{ + int ret, rlen; + + for (rlen = 0; rlen < count; ) { + dord: + ret = read( rfd, (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; +} + +static void +GRead( void *buf, int count ) +{ + if (Reader( buf, count ) != count) + LogPanic( "Can't read from core\n" ); +} + +static void +GWrite( const void *buf, int count ) +{ + if (write( wfd, buf, count ) != count) + LogPanic( "Can't write to core\n" ); +#ifdef _POSIX_PRIORITY_SCHEDULING + if ((debugLevel & DEBUG_HLPCON)) + sched_yield(); +#endif +} + +static void +GSendInt( int val ) +{ + GWrite( &val, sizeof(val) ); +} + +static void +GSendStr( const char *buf ) +{ + if (buf) { + int len = strlen( buf ) + 1; + GWrite( &len, sizeof(len) ); + GWrite( buf, len ); + } else + GWrite( &buf, sizeof(int)); +} + +static void +GSendNStr( const char *buf, int len ) +{ + int tlen = len + 1; + GWrite( &tlen, sizeof(tlen) ); + GWrite( buf, len ); + GWrite( "", 1 ); +} + +#ifdef XDMCP +static void +GSendArr( int len, const char *data ) +{ + GWrite( &len, sizeof(len) ); + GWrite( data, len ); +} +#endif + +static int +GRecvCmd( int *val ) +{ + if (Reader( val, sizeof(*val) ) != sizeof(*val)) + return 0; + return 1; +} + +static int +GRecvInt() +{ + int val; + + GRead( &val, sizeof(val) ); + return val; +} + +static char * +GRecvStr() +{ + int len; + char *buf; + + len = GRecvInt(); + if (!len) + return 0; + if (!(buf = malloc( len ))) + LogPanic( "No memory for read buffer" ); + GRead( buf, len ); + return buf; +} + + +/* #define WANT_CLOSE 1 */ + +typedef struct File { + char *buf, *eof, *cur; +#if defined(HAVE_MMAP) && defined(WANT_CLOSE) + int ismapped; +#endif +} File; + +static int +readFile( File *file, const char *fn, const char *what ) +{ + int fd; + off_t flen; + + if ((fd = open( fn, O_RDONLY )) < 0) { + LogInfo( "Cannot open %s file %s\n", what, fn ); + return 0; + } + + flen = lseek( fd, 0, SEEK_END ); +#ifdef HAVE_MMAP +# ifdef WANT_CLOSE + file->ismapped = 0; +# endif + file->buf = mmap( 0, flen + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0 ); +# ifdef WANT_CLOSE + if (file->buf) + file->ismapped = 1; + else +# else + if (!file->buf) +# endif +#endif + { + if (!(file->buf = Malloc( flen + 1 ))) { + close( fd ); + return 0; + } + lseek( fd, 0, SEEK_SET ); + if (read( fd, file->buf, flen ) != flen) { + free( file->buf ); + LogError( "Cannot read %s file %s\n", what, fn ); + close( fd ); + return 0; + } + } + file->eof = (file->cur = file->buf) + flen; + close( fd ); + return 1; +} + +#ifdef WANT_CLOSE +static void +freeBuf( File *file ) +{ +# ifdef HAVE_MMAP + if (file->ismapped) + munmap( file->buf, file->eof - file->buf + 1 ); + else +# endif + free( file->buf ); +} +#endif + +CONF_READ_VARS + +#define C_MTYPE_MASK 0x30000000 +# define C_PATH 0x10000000 /* C_TYPE_STR is a path spec */ +# define C_BOOL 0x10000000 /* C_TYPE_INT is a boolean */ +# define C_ENUM 0x20000000 /* C_TYPE_INT is an enum (option) */ +# define C_GRP 0x30000000 /* C_TYPE_INT is a group spec */ +#define C_INTERNAL 0x40000000 /* don't expose to core */ +#define C_CONFIG 0x80000000 /* process only for finding deps */ + +#ifdef XDMCP +static int +PrequestPort( Value *retval ) +{ + if (!VxdmcpEnable.ptr) { + retval->ptr = (char *)0; + return 1; + } + return 0; +} +#endif + +static Value + emptyStr = { "", 1 }, + nullValue = { 0, 0 }, + emptyArgv = { (char *)&nullValue, 0 }; + +static int +PnoPassUsers( Value *retval ) +{ + if (!VnoPassEnable.ptr) { + *retval = emptyArgv; + return 1; + } + return 0; +} + +static int +PautoLoginX( Value *retval ) +{ + if (!VautoLoginEnable.ptr) { + *retval = emptyStr; + return 1; + } + return 0; +} + +CONF_READ_ENTRIES + +static const char *kdmrc = KDMCONF "/kdmrc"; + +static Section *rootsec; + +static void +ReadConf() +{ + const char *nstr, *dstr, *cstr, *dhost, *dnum, *dclass; + char *s, *e, *st, *en, *ek, *sl, *pt; + Section *cursec; + Entry *curent; + Ent *ce; + int nlen, dlen, clen, dhostl, dnuml, dclassl; + int i, line, sectmoan, restl; + File file; + static int confread; + + if (confread) + return; + confread = 1; + + Debug( "reading config %s ...\n", kdmrc ); + if (!readFile( &file, kdmrc, "master configuration" )) + return; + + for (s = file.buf, line = 0, cursec = 0, sectmoan = 1; s < file.eof; s++) { + line++; + + while ((s < file.eof) && isspace( *s ) && (*s != '\n')) + s++; + + if ((s < file.eof) && ((*s == '\n') || (*s == '#'))) { + sktoeol: + while ((s < file.eof) && (*s != '\n')) + s++; + continue; + } + sl = s; + + if (*s == '[') { + sectmoan = 0; + while ((s < file.eof) && (*s != '\n')) + s++; + e = s - 1; + while ((e > sl) && isspace( *e )) + e--; + if (*e != ']') { + cursec = 0; + LogError( "Invalid section header at %s:%d\n", kdmrc, line ); + continue; + } + nstr = sl + 1; + nlen = e - nstr; + for (cursec = rootsec; cursec; cursec = cursec->next) + if (nlen == cursec->nlen && + !memcmp( nstr, cursec->name, nlen )) + { + LogInfo( "Multiple occurrences of section [%.*s] in %s. " + "Consider merging them.\n", nlen, nstr, kdmrc ); + goto secfnd; + } + if (nstr[0] == 'X' && nstr[1] == '-') { + cstr = nstr + nlen; + clen = 0; + while (++clen, *--cstr != '-'); + if (cstr == nstr + 1) + goto illsec; + dstr = nstr + 2; + dlen = nlen - clen - 2; + dhost = dstr; + dhostl = 0; + for (restl = dlen; restl; restl--) { + if (dhost[dhostl] == ':') { + dnum = dhost + dhostl + 1; + dnuml = 0; + for (restl--; restl; restl--) { + if (dnum[dnuml] == '_') { + dclass = dnum + dnuml + 1; + dclassl = restl; + goto gotall; + } + dnuml++; + } + goto gotnum; + } + dhostl++; + } + dnum = "*"; + dnuml = 1; + gotnum: + dclass = "*"; + dclassl = 1; + gotall: ; + } else { + if (nstr[0] == '-') + goto illsec; + dstr = 0; + dlen = 0; + dhost = 0; + dhostl = 0; + dnum = 0; + dnuml = 0; + dclass = 0; + dclassl = 0; + cstr = nstr; + clen = nlen; + } + for (i = 0; i < as(allSects); i++) + if ((int)strlen( allSects[i]->name ) == clen && + !memcmp( allSects[i]->name, cstr, clen )) + goto newsec; + illsec: + cursec = 0; + LogError( "Unrecognized section name [%.*s] at %s:%d\n", + nlen, nstr, kdmrc, line ); + continue; + newsec: + if (!(cursec = Malloc( sizeof(*cursec) ))) + return; + cursec->name = nstr; + cursec->nlen = nlen; + cursec->dname = dstr; + cursec->dlen = dlen; + cursec->dhost = dhost; + cursec->dhostl = dhostl; + cursec->dnum = dnum; + cursec->dnuml = dnuml; + cursec->dclass = dclass; + cursec->dclassl = dclassl; + cursec->sect = allSects[i]; + cursec->entries = 0; + cursec->next = rootsec; + rootsec = cursec; + /*Debug( "now in section [%.*s], dpy '%.*s', core '%.*s'\n", + nlen, nstr, dlen, dstr, clen, cstr );*/ + secfnd: + continue; + } + + if (!cursec) { + if (sectmoan) { + sectmoan = 0; + LogError( "Entry outside any section at %s:%d", kdmrc, line ); + } + goto sktoeol; + } + + for (; (s < file.eof) && (*s != '\n'); s++) + if (*s == '=') + goto haveeq; + LogError( "Invalid entry (missing '=') at %s:%d\n", kdmrc, line ); + continue; + + haveeq: + for (ek = s - 1; ; ek--) { + if (ek < sl) { + LogError( "Invalid entry (empty key) at %s:%d\n", kdmrc, line ); + goto sktoeol; + } + if (!isspace( *ek )) + break; + } + + s++; + while ((s < file.eof) && isspace( *s ) && (*s != '\n')) + s++; + for (pt = st = en = s; s < file.eof && *s != '\n'; s++) { + if (*s == '\\') { + s++; + if (s >= file.eof || *s == '\n') { + LogError( "Trailing backslash at %s:%d\n", kdmrc, line ); + break; + } + switch (*s) { + case 's': *pt++ = ' '; break; + case 't': *pt++ = '\t'; break; + case 'n': *pt++ = '\n'; break; + case 'r': *pt++ = '\r'; break; + case '\\': *pt++ = '\\'; break; + default: *pt++ = '\\'; *pt++ = *s; break; + } + en = pt; + } else { + *pt++ = *s; + if (*s != ' ' && *s != '\t') + en = pt; + } + } + + nstr = sl; + nlen = ek - sl + 1; + /*Debug( "read entry '%.*s'='%.*s'\n", nlen, nstr, en - st, st );*/ + for (i = 0; i < cursec->sect->numents; i++) { + ce = cursec->sect->ents + i; + if ((int)strlen( ce->name ) == nlen && + !memcmp( ce->name, nstr, nlen )) + goto keyok; + } + LogError( "Unrecognized key '%.*s' in section [%.*s] at %s:%d\n", + nlen, nstr, cursec->nlen, cursec->name, kdmrc, line ); + continue; + keyok: + for (curent = cursec->entries; curent; curent = curent->next) + if (ce == curent->ent) { + LogError( "Multiple occurrences of key '%s' in section [%.*s]" + " of %s\n", + ce->name, cursec->nlen, cursec->name, kdmrc ); + goto keyfnd; + } + if (!(curent = Malloc( sizeof(*curent) ))) + return; + curent->ent = ce; + curent->line = line; + curent->val = st; + curent->vallen = en - st; + curent->next = cursec->entries; + cursec->entries = curent; + keyfnd: + continue; + } +} + +static Entry * +FindGEnt( int id ) +{ + Section *cursec; + Entry *curent; + + for (cursec = rootsec; cursec; cursec = cursec->next) + if (!cursec->dname) + for (curent = cursec->entries; curent; curent = curent->next) + if (curent->ent->id == id) { + Debug( "line %d: %s = %'.*s\n", + curent->line, curent->ent->name, + curent->vallen, curent->val ); + return curent; + } + return 0; +} + +/* Display name match scoring: + * - class (any/exact) -> 0/1 + * - number (any/exact) -> 0/2 + * - host (any/nonempty/trail/exact) -> 0/4/8/12 + */ +static Entry * +FindDEnt( int id, DSpec *dspec ) +{ + Section *cursec, *bestsec; + Entry *curent, *bestent; + int score, bestscore; + + bestscore = -1, bestent = 0; + for (cursec = rootsec; cursec; cursec = cursec->next) + if (cursec->dname) { + score = 0; + if (cursec->dclassl != 1 || cursec->dclass[0] != '*') { + if (cursec->dclassl == dspec->dclassl && + !memcmp( cursec->dclass, dspec->dclass, dspec->dclassl )) + score = 1; + else + continue; + } + if (cursec->dnuml != 1 || cursec->dnum[0] != '*') { + if (cursec->dnuml == dspec->dnuml && + !memcmp( cursec->dnum, dspec->dnum, dspec->dnuml )) + score += 2; + else + continue; + } + if (cursec->dhostl != 1 || cursec->dhost[0] != '*') { + if (cursec->dhostl == 1 && cursec->dhost[0] == '+') { + if (dspec->dhostl) + score += 4; + else + continue; + } else if (cursec->dhost[0] == '.') { + if (cursec->dhostl < dspec->dhostl && + !memcmp( cursec->dhost, + dspec->dhost + dspec->dhostl - cursec->dhostl, + cursec->dhostl )) + score += 8; + else + continue; + } else { + if (cursec->dhostl == dspec->dhostl && + !memcmp( cursec->dhost, dspec->dhost, dspec->dhostl )) + score += 12; + else + continue; + } + } + if (score > bestscore) { + for (curent = cursec->entries; curent; curent = curent->next) + if (curent->ent->id == id) { + bestent = curent; + bestsec = cursec; + bestscore = score; + break; + } + } + } + if (bestent) + Debug( "line %d: %.*s:%.*s_%.*s/%s = %'.*s\n", bestent->line, + bestsec->dhostl, bestsec->dhost, + bestsec->dnuml, bestsec->dnum, + bestsec->dclassl, bestsec->dclass, + bestent->ent->name, bestent->vallen, bestent->val ); + return bestent; +} + +static const char * +CvtValue( Ent *et, Value *retval, int vallen, const char *val, char **eopts ) +{ + Value *ents; + int i, b, e, tlen, nents, esiz; + char buf[80]; + + switch (et->id & C_TYPE_MASK) { + case C_TYPE_INT: + for (i = 0; i < vallen && i < (int)sizeof(buf) - 1; i++) + buf[i] = tolower( val[i] ); + buf[i] = 0; + if ((et->id & C_MTYPE_MASK) == C_BOOL) { + if (!strcmp( buf, "true" ) || + !strcmp( buf, "on" ) || + !strcmp( buf, "yes" ) || + !strcmp( buf, "1" )) + retval->ptr = (char *)1; + else if (!strcmp( buf, "false" ) || + !strcmp( buf, "off" ) || + !strcmp( buf, "no" ) || + !strcmp( buf, "0" )) + retval->ptr = (char *)0; + else + return "boolean"; + return 0; + } else if ((et->id & C_MTYPE_MASK) == C_ENUM) { + for (i = 0; eopts[i]; i++) + if (!memcmp( eopts[i], val, vallen ) && !eopts[i][vallen]) { + retval->ptr = (char *)i; + return 0; + } + return "option"; + } else if ((et->id & C_MTYPE_MASK) == C_GRP) { + struct group *ge; + if ((ge = getgrnam( buf ))) { + retval->ptr = (char *)ge->gr_gid; + return 0; + } + } + retval->ptr = 0; + if (sscanf( buf, "%li", &retval->ptr ) != 1) + return "integer"; + return 0; + case C_TYPE_STR: + retval->ptr = val; + retval->len = vallen + 1; + if ((et->id & C_MTYPE_MASK) == C_PATH) + if (vallen && val[vallen-1] == '/') + retval->len--; + return 0; + case C_TYPE_ARGV: + if (!(ents = Malloc( sizeof(Value) * (esiz = 10) ))) + return 0; + for (nents = 0, tlen = 0, i = 0; ; i++) { + for (; i < vallen && isspace( val[i] ); i++); + for (b = i; i < vallen && val[i] != ','; i++); + if (b == i) + break; + for (e = i; e > b && isspace( val[e - 1] ); e--); + if (esiz < nents + 2) { + Value *entsn = Realloc( ents, + sizeof(Value) * (esiz = esiz * 2 + 1) ); + if (!nents) + break; + ents = entsn; + } + ents[nents].ptr = val + b; + ents[nents].len = e - b; + nents++; + tlen += e - b + 1; + } + ents[nents].ptr = 0; + retval->ptr = (char *)ents; + retval->len = tlen; + return 0; + default: + LogError( "Internal error: unknown value type in id %#x\n", et->id ); + return 0; + } +} + +static void +GetValue( Ent *et, DSpec *dspec, Value *retval, char **eopts ) +{ + Entry *ent; + const char *errs; + +/* Debug( "Getting value %#x\n", et->id );*/ + if (dspec) + ent = FindDEnt( et->id, dspec ); + else + ent = FindGEnt( et->id ); + if (ent) { + if (!(errs = CvtValue( et, retval, ent->vallen, ent->val, eopts ))) + return; + LogError( "Invalid %s value '%.*s' at %s:%d\n", + errs, ent->vallen, ent->val, kdmrc, ent->line ); + } + Debug( "default: %s = %'s\n", et->name, et->def ); + if ((errs = CvtValue( et, retval, strlen( et->def ), et->def, eopts ))) + LogError( "Internal error: invalid default %s value '%s' for key %s\n", + errs, et->def, et->name ); +} + +static int +AddValue( ValArr *va, int id, Value *val ) +{ + int nu; + +/* Debug( "Addig value %#x\n", id );*/ + if (va->nents == va->esiz) { + va->ents = Realloc( va->ents, sizeof(Val) * (va->esiz += 50) ); + if (!va->ents) + return 0; + } + va->ents[va->nents].id = id; + va->ents[va->nents].val = *val; + va->nents++; + switch (id & C_TYPE_MASK) { + case C_TYPE_INT: + break; + case C_TYPE_STR: + va->nchars += val->len; + break; + case C_TYPE_ARGV: + va->nchars += val->len; + for (nu = 0; ((Value *)val->ptr)[nu++].ptr; ); + va->nptrs += nu; + break; + } + return 1; +} + +static void +CopyValues( ValArr *va, Sect *sec, DSpec *dspec, int isconfig ) +{ + Value val; + int i; + + Debug( "getting values for section class [%s]\n", sec->name ); + for (i = 0; i < sec->numents; i++) { +/*Debug ("value %#x\n", sec->ents[i].id);*/ + if ((sec->ents[i].id & (int)C_CONFIG) != isconfig) + ; + else if (sec->ents[i].id & C_INTERNAL) { + GetValue( sec->ents + i, dspec, ((Value *)sec->ents[i].ptr), 0 ); + } else { + if (((sec->ents[i].id & C_MTYPE_MASK) == C_ENUM) || + !sec->ents[i].ptr || + !((int (*)( Value * ))sec->ents[i].ptr)(&val)) { + GetValue( sec->ents + i, dspec, &val, + (char **)sec->ents[i].ptr ); + } + if (!AddValue( va, sec->ents[i].id, &val )) + break; + } + } + return; +} + +static void +SendValues( ValArr *va ) +{ + Value *cst; + int i, nu; + + GSendInt( va->nents ); + GSendInt( va->nptrs ); + GSendInt( 0/*va->nints*/ ); + GSendInt( va->nchars ); + for (i = 0; i < va->nents; i++) { + GSendInt( va->ents[i].id & ~C_PRIVATE ); + switch (va->ents[i].id & C_TYPE_MASK) { + case C_TYPE_INT: + GSendInt( (int)va->ents[i].val.ptr ); + break; + case C_TYPE_STR: + GSendNStr( va->ents[i].val.ptr, va->ents[i].val.len - 1 ); + break; + case C_TYPE_ARGV: + cst = (Value *)va->ents[i].val.ptr; + for (nu = 0; cst[nu].ptr; nu++); + GSendInt( nu ); + for (; cst->ptr; cst++) + GSendNStr( cst->ptr, cst->len ); + break; + } + } +} + + +#ifdef XDMCP +static char * +ReadWord( File *file, int *len, int EOFatEOL ) +{ + char *wordp, *wordBuffer; + int quoted; + char c; + + rest: + wordp = wordBuffer = file->cur; + mloop: + quoted = 0; + qloop: + if (file->cur == file->eof) { + doeow: + if (wordp == wordBuffer) + return 0; + retw: + *wordp = '\0'; + *len = wordp - wordBuffer; + return wordBuffer; + } + c = *file->cur++; + switch (c) { + case '#': + if (quoted) + break; + do { + if (file->cur == file->eof) + goto doeow; + c = *file->cur++; + } while (c != '\n'); + case '\0': + case '\n': + if (EOFatEOL && !quoted) { + file->cur--; + goto doeow; + } + if (wordp != wordBuffer) { + file->cur--; + goto retw; + } + goto rest; + case ' ': + case '\t': + if (wordp != wordBuffer) + goto retw; + goto rest; + case '\\': + if (!quoted) { + quoted = 1; + goto qloop; + } + break; + } + *wordp++ = c; + goto mloop; +} + +#define ALIAS_CHARACTER '%' +#define EQUAL_CHARACTER '=' +#define NEGATE_CHARACTER '!' +#define CHOOSER_STRING "CHOOSER" +#define BROADCAST_STRING "BROADCAST" +#define NOBROADCAST_STRING "NOBROADCAST" +#define LISTEN_STRING "LISTEN" +#define WILDCARD_STRING "*" + +typedef struct hostEntry { + struct hostEntry *next; + int type; + union _hostOrAlias { + char *aliasPattern; + char *hostPattern; + struct _display { + int connectionType; + int hostAddrLen; + char *hostAddress; + } displayAddress; + } entry; +} HostEntry; + +typedef struct listenEntry { + struct listenEntry *next; + int iface; + int mcasts; + int nmcasts; +} ListenEntry; + +typedef struct aliasEntry { + struct aliasEntry *next; + char *name; + HostEntry **pHosts; + int hosts; + int nhosts; + int hasBad; +} AliasEntry; + +typedef struct aclEntry { + struct aclEntry *next; + HostEntry **pEntries; + int entries; + int nentries; + HostEntry **pHosts; + int hosts; + int nhosts; + int flags; +} AclEntry; + + +static int +HasGlobCharacters( char *s ) +{ + for (;;) + switch (*s++) { + case '?': + case '*': + return 1; + case '\0': + return 0; + } +} + +#define PARSE_ALL 0 +#define PARSE_NO_BCAST 1 +#define PARSE_NO_PAT 2 +#define PARSE_NO_ALIAS 4 + +static int +ParseHost( int *nHosts, HostEntry ***hostPtr, int *nChars, + char *hostOrAlias, int len, int parse ) +{ +#if defined(IPv6) && defined(AF_INET6) + struct addrinfo *ai; +#else + struct hostent *hostent; +#endif + void *addr; + int addr_type, addr_len; + + if (!(**hostPtr = (HostEntry *)Malloc( sizeof(HostEntry)))) + return 0; + if (!(parse & PARSE_NO_BCAST) && !strcmp( hostOrAlias, BROADCAST_STRING )) + { + (**hostPtr)->type = HOST_BROADCAST; + } + else if (!(parse & PARSE_NO_ALIAS) && *hostOrAlias == ALIAS_CHARACTER) + { + (**hostPtr)->type = HOST_ALIAS; + (**hostPtr)->entry.aliasPattern = hostOrAlias + 1; + *nChars += len; + } + else if (!(parse & PARSE_NO_PAT) && HasGlobCharacters( hostOrAlias )) + { + (**hostPtr)->type = HOST_PATTERN; + (**hostPtr)->entry.hostPattern = hostOrAlias; + *nChars += len + 1; + } + else + { + (**hostPtr)->type = HOST_ADDRESS; +#if defined(IPv6) && defined(AF_INET6) + if (getaddrinfo( hostOrAlias, NULL, NULL, &ai )) +#else + if (!(hostent = gethostbyname( hostOrAlias ))) +#endif + { + LogWarn( "XDMCP ACL: unresolved host %'s\n", hostOrAlias ); + free( (char *)(**hostPtr) ); + return 0; + } +#if defined(IPv6) && defined(AF_INET6) + addr_type = ai->ai_addr->sa_family; + if (ai->ai_family == AF_INET) { + addr = &((struct sockaddr_in *)ai->ai_addr)->sin_addr; + addr_len = sizeof(struct in_addr); + } else /*if (ai->ai_addr->sa_family == AF_INET6)*/ { + addr = &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr; + addr_len = sizeof(struct in6_addr); + } +#else + addr_type = hostent->h_addrtype; + addr = hostent->h_addr; + addr_len = hostent->h_length; +#endif + if (!((**hostPtr)->entry.displayAddress.hostAddress = + Malloc( addr_len ))) + { +#if defined(IPv6) && defined(AF_INET6) + freeaddrinfo( ai ); +#endif + free( (char *)(**hostPtr) ); + return 0; + } + memcpy( (**hostPtr)->entry.displayAddress.hostAddress, addr, addr_len ); + *nChars += addr_len; + (**hostPtr)->entry.displayAddress.hostAddrLen = addr_len; + (**hostPtr)->entry.displayAddress.connectionType = addr_type; +#if defined(IPv6) && defined(AF_INET6) + freeaddrinfo( ai ); +#endif + } + *hostPtr = &(**hostPtr)->next; + (*nHosts)++; + return 1; +} + +/* Returns non-0 if string is matched by pattern. Does case folding. */ +static int +patternMatch( const char *string, const char *pattern ) +{ + int p, s; + + if (!string) + string = ""; + + for (;;) { + s = *string++; + switch (p = *pattern++) { + case '*': + if (!*pattern) + return 1; + for (string--; *string; string++) + if (patternMatch( string, pattern )) + return 1; + return 0; + case '?': + if (s == '\0') + return 0; + break; + case '\0': + return s == '\0'; + case '\\': + p = *pattern++; + /* fall through */ + default: + if (tolower( p ) != tolower( s )) + return 0; + } + } +} + +#define MAX_DEPTH 32 + +#define CHECK_NOT 1 +#define CHECK_NO_PAT 2 + +static int +checkHostlist( HostEntry **hosts, int nh, AliasEntry *aliases, int na, + int depth, int flags ) +{ + HostEntry *h; + AliasEntry *a; + int hn, an, am; + + for (h = *hosts, hn = 0; hn < nh; hn++, h = h->next) + if (h->type == HOST_ALIAS) { + if (depth == MAX_DEPTH) { + LogError( "XDMCP ACL: alias recursion involving %%%s\n", + h->entry.aliasPattern ); + return 1; + } + for (a = aliases, an = 0, am = 0; an < na; an++, a = a->next) + if (patternMatch( a->name, h->entry.aliasPattern )) { + am = 1; + if ((flags & CHECK_NOT) && a->hasBad) { + LogError( "XDMCP ACL: alias %%%s with unresolved hosts " + "in denying rule\n", a->name ); + return 1; + } + if (checkHostlist( a->pHosts, a->nhosts, aliases, na, + depth + 1, flags )) + return 1; + } + if (!am) { + if (flags & CHECK_NOT) { + LogError( "XDMCP ACL: unresolved alias pattern %%%s " + "in denying rule\n", h->entry.aliasPattern ); + return 1; + } else + LogWarn( "XDMCP ACL: unresolved alias pattern %%%s\n", + h->entry.aliasPattern ); + } + } else if (h->type == HOST_PATTERN && (flags & CHECK_NO_PAT)) + LogWarn( "XDMCP ACL: wildcarded pattern %'s in host-only context\n", + h->entry.hostPattern ); + return 0; +} + +static void +ReadAccessFile( const char *fname ) +{ + HostEntry *hostList, **hostPtr = &hostList; + AliasEntry *aliasList, **aliasPtr = &aliasList; + AclEntry *acList, **acPtr = &acList, *acl; + ListenEntry *listenList, **listenPtr = &listenList; + char *displayOrAlias, *hostOrAlias; + File file; + int nHosts, nAliases, nAcls, nListens, nChars, error, bad; + int i, len; + + nHosts = nAliases = nAcls = nListens = nChars = error = 0; + if (!readFile( &file, fname, "XDMCP access control" )) + goto sendacl; + while ((displayOrAlias = ReadWord( &file, &len, FALSE ))) { + if (*displayOrAlias == ALIAS_CHARACTER) + { + if (!(*aliasPtr = (AliasEntry *)Malloc( sizeof(AliasEntry)))) { + error = 1; + break; + } + (*aliasPtr)->name = displayOrAlias + 1; + nChars += len; + (*aliasPtr)->hosts = nHosts; + (*aliasPtr)->pHosts = hostPtr; + (*aliasPtr)->nhosts = 0; + (*aliasPtr)->hasBad = 0; + while ((hostOrAlias = ReadWord( &file, &len, TRUE ))) { + if (ParseHost( &nHosts, &hostPtr, &nChars, hostOrAlias, len, + PARSE_NO_BCAST )) + (*aliasPtr)->nhosts++; + else + (*aliasPtr)->hasBad = 1; + } + aliasPtr = &(*aliasPtr)->next; + nAliases++; + } + else if (!strcmp( displayOrAlias, LISTEN_STRING )) + { + if (!(*listenPtr = (ListenEntry *)Malloc( sizeof(ListenEntry)))) { + error = 1; + break; + } + (*listenPtr)->iface = nHosts; + if (!(hostOrAlias = ReadWord( &file, &len, TRUE )) || + !strcmp( hostOrAlias, WILDCARD_STRING ) || + !ParseHost( &nHosts, &hostPtr, &nChars, hostOrAlias, len, + PARSE_NO_BCAST|PARSE_NO_PAT|PARSE_NO_ALIAS )) + { + (*listenPtr)->iface = -1; + } + (*listenPtr)->mcasts = nHosts; + (*listenPtr)->nmcasts = 0; + while ((hostOrAlias = ReadWord( &file, &len, TRUE ))) { + if (ParseHost( &nHosts, &hostPtr, &nChars, hostOrAlias, len, + PARSE_NO_BCAST|PARSE_NO_PAT|PARSE_NO_ALIAS )) + (*listenPtr)->nmcasts++; + } + listenPtr = &(*listenPtr)->next; + nListens++; + } + else + { + if (!(*acPtr = (AclEntry *)Malloc( sizeof(AclEntry)))) { + error = 1; + break; + } + (*acPtr)->flags = 0; + if (*displayOrAlias == NEGATE_CHARACTER) { + (*acPtr)->flags |= a_notAllowed; + displayOrAlias++; + } else if (*displayOrAlias == EQUAL_CHARACTER) + displayOrAlias++; + (*acPtr)->entries = nHosts; + (*acPtr)->pEntries = hostPtr; + (*acPtr)->nentries = 1; + if (!ParseHost( &nHosts, &hostPtr, &nChars, displayOrAlias, len, + PARSE_NO_BCAST )) + { + bad = 1; + if ((*acPtr)->flags & a_notAllowed) { + LogError( "XDMCP ACL: unresolved host in denying rule\n" ); + error = 1; + } + } else + bad = 0; + (*acPtr)->hosts = nHosts; + (*acPtr)->pHosts = hostPtr; + (*acPtr)->nhosts = 0; + while ((hostOrAlias = ReadWord( &file, &len, TRUE ))) { + if (!strcmp( hostOrAlias, CHOOSER_STRING )) + (*acPtr)->flags |= a_useChooser; + else if (!strcmp( hostOrAlias, NOBROADCAST_STRING )) + (*acPtr)->flags |= a_notBroadcast; + else { + if (ParseHost( &nHosts, &hostPtr, &nChars, + hostOrAlias, len, PARSE_NO_PAT )) + (*acPtr)->nhosts++; + } + } + if (!bad) { + acPtr = &(*acPtr)->next; + nAcls++; + } + } + } + + if (!nListens) { + if (!(*listenPtr = (ListenEntry *)Malloc( sizeof(ListenEntry)))) + error = 1; + else { + (*listenPtr)->iface = -1; + (*listenPtr)->mcasts = nHosts; + (*listenPtr)->nmcasts = 0; +#if defined(IPv6) && defined(AF_INET6) && defined(XDM_DEFAULT_MCAST_ADDR6) + if (ParseHost( &nHosts, &hostPtr, &nChars, + XDM_DEFAULT_MCAST_ADDR6, + sizeof(XDM_DEFAULT_MCAST_ADDR6)-1, + PARSE_ALL )) + (*listenPtr)->nmcasts++; +#endif + nListens++; + } + } + + for (acl = acList, i = 0; i < nAcls; i++, acl = acl->next) + if (checkHostlist( acl->pEntries, acl->nentries, aliasList, nAliases, + 0, (acl->flags & a_notAllowed) ? CHECK_NOT : 0 ) || + checkHostlist( acl->pHosts, acl->nhosts, aliasList, nAliases, + 0, CHECK_NO_PAT )) + error = 1; + + if (error) { + nHosts = nAliases = nAcls = nListens = nChars = 0; + sendacl: + LogError( "No XDMCP requests will be granted\n" ); + } + GSendInt( nHosts ); + GSendInt( nListens ); + GSendInt( nAliases ); + GSendInt( nAcls ); + GSendInt( nChars ); + for (i = 0; i < nHosts; i++, hostList = hostList->next) { + GSendInt( hostList->type ); + switch (hostList->type) { + case HOST_ALIAS: + GSendStr( hostList->entry.aliasPattern ); + break; + case HOST_PATTERN: + GSendStr( hostList->entry.hostPattern ); + break; + case HOST_ADDRESS: + GSendArr( hostList->entry.displayAddress.hostAddrLen, + hostList->entry.displayAddress.hostAddress ); + GSendInt( hostList->entry.displayAddress.connectionType ); + break; + } + } + for (i = 0; i < nListens; i++, listenList = listenList->next) { + GSendInt( listenList->iface ); + GSendInt( listenList->mcasts ); + GSendInt( listenList->nmcasts ); + } + for (i = 0; i < nAliases; i++, aliasList = aliasList->next) { + GSendStr( aliasList->name ); + GSendInt( aliasList->hosts ); + GSendInt( aliasList->nhosts ); + } + for (i = 0; i < nAcls; i++, acList = acList->next) { + GSendInt( acList->entries ); + GSendInt( acList->nentries ); + GSendInt( acList->hosts ); + GSendInt( acList->nhosts ); + GSendInt( acList->flags ); + } +} +#endif + + +int main( int argc ATTR_UNUSED, char **argv ) +{ + DSpec dspec; + ValArr va; + char *ci, *disp, *dcls, *cfgfile; + int what; + + if (!(ci = getenv( "CONINFO" ))) { + fprintf( stderr, "This program is part of kdm and should not be run manually.\n" ); + return 1; + } + if (sscanf( ci, "%d %d", &rfd, &wfd ) != 2) + return 1; + + InitLog(); + + if ((debugLevel = GRecvInt()) & DEBUG_WCONFIG) + sleep( 100 ); + +/* Debug ("parsing command line\n");*/ + if (**++argv) + kdmrc = *argv; +/* + while (*++argv) { + } +*/ + + for (;;) { +/* Debug ("Awaiting command ...\n");*/ + if (!GRecvCmd( &what )) + break; + switch (what) { + case GC_Files: +/* Debug ("GC_Files\n");*/ + ReadConf(); + CopyValues( 0, &secGeneral, 0, C_CONFIG ); +#ifdef XDMCP + CopyValues( 0, &secXdmcp, 0, C_CONFIG ); + GSendInt( 2 ); +#else + GSendInt( 1 ); +#endif + GSendStr( kdmrc ); + GSendInt( -1 ); +#ifdef XDMCP + GSendNStr( VXaccess.ptr, VXaccess.len - 1 ); + GSendInt( 0 ); +#endif + for (; (what = GRecvInt()) != -1; ) + switch (what) { + case GC_gGlobal: + case GC_gDisplay: + GSendInt( 0 ); + break; +#ifdef XDMCP + case GC_gXaccess: + GSendInt( 1 ); + break; +#endif + default: + GSendInt( -1 ); + break; + } + break; + case GC_GetConf: +/* Debug( "GC_GetConf\n" );*/ + memset( &va, 0, sizeof(va) ); + what = GRecvInt(); + cfgfile = GRecvStr(); + switch (what) { + case GC_gGlobal: +/* Debug( "GC_gGlobal\n" );*/ + Debug( "getting global config\n" ); + ReadConf(); + CopyValues( &va, &secGeneral, 0, 0 ); +#ifdef XDMCP + CopyValues( &va, &secXdmcp, 0, 0 ); +#endif + CopyValues( &va, &secShutdown, 0, 0 ); + SendValues( &va ); + break; + case GC_gDisplay: +/* Debug( "GC_gDisplay\n" );*/ + disp = GRecvStr(); +/* Debug( " Display %s\n", disp );*/ + dcls = GRecvStr(); +/* Debug( " Class %s\n", dcls );*/ + Debug( "getting config for display %s, class %s\n", disp, dcls ); + MkDSpec( &dspec, disp, dcls ? dcls : "" ); + ReadConf(); + CopyValues( &va, &sec_Core, &dspec, 0 ); + CopyValues( &va, &sec_Greeter, &dspec, 0 ); + free( disp ); + if (dcls) + free( dcls ); + SendValues( &va ); + break; +#ifdef XDMCP + case GC_gXaccess: + ReadAccessFile( cfgfile ); + break; +#endif + default: + Debug( "Unsupported config category %#x\n", what ); + } + free( cfgfile ); + break; + default: + Debug( "Unknown config command %#x\n", what ); + } + } + +/* Debug( "Config reader exiting ..." );*/ + return EX_NORMAL; +} diff --git a/kdm/kfrontend/kdm_greet.c b/kdm/kfrontend/kdm_greet.c new file mode 100644 index 000000000..6abc2c057 --- /dev/null +++ b/kdm/kfrontend/kdm_greet.c @@ -0,0 +1,787 @@ +/* + +KDE Greeter module for xdm + +Copyright (C) 2001-2003 Oswald Buddenhagen <[email protected]> + +This file contains code from the old xdm core, +Copyright 1988, 1998 Keith Packard, MIT X Consortium/The Open Group + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include <config.h> + +#include "kdm_greet.h" +#include "kdmconfig.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdarg.h> +#include <string.h> +#include <signal.h> +#include <setjmp.h> +#include <ctype.h> +#include <time.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#ifdef _POSIX_PRIORITY_SCHEDULING +# include <sched.h> +#endif + +#if defined(HAVE_XTEST) || defined(HAVE_XKB) +# include <X11/Xlib.h> +# include <X11/keysym.h> +#endif + +#ifdef HAVE_XTEST +# include <X11/extensions/XTest.h> +#endif + +#ifdef HAVE_XKB +# include <X11/XKBlib.h> +#endif + +extern void LogOutOfMem( void ); + +static void * +Realloc( void *ptr, size_t size ) +{ + void *ret; + + if (!(ret = realloc( ptr, size )) && size) + LogOutOfMem(); + return ret; +} + +#define PRINT_QUOTES +#define PRINT_ARRAYS +#define LOG_NAME "kdm_greet" +#define LOG_DEBUG_MASK DEBUG_GREET +#define LOG_PANIC_EXIT 1 +#define STATIC +#include <printf.c> + +static void +GDebug( const char *fmt, ... ) +{ + va_list args; + + if (debugLevel & DEBUG_HLPCON) { + va_start( args, fmt ); + Logger( DM_DEBUG, fmt, args ); + va_end( args ); + } +} + + +char *dname; + +int rfd; +static int wfd, mrfd, mwfd, srfd, swfd; +static const char *who; + +void +GSet( int master ) +{ + if (master) + rfd = mrfd, wfd = mwfd, who = "core (master)"; + else + rfd = srfd, wfd = swfd, who = "core"; + +} + +static int +Reader( void *buf, int count ) +{ + int ret, rlen; + + for (rlen = 0; rlen < count; ) { + dord: + ret = read( rfd, (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; +} + +static void +GRead( void *buf, int count ) +{ + if (Reader( buf, count ) != count) + LogPanic( "Can't read from %s\n", who ); +} + +static void +GWrite( const void *buf, int count ) +{ + if (write( wfd, buf, count ) != count) + LogPanic( "Can't write to %s\n", who ); +#ifdef _POSIX_PRIORITY_SCHEDULING + if ((debugLevel & DEBUG_HLPCON)) + sched_yield(); +#endif +} + +void +GSendInt( int val ) +{ + GDebug( "Sending int %d (%#x) to %s\n", val, val, who ); + GWrite( &val, sizeof(val) ); +} + +void +GSendStr( const char *buf ) +{ + int len = buf ? strlen( buf ) + 1 : 0; + GDebug( "Sending string %'s to %s\n", buf, who ); + GWrite( &len, sizeof(len) ); + GWrite( buf, len ); +} + +/* +static void +GSendNStr( const char *buf, int len ) +{ + int tlen = len + 1; + GDebug( "Sending string %'.*s to %s\n", len, buf, who ); + GWrite( &tlen, sizeof(tlen) ); + GWrite( buf, len ); + GWrite( "", 1 ); +} +*/ + +void +GSendArr( int len, const char *buf ) +{ + GDebug( "Sending array %02[:*hhx to %s\n", len, buf, who ); + GWrite( &len, sizeof(len) ); + GWrite( buf, len ); +} + +int +GRecvInt() +{ + int val; + + GDebug( "Receiving int from %s ...\n", who ); + GRead( &val, sizeof(val) ); + GDebug( " -> %d (%#x)\n", val, val ); + return val; +} + +static char * +iGRecvArr( int *rlen ) +{ + int len; + char *buf; + + GRead( &len, sizeof(len) ); + *rlen = len; + GDebug( " -> %d bytes\n", len ); + if (!len) + return (char *)0; + if (!(buf = malloc( len ))) + LogPanic( "No memory for read buffer\n" ); + GRead( buf, len ); + return buf; +} + +char * +GRecvStr() +{ + int len; + char *buf; + + GDebug( "Receiving string from %s ...\n", who ); + buf = iGRecvArr( &len ); + GDebug( " -> %'.*s\n", len, buf ); + return buf; +} + +char ** +GRecvStrArr( int *rnum ) +{ + int num; + char **argv, **cargv; + + GDebug( "Receiving string array from %s ...\n", who ); + GRead( &num, sizeof(num) ); + GDebug( " -> %d strings\n", num ); + if (rnum) + *rnum = num; + if (!num) + return (char **)0; + if (!(argv = malloc( num * sizeof(char *)))) + LogPanic( "No memory for read buffer\n" ); + for (cargv = argv; --num >= 0; cargv++) + *cargv = GRecvStr(); + return argv; +} + +char * +GRecvArr( int *num ) +{ + char *arr; + + GDebug( "Receiving array from %s ...\n", who ); + GRead( num, sizeof(*num) ); + GDebug( " -> %d bytes\n", *num ); + if (!*num) + return (char *)0; + if (!(arr = malloc( *num ))) + LogPanic( "No memory for read buffer\n" ); + GRead( arr, *num ); + GDebug( " -> %02[*hhx\n", *num, arr ); + return arr; +} + +static void +ReqCfg( int id ) +{ + GSendInt( G_GetCfg ); + GSendInt( id ); + switch (GRecvInt()) { + case GE_NoEnt: + LogPanic( "Config value %#x not available\n", id ); + case GE_BadType: + LogPanic( "Core does not know type of config value %#x\n", id ); + } +} + +int +GetCfgInt( int id ) +{ + ReqCfg( id ); + return GRecvInt(); +} + +char * +GetCfgStr( int id ) +{ + ReqCfg( id ); + return GRecvStr(); +} + +char ** +GetCfgStrArr( int id, int *len ) +{ + ReqCfg( id ); + return GRecvStrArr( len ); +} + +static void +disposeSession( dpySpec *sess ) +{ + free( sess->display ); + free( sess->from ); + if (sess->user) + free( sess->user ); + if (sess->session) + free( sess->session ); +} + +dpySpec * +fetchSessions( int flags ) +{ + dpySpec *sess, *sessions = 0, tsess; + + GSet( 1 ); + GSendInt( G_List ); + GSendInt( flags ); + next: + while ((tsess.display = GRecvStr())) { + tsess.from = GRecvStr(); +#ifdef HAVE_VTS + tsess.vt = GRecvInt(); +#endif + tsess.user = GRecvStr(); + tsess.session = GRecvStr(); + tsess.flags = GRecvInt(); + if ((tsess.flags & isTTY) && *tsess.from) + for (sess = sessions; sess; sess = sess->next) + if (sess->user && !strcmp( sess->user, tsess.user ) && + !strcmp( sess->from, tsess.from )) + { + sess->count++; + disposeSession( &tsess ); + goto next; + } + if (!(sess = malloc( sizeof(*sess) ))) + LogPanic( "Out of memory\n" ); + tsess.count = 1; + tsess.next = sessions; + *sess = tsess; + sessions = sess; + } + GSet( 0 ); + return sessions; +} + +void +disposeSessions( dpySpec *sess ) +{ + while (sess) { + dpySpec *nsess = sess->next; + disposeSession( sess ); + free( sess ); + sess = nsess; + } +} + +void +freeStrArr( char **arr ) +{ + char **tarr; + + if (arr) { + for (tarr = arr; *tarr; tarr++) + free( *tarr ); + free( arr ); + } +} + + +static int +ignoreErrors( Display *dpy ATTR_UNUSED, XErrorEvent *event ATTR_UNUSED ) +{ + Debug( "ignoring X error\n" ); + return 0; +} + +/* + * this is mostly bogus -- but quite useful. I wish the protocol + * had some way of enumerating and identifying clients, that way + * this code wouldn't have to be this kludgy. + */ + +static void +killWindows( Display *dpy, Window window ) +{ + Window root, parent, *children; + unsigned child, nchildren = 0; + + while (XQueryTree( dpy, window, &root, &parent, &children, &nchildren ) + && nchildren > 0) + { + for (child = 0; child < nchildren; child++) { + Debug( "XKillClient 0x%lx\n", (unsigned long)children[child] ); + XKillClient( dpy, children[child] ); + } + XFree( (char *)children ); + } +} + +static jmp_buf resetJmp; + +static void +abortReset( int n ATTR_UNUSED ) +{ + longjmp (resetJmp, 1); +} + +/* + * this display connection better not have any windows... + */ + +static void +pseudoReset( Display *dpy ) +{ + int screen; + + if (setjmp( resetJmp )) { + LogError( "pseudoReset timeout\n" ); + } else { + (void)signal( SIGALRM, abortReset ); + (void)alarm( 30 ); + XSetErrorHandler( ignoreErrors ); + for (screen = 0; screen < ScreenCount( dpy ); screen++) { + Debug( "pseudoReset screen %d\n", screen ); + killWindows( dpy, RootWindow( dpy, screen ) ); + } + Debug( "before XSync\n" ); + XSync( dpy, False ); + (void)alarm( 0 ); + } + signal( SIGALRM, SIG_DFL ); + XSetErrorHandler( (XErrorHandler)0 ); + Debug( "pseudoReset done\n" ); +} + + +static jmp_buf syncJump; + +static void +syncTimeout( int n ATTR_UNUSED ) +{ + longjmp( syncJump, 1 ); +} + +void +SecureDisplay( Display *dpy ) +{ + Debug( "SecureDisplay %s\n", dname ); + (void)signal( SIGALRM, syncTimeout ); + if (setjmp( syncJump )) { + LogError( "Display %s could not be secured\n", dname ); + exit( EX_RESERVER_DPY ); + } + (void)alarm( (unsigned)_grabTimeout ); + Debug( "Before XGrabServer %s\n", dname ); + XGrabServer( dpy ); + Debug( "XGrabServer succeeded %s\n", dname ); + if (XGrabKeyboard( dpy, DefaultRootWindow( dpy ), True, GrabModeAsync, + GrabModeAsync, CurrentTime ) != GrabSuccess) + { + (void)alarm( 0 ); + (void)signal( SIGALRM, SIG_DFL ); + LogError( "Keyboard on display %s could not be secured\n", dname ); + sleep( 10 ); + exit( EX_RESERVER_DPY ); + } + (void)alarm( 0 ); + (void)signal( SIGALRM, SIG_DFL ); + pseudoReset( dpy ); + if (!_grabServer) + { + XUngrabServer( dpy ); + XSync( dpy, 0 ); + } + Debug( "done secure %s\n", dname ); +#ifdef HAVE_XKBSETPERCLIENTCONTROLS + /* + * Activate the correct mapping for modifiers in XKB extension as + * grabbed keyboard has its own mapping by default + */ + { + int opcode, evbase, errbase, majret, minret; + unsigned int value = XkbPCF_GrabsUseXKBStateMask; + if (XkbQueryExtension( dpy, &opcode, &evbase, + &errbase, &majret, &minret )) + XkbSetPerClientControls( dpy, value, &value ); + } +#endif +} + +void +UnsecureDisplay( Display *dpy ) +{ + Debug( "Unsecure display %s\n", dname ); + if (_grabServer) { + XUngrabServer( dpy ); + XSync( dpy, 0 ); + } +} + +static jmp_buf pingTime; + +static int +PingLostIOErr( Display *dpy ATTR_UNUSED ) +{ + longjmp( pingTime, 1 ); +} + +static void +PingLostSig( int n ATTR_UNUSED ) +{ + longjmp( pingTime, 1 ); +} + +int +PingServer( Display *dpy ) +{ + int (*oldError)( Display * ); + void (*oldSig)( int ); + int oldAlarm; + + oldError = XSetIOErrorHandler( PingLostIOErr ); + oldAlarm = alarm( 0 ); + oldSig = signal( SIGALRM, PingLostSig ); + (void)alarm( _pingTimeout * 60 ); + if (!setjmp( pingTime )) { + Debug( "Ping server\n" ); + XSync( dpy, 0 ); + } else { + Debug( "Server dead\n" ); + (void)alarm( 0 ); + (void)signal( SIGALRM, SIG_DFL ); + XSetIOErrorHandler( oldError ); + return 0; + } + (void)alarm( 0 ); + (void)signal( SIGALRM, oldSig ); + (void)alarm( oldAlarm ); + Debug( "Server alive\n" ); + XSetIOErrorHandler( oldError ); + return 1; +} + +/* + * Modifier changing code based on kdebase/kxkb/kcmmisc.cpp + * + * XTest part: Copyright (C) 2000-2001 Lubos Lunak <[email protected]> + * XKB part: Copyright (C) 2001-2002 Oswald Buddenhagen <[email protected]> + * + */ + +#ifdef HAVE_XKB +static int +xkb_init( Display *dpy ) +{ + int xkb_opcode, xkb_event, xkb_error; + int xkb_lmaj = XkbMajorVersion; + int xkb_lmin = XkbMinorVersion; + return XkbLibraryVersion( &xkb_lmaj, &xkb_lmin ) && + XkbQueryExtension( dpy, &xkb_opcode, &xkb_event, + &xkb_error, &xkb_lmaj, &xkb_lmin ); +} + +static unsigned int +xkb_modifier_mask_work( XkbDescPtr xkb, const char *name ) +{ + int i; + + if (!xkb->names) + return 0; + for (i = 0; i < XkbNumVirtualMods; i++) { + char *modStr = XGetAtomName( xkb->dpy, xkb->names->vmods[i] ); + if (modStr != NULL && strcmp( name, modStr ) == 0) { + unsigned int mask; + XkbVirtualModsToReal( xkb, 1 << i, &mask ); + return mask; + } + } + return 0; +} + +static unsigned int +xkb_modifier_mask( Display *dpy, const char *name ) +{ + XkbDescPtr xkb; + + if ((xkb = XkbGetKeyboard( dpy, XkbAllComponentsMask, XkbUseCoreKbd ))) { + unsigned int mask = xkb_modifier_mask_work( xkb, name ); + XkbFreeKeyboard( xkb, 0, True ); + return mask; + } + return 0; +} + +static int +xkb_get_modifier_state( Display *dpy, const char *name ) +{ + unsigned int mask; + XkbStateRec state; + + if (!(mask = xkb_modifier_mask( dpy, name ))) + return 0; + XkbGetState( dpy, XkbUseCoreKbd, &state ); + return (mask & state.locked_mods) != 0; +} + +static int +xkb_set_modifier( Display *dpy, const char *name, int sts ) +{ + unsigned int mask; + + if (!(mask = xkb_modifier_mask( dpy, name ))) + return 0; + XkbLockModifiers( dpy, XkbUseCoreKbd, mask, sts ? mask : 0 ); + return 1; +} +#endif /* HAVE_XKB */ + +#ifdef HAVE_XTEST +static int +xtest_get_modifier_state( Display *dpy, int key ) +{ + XModifierKeymap *map; + KeyCode modifier_keycode; + unsigned int i, mask; + Window dummy1, dummy2; + int dummy3, dummy4, dummy5, dummy6; + + if ((modifier_keycode = XKeysymToKeycode( dpy, key )) == NoSymbol) + return 0; + map = XGetModifierMapping( dpy ); + for (i = 0; i < 8; ++i) + if (map->modifiermap[map->max_keypermod * i] == modifier_keycode) { + XFreeModifiermap( map ); + XQueryPointer( dpy, DefaultRootWindow( dpy ), + &dummy1, &dummy2, &dummy3, &dummy4, &dummy5, &dummy6, + &mask ); + return (mask & (1 << i)) != 0; + } + XFreeModifiermap( map ); + return 0; +} + +static void +xtest_fake_keypress( Display *dpy, int key ) +{ + XTestFakeKeyEvent( dpy, XKeysymToKeycode( dpy, key ), True, CurrentTime ); + XTestFakeKeyEvent( dpy, XKeysymToKeycode( dpy, key ), False, CurrentTime ); +} +#endif /* HAVE_XTEST */ + +#ifdef HAVE_XKB +static int havexkb; +#endif +static int nummodified, oldnumstate, newnumstate; +static Display *dpy; + +void +setup_modifiers( Display *mdpy, int numlock ) +{ + if (numlock == 2) + return; + newnumstate = numlock; + nummodified = 1; + dpy = mdpy; +#ifdef HAVE_XKB + if (xkb_init( mdpy )) { + havexkb = 1; + oldnumstate = xkb_get_modifier_state( mdpy, "NumLock" ); + xkb_set_modifier( mdpy, "NumLock", numlock ); + return; + } +#endif +#ifdef HAVE_XTEST + oldnumstate = xtest_get_modifier_state( mdpy, XK_Num_Lock ); + if (oldnumstate != numlock) + xtest_fake_keypress( mdpy, XK_Num_Lock ); +#endif +} + +void +restore_modifiers( void ) +{ +#ifdef HAVE_XTEST + int numstat; +#endif + + if (!nummodified) + return; +#ifdef HAVE_XKB + if (havexkb) { + if (xkb_get_modifier_state( dpy, "NumLock" ) == newnumstate) + xkb_set_modifier( dpy, "NumLock", oldnumstate ); + return; + } +#endif +#ifdef HAVE_XTEST + numstat = xtest_get_modifier_state( dpy, XK_Num_Lock ); + if (numstat == newnumstate && newnumstate != oldnumstate) + xtest_fake_keypress( dpy, XK_Num_Lock ); +#endif +} + +void +setCursor( Display *mdpy, int window, int shape ) +{ + Cursor xcursor; + + if ((xcursor = XCreateFontCursor( mdpy, shape ))) { + XDefineCursor( mdpy, window, xcursor ); + XFreeCursor( mdpy, xcursor ); + XFlush( mdpy ); + } +} + +static void +sigterm( int n ATTR_UNUSED ) +{ + exit( EX_NORMAL ); +} + +static char *savhome; + +static void +cleanup( void ) +{ + char buf[128]; + + if (strcmp( savhome, getenv( "HOME" ) ) || memcmp( savhome, "/tmp/", 5 )) + LogError( "Internal error: memory corruption detected\n" ); /* no panic: recursion */ + else { + sprintf( buf, "rm -rf %s", savhome ); + system( buf ); + } +} + +extern void kg_main( const char *argv0 ); + +int +main( int argc ATTR_UNUSED, char **argv ) +{ + char *ci; + int i; + char qtrc[40]; + + if (!(ci = getenv( "CONINFO" ))) { + fprintf( stderr, "This program is part of kdm and should not be run manually.\n" ); + return 1; + } + if (sscanf( ci, "%d %d %d %d", &srfd, &swfd, &mrfd, &mwfd ) != 4) + return 1; + fcntl( srfd, F_SETFD, FD_CLOEXEC ); + fcntl( swfd, F_SETFD, FD_CLOEXEC ); + fcntl( mrfd, F_SETFD, FD_CLOEXEC ); + fcntl( mwfd, F_SETFD, FD_CLOEXEC ); + GSet( 0 ); + + InitLog(); + + if ((debugLevel = GRecvInt()) & DEBUG_WGREET) + sleep( 100 ); + + signal( SIGTERM, sigterm ); + + dname = getenv( "DISPLAY" ); + + init_config(); + + /* for QSettings */ + srand( time( 0 ) ); + for (i = 0; i < 10000; i++) { + sprintf( qtrc, "/tmp/%010d", rand() ); + if (!mkdir( qtrc, 0700 )) + goto okay; + } + LogPanic( "Cannot create $HOME\n" ); + okay: + if (setenv( "HOME", qtrc, 1 )) + LogPanic( "Cannot set $HOME\n" ); + if (!(savhome = strdup( qtrc ))) + LogPanic( "Cannot save $HOME\n" ); + atexit( cleanup ); + + setenv( "LC_ALL", _language, 1 ); + + kg_main( argv[0] ); + + return EX_NORMAL; +} diff --git a/kdm/kfrontend/kdm_greet.h b/kdm/kfrontend/kdm_greet.h new file mode 100644 index 000000000..df325c28d --- /dev/null +++ b/kdm/kfrontend/kdm_greet.h @@ -0,0 +1,91 @@ +/* + +KDE Greeter module for xdm + +Copyright (C) 2001-2003 Oswald Buddenhagen <[email protected]> + +This file contains code from the old xdm core, +Copyright 1988, 1998 Keith Packard, MIT X Consortium/The Open Group + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#ifndef _KDM_GREET_H_ +#define _KDM_GREET_H_ + +#include <greet.h> /* for the ATTR_ defines */ +#include <config.ci> /* for the HAVE_VTS define */ + +#ifdef __cplusplus +extern "C" { +#endif + +void GSet( int master ); +void GSendInt( int val ); +void GSendStr( const char *buf ); +/*void GSendNStr( const char *buf, int len );*/ +void GSendArr( int len, const char *buf ); +int GRecvInt( void ); +char *GRecvStr( void ); +char **GRecvStrArr( int *len ); +char *GRecvArr( int *len ); + +int GetCfgInt( int id ); +char *GetCfgStr( int id ); +char **GetCfgStrArr( int id, int *len ); + +typedef struct dpySpec { + struct dpySpec *next; + char *display, *from, *user, *session; +#ifdef HAVE_VTS + int vt; +#endif + int flags; + int count; +} dpySpec; + +dpySpec *fetchSessions( int flags ); +void disposeSessions( dpySpec *sess ); + +void freeStrArr( char **arr ); + +void Debug( const char *fmt, ... ); +void LogInfo( const char *fmt, ... ); +void LogWarn( const char *fmt, ... ); +void LogError( const char *fmt, ... ); +void LogPanic( const char *fmt, ... ) ATTR_NORETURN; + +struct _XDisplay; + +void SecureDisplay( struct _XDisplay *dpy ); +void UnsecureDisplay( struct _XDisplay *dpy ); +int PingServer( struct _XDisplay *dpy ); + +void setup_modifiers( struct _XDisplay *mdpy, int numlock ); +void restore_modifiers( void ); + +void setCursor( struct _XDisplay *mdpy, int window, int shape ); + + +extern int rfd; /* for select() loops */ + +extern char *dname; /* d->name */ + +#ifdef __cplusplus +} +#endif + +#endif /* _KDM_GREET_H_ */ diff --git a/kdm/kfrontend/kdmclock.cpp b/kdm/kfrontend/kdmclock.cpp new file mode 100644 index 000000000..88bc33aaa --- /dev/null +++ b/kdm/kfrontend/kdmclock.cpp @@ -0,0 +1,176 @@ +/* + +clock module for kdm + +Copyright (C) 2000 Espen Sand, [email protected] + Based on work by NN(yet to be determined) +flicker free code by Remi Guyomarch <[email protected]> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include "kdmclock.h" + +//#include <kapplication.h> +//#include <kconfig.h> + +#include <qdatetime.h> +#include <qpixmap.h> +#include <qpainter.h> +#include <qtimer.h> + +KdmClock::KdmClock( QWidget *parent, const char *name ) + : inherited( parent, name ) +{ + // start timer + QTimer *timer = new QTimer( this ); + connect( timer, SIGNAL(timeout()), SLOT(timeout()) ); + timer->start( 1000 ); + + // reading rc file + //KConfig *config = kapp->config(); + + //config->setGroup( "Option" ); + mDate = false;//config->readNumEntry( "date", FALSE ); + mSecond = true;//config->readNumEntry( "second", TRUE ); + mDigital = false;//config->readNumEntry( "digital", FALSE ); + mBorder = false;//config->readNumEntry( "border", FALSE ); + + //config->setGroup( "Font" ); + mFont.setFamily( QString::fromLatin1("Utopia")/*config->readEntry( "Family", "Utopia")*/ ); + mFont.setPointSize( 51/*config->readNumEntry( "Point Size", 51)*/ ); + mFont.setWeight( 75/*config->readNumEntry( "Weight", 75)*/ ); + mFont.setItalic( TRUE/*config->readNumEntry( "Italic",TRUE )*/ ); + mFont.setBold( TRUE/*config->readNumEntry( "Bold",TRUE )*/ ); + + setFixedSize( 100, 100 ); + + if (mBorder) { + setLineWidth( 1 ); + setFrameStyle( Box|Plain ); + //setFrameStyle( WinPanel|Sunken ); + } + +/* + if (!mDigital) { + if (height() < width()) + resize( height(), height() ); + else + resize( width() ,width() ); + } +*/ + + //setBackgroundOrigin( WindowOrigin ); + mBackgroundBrush = backgroundBrush(); + setBackgroundMode( NoBackground ); + repaint(); +} + + +void KdmClock::showEvent( QShowEvent * ) +{ + repaint(); +} + + +void KdmClock::timeout() +{ + repaint(); +} + +void KdmClock::paintEvent( QPaintEvent * ) +{ + if (!isVisible()) + return; + + QPainter p( this ); + drawFrame( &p ); + + QPixmap pm( contentsRect().size() ); + QPainter paint; + paint.begin( &pm ); + paint.fillRect( contentsRect(), mBackgroundBrush ); + + // get current time + QTime time = QTime::currentTime(); + +/* + if (mDigital) { + QString buf; + if (mSecond) + buf.sprintf( "%02d:%02d:%02d", time.hour(), time.minute(), + time.second() ); + else + buf.sprintf( "%02d:%02d", time.hour(), time.minute() ); + mFont.setPointSize( QMIN( (int)(width()/buf.length()*1.5),height() ) ); + paint.setFont( mFont ); + paint.setPen( backgroundColor() ); + paint.drawText( contentsRect(),AlignHCenter|AlignVCenter, buf,-1,0,0 ); + } else { +*/ + QPointArray pts; + QPoint cp = contentsRect().center() - QPoint( 2,2 ); + int d = QMIN( contentsRect().width()-15,contentsRect().height()-15 ); + paint.setPen( foregroundColor() ); + paint.setBrush( foregroundColor() ); + + QWMatrix matrix; + matrix.translate( cp.x(), cp.y() ); + matrix.scale( d/1000.0F, d/1000.0F ); + + // Hour + float h_angle = 30*(time.hour()%12-3) + time.minute()/2; + matrix.rotate( h_angle ); + paint.setWorldMatrix( matrix ); + pts.setPoints( 4, -20,0, 0,-20, 300,0, 0,20 ); + paint.drawPolygon( pts ); + matrix.rotate( -h_angle ); + + // Minute + float m_angle = (time.minute()-15)*6; + matrix.rotate( m_angle ); + paint.setWorldMatrix( matrix ); + pts.setPoints( 4, -10,0, 0,-10, 400,0, 0,10 ); + paint.drawPolygon( pts ); + matrix.rotate( -m_angle ); + + // Second + float s_angle = (time.second()-15)*6; + matrix.rotate( s_angle ); + paint.setWorldMatrix( matrix ); + pts.setPoints( 4,0,0,0,0,400,0,0,0 ); + if (mSecond) + paint.drawPolygon( pts ); + matrix.rotate( -s_angle ); + + // quadrante + for (int i=0 ; i < 60 ; i++) { + paint.setWorldMatrix( matrix ); + if ((i % 5) == 0) + paint.drawLine( 450,0, 500,0 ); // draw hour lines + else + paint.drawPoint( 480,0 ); // draw second lines + matrix.rotate( 6 ); + } + +// } // if (mDigital) + paint.end(); + + // flicker free code by Remi Guyomarch <[email protected]> + bitBlt( this, contentsRect().topLeft(), &pm ); +} + +#include "kdmclock.moc" diff --git a/kdm/kfrontend/kdmclock.h b/kdm/kfrontend/kdmclock.h new file mode 100644 index 000000000..92ce97733 --- /dev/null +++ b/kdm/kfrontend/kdmclock.h @@ -0,0 +1,52 @@ +/* + +clock module for kdm + +Copyright (C) 2000 Espen Sand, [email protected] + Based on work by NN (yet to be determined) + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#ifndef _KDM_CLOCK_H_ +#define _KDM_CLOCK_H_ + +#include <qframe.h> + +class KdmClock : public QFrame { + Q_OBJECT + typedef QFrame inherited; + + public: + KdmClock( QWidget *parent=0, const char *name=0 ); + + protected: + virtual void showEvent( QShowEvent * ); + virtual void paintEvent( QPaintEvent * ); + + private slots: + void timeout(); + + private: + QBrush mBackgroundBrush; + QFont mFont; + bool mSecond; + bool mDigital; + bool mDate; + bool mBorder; +}; + +#endif diff --git a/kdm/kfrontend/kdmconfig.cpp b/kdm/kfrontend/kdmconfig.cpp new file mode 100644 index 000000000..5d8d24e91 --- /dev/null +++ b/kdm/kfrontend/kdmconfig.cpp @@ -0,0 +1,177 @@ +/* + +Config for kdm + +Copyright (C) 1997, 1998 Steffen Hansen <[email protected]> +Copyright (C) 2000-2003 Oswald Buddenhagen <[email protected]> + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include "kdmconfig.h" +#include "kdm_greet.h" + +#include <kapplication.h> +#include <klocale.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/utsname.h> + +CONF_GREET_DEFS + +QString _stsFile; +bool _isLocal; +bool _authorized; + +static QString +GetCfgQStr( int id ) +{ + char *tmp = GetCfgStr( id ); + QString qs = QString::fromUtf8( tmp ); + free( tmp ); + return qs; +} + +static QStringList +GetCfgQStrList( int id ) +{ + int i, len; + char **tmp = GetCfgStrArr( id, &len ); + QStringList qsl; + for (i = 0; i < len - 1; i++) { + qsl.append( QString::fromUtf8( tmp[i] ) ); + free( tmp[i] ); + } + free( tmp ); + return qsl; +} + +// Based on kconfigbase.cpp +static QFont +Str2Font( const QString &aValue ) +{ + uint nFontBits; + QFont aRetFont; + QString chStr; + + QStringList sl = QStringList::split( QString::fromLatin1(","), aValue ); + + if (sl.count() == 1) { + /* X11 font spec */ + aRetFont = QFont( aValue ); + aRetFont.setRawMode( true ); + } else if (sl.count() == 10) { + /* qt3 font spec */ + aRetFont.fromString( aValue ); + } else if (sl.count() == 6) { + /* backward compatible kde2 font spec */ + aRetFont = QFont( sl[0], sl[1].toInt(), sl[4].toUInt() ); + + aRetFont.setStyleHint( (QFont::StyleHint)sl[2].toUInt() ); + + nFontBits = sl[5].toUInt(); + aRetFont.setItalic( (nFontBits & 0x01) != 0 ); + aRetFont.setUnderline( (nFontBits & 0x02) != 0 ); + aRetFont.setStrikeOut( (nFontBits & 0x04) != 0 ); + aRetFont.setFixedPitch( (nFontBits & 0x08) != 0 ); + aRetFont.setRawMode( (nFontBits & 0x20) != 0 ); + } + aRetFont.setStyleStrategy( (QFont::StyleStrategy) + (QFont::PreferMatch | + (_antiAliasing ? QFont::PreferAntialias : QFont::NoAntialias)) ); + + return aRetFont; +} + +extern "C" +void init_config( void ) +{ + CONF_GREET_INIT + + _isLocal = GetCfgInt( C_isLocal ); + _hasConsole = _hasConsole && _isLocal && GetCfgInt( C_hasConsole ); + _authorized = GetCfgInt( C_isAuthorized ); + + _stsFile = _dataDir + "/kdmsts"; + + // Greet String + char hostname[256], *ptr; + hostname[0] = '\0'; + if (!gethostname( hostname, sizeof(hostname) )) + hostname[sizeof(hostname)-1] = '\0'; + struct utsname tuname; + uname( &tuname ); + QString gst = _greetString; + _greetString = QString::null; + int i, j, l = gst.length(); + for (i = 0; i < l; i++) { + if (gst[i] == '%') { + switch (gst[++i].cell()) { + case '%': _greetString += gst[i]; continue; + case 'd': ptr = dname; break; + case 'h': ptr = hostname; break; + case 'n': ptr = tuname.nodename; + for (j = 0; ptr[j]; j++) + if (ptr[j] == '.') { + ptr[j] = 0; + break; + } + break; + case 's': ptr = tuname.sysname; break; + case 'r': ptr = tuname.release; break; + case 'm': ptr = tuname.machine; break; + default: _greetString += i18n("[fix kdmrc!]"); continue; + } + _greetString += QString::fromLocal8Bit( ptr ); + } else + _greetString += gst[i]; + } +} + + +/* out-of-place utility function */ +void +decodeSess( dpySpec *sess, QString &user, QString &loc ) +{ + if (sess->flags & isTTY) { + user = + i18n( "%1: TTY login", "%1: %n TTY logins", sess->count ) + .arg( sess->user ); + loc = +#ifdef HAVE_VTS + sess->vt ? + QString("vt%1").arg( sess->vt ) : +#endif + QString::fromLatin1( *sess->from ? sess->from : sess->display ); + } else { + user = + !sess->user ? + i18n("Unused") : + *sess->user ? + i18n("user: session type", "%1: %2") + .arg( sess->user ).arg( sess->session ) : + i18n("... host", "X login on %1").arg( sess->session ); + loc = +#ifdef HAVE_VTS + sess->vt ? + QString("%1, vt%2").arg( sess->display ).arg( sess->vt ) : +#endif + QString::fromLatin1( sess->display ); + } +} diff --git a/kdm/kfrontend/kdmconfig.h b/kdm/kfrontend/kdmconfig.h new file mode 100644 index 000000000..3077a2ce0 --- /dev/null +++ b/kdm/kfrontend/kdmconfig.h @@ -0,0 +1,55 @@ +/* + +Configuration for kdm + +Copyright (C) 1997, 1998, 2000 Steffen Hansen <[email protected]> +Copyright (C) 2000-2003 Oswald Buddenhagen <[email protected]> + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + + +#ifndef KDMCONFIG_H +#define KDMCONFIG_H + +#include <config.h> + +#include "config.ci" + +#ifdef __cplusplus + +#include <qstring.h> +#include <qstringlist.h> +#include <qfont.h> + +extern QString _stsFile; +extern bool _isLocal; +extern bool _authorized; + +CONF_GREET_CPP_DECLS + +// this file happens to be included everywhere, so just put it here +struct dpySpec; +void decodeSess( dpySpec *sess, QString &user, QString &loc ); + +extern "C" +#endif +void init_config( void ); + +CONF_GREET_C_DECLS + +#endif /* KDMCONFIG_H */ diff --git a/kdm/kfrontend/kdmctl.c b/kdm/kfrontend/kdmctl.c new file mode 100644 index 000000000..72e133162 --- /dev/null +++ b/kdm/kfrontend/kdmctl.c @@ -0,0 +1,236 @@ +/* + +KDM remote control application + +Copyright (C) 2004 Oswald Buddenhagen <[email protected]> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <sys/socket.h> +#include <sys/un.h> + +static int +openctl( int fd, int err, const char *ctl, const char *dpy ) +{ + struct sockaddr_un sa; + + sa.sun_family = AF_UNIX; + if (dpy) + snprintf( sa.sun_path, sizeof(sa.sun_path), + "%s/dmctl-%s/socket", ctl, dpy ); + else + snprintf( sa.sun_path, sizeof(sa.sun_path), + "%s/dmctl/socket", ctl ); + if (!connect( fd, (struct sockaddr *)&sa, sizeof(sa) )) + return 1; + if (err) + fprintf( stderr, "Cannot connect socket '%s'.\n", sa.sun_path ); + return 0; +} + +static const char * +readcfg( const char *cfg ) +{ + FILE *fp; + const char *ctl; + char *ptr, *ptr2; + char buf[1024]; + + if (!(fp = fopen( cfg, "r" ))) { + fprintf( stderr, + "Cannot open kdm config file '%s'.\n", + cfg ); + return 0; + } + ctl = "/var/run/xdmctl"; + while (fgets( buf, sizeof(buf), fp )) + if (!strncmp( buf, "FifoDir", 7 )) { + ptr = buf + 7; + while (*ptr && isspace( *ptr )) + ptr++; + if (*ptr++ != '=') + continue; + while (*ptr && isspace( *ptr )) + ptr++; + for (ptr2 = buf + strlen( buf ); + ptr2 > ptr && isspace( *(ptr2 - 1) ); + ptr2--); + *ptr2 = 0; + ctl = strdup( ptr ); + break; + } + fclose( fp ); + return ctl; +} + +static int +exe( int fd, const char *in, int len ) +{ + char buf[4096]; + + if (write( fd, in, len ) != len) { + fprintf( stderr, "Cannot send command\n" ); + return 1; + } + if ((len = read( fd, buf, sizeof(buf) )) <= 0) { + fprintf( stderr, "Cannot receive reply\n" ); + return 1; + } + if (len == sizeof(buf) && buf[sizeof(buf) - 1] != '\n') + fprintf( stderr, "Warning: reply is too long\n" ); + fwrite( buf, 1, len, stdout ); + if (len == sizeof(buf) && buf[sizeof(buf) - 1] != '\n') + puts( "[...]" ); + return 0; +} + +static int +run( int fd, char **argv ) +{ + unsigned len, l; + char buf[1024]; + + if (!*argv) + return exe( fd, "caps\n", 5 ); + if (!strcmp( *argv, "-" )) { + for (;;) { + if (isatty( 0 )) { + fputs( "> ", stdout ); + fflush( stdout ); + } + if (!fgets( buf, sizeof(buf), stdin )) + return 0; + if (exe( fd, buf, strlen( buf ) )) + return 1; + } + } else { + len = strlen( *argv ); + if (len >= sizeof(buf)) + goto bad; + memcpy( buf, *argv, len ); + while (*++argv) { + l = strlen( *argv ); + if (len + l + 1 >= sizeof(buf)) + goto bad; + buf[len++] = '\t'; + memcpy( buf + len, *argv, l ); + len += l; + } + buf[len++] = '\n'; + return exe( fd, buf, len ); + bad: + fprintf( stderr, "Command too long\n" ); + return 1; + } +} + +int +main( int argc, char **argv ) +{ + char *dpy = getenv( "DISPLAY" ); + const char *ctl = getenv( "DM_CONTROL" ); + const char *cfg = KDE_CONFDIR "/kdm/kdmrc"; + char *ptr; + int fd; + + (void)argc; + while (*++argv) { + ptr = *argv; + if (*ptr != '-' || !*(ptr + 1)) + break; + ptr++; + if (*ptr == '-') + ptr++; + if (!strcmp( ptr, "h" ) || !strcmp( ptr, "help" )) { + puts( +"Usage: kdmctl [options] [command [command arguments]]\n" +"\n" +"Options are:\n" +" -h -help This help message.\n" +" -g -global Use global control socket even if $DISPLAY is set\n" +" -d -display Override $DISPLAY\n" +" -s -sockets Override $DM_CONTROL\n" +" -c -config Use alternative kdm config file\n" +"\n" +"The directory in which the sockets are located is determined this way:\n" +"- the -s option is examined\n" +"- the $DM_CONTROL variable is examined\n" +"- the kdm config file is searched for the FifoDir key\n" +"- /var/run/xdmctl and /var/run are tried\n" +"\n" +"If $DISPLAY is set (or -d was specified) and -g was not specified, the\n" +"display-specific control socket will be used, otherwise the global one.\n" +"\n" +"Tokens in the command and the reply are tab-separated.\n" +"Command arguments can be specified as separate command line parameters,\n" +"in which case they are simply concatenated with tabs in between.\n" +"\n" +"If the command is '-', kdmctl reads commands from stdin.\n" +"The default command is 'caps'.\n" + ); + return 0; + } else if (!strcmp( ptr, "g" ) || !strcmp( ptr, "global" )) + dpy = 0; + else if (!strcmp( ptr, "d" ) || !strcmp( ptr, "display" )) { + if (!argv[1]) + goto needarg; + dpy = *++argv; + } else if (!strcmp( ptr, "s" ) || !strcmp( ptr, "sockets" )) { + if (!argv[1]) + goto needarg; + ctl = *++argv; + } else if (!strcmp( ptr, "c" ) || !strcmp( ptr, "config" )) { + if (!argv[1]) { + needarg: + fprintf( stderr, "Option '%s' needs argument.\n", + ptr ); + return 1; + } + cfg = *++argv; + } else { + fprintf( stderr, "Unknown option '%s'.\n", ptr ); + return 1; + } + } + if ((!ctl || !*ctl) && *cfg) + ctl = readcfg( cfg ); + if ((fd = socket( PF_UNIX, SOCK_STREAM, 0 )) < 0) { + fprintf( stderr, "Cannot create UNIX socket\n" ); + return 1; + } + if (dpy && (ptr = strchr( dpy, ':' )) && (ptr = strchr( ptr, '.' ))) + *ptr = 0; + if (ctl && *ctl) { + if (!openctl( fd, 1, ctl, dpy )) + return 1; + } else { + if (!openctl( fd, 0, "/var/run/xdmctl", dpy ) && + !openctl( fd, 0, "/var/run", dpy )) + { + fprintf( stderr, "No command socket found.\n" ); + return 1; + } + } + return run( fd, argv ); +} diff --git a/kdm/kfrontend/kdmshutdown.cpp b/kdm/kfrontend/kdmshutdown.cpp new file mode 100644 index 000000000..dfd8558e2 --- /dev/null +++ b/kdm/kfrontend/kdmshutdown.cpp @@ -0,0 +1,714 @@ +/* + +Shutdown dialog + +Copyright (C) 1997, 1998, 2000 Steffen Hansen <[email protected]> +Copyright (C) 2000-2003,2005 Oswald Buddenhagen <[email protected]> + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include "kdmshutdown.h" +#include "kdm_greet.h" + +#include <kapplication.h> +#include <kseparator.h> +#include <klocale.h> +#include <kpushbutton.h> +#include <kstdguiitem.h> +#include <kprocio.h> +#include <kdialog.h> +#include <kstandarddirs.h> +#include <kuser.h> + +#include <qcombobox.h> +#include <qvbuttongroup.h> +#include <qstyle.h> +#include <qlayout.h> +#include <qaccel.h> +#include <qpopupmenu.h> +#include <qcheckbox.h> +#include <qlineedit.h> +#include <qlabel.h> +#include <qdatetime.h> +#include <qlistview.h> +#include <qheader.h> +#include <qdatetime.h> + +#define KDmh KDialog::marginHint() +#define KDsh KDialog::spacingHint() + +#include <stdlib.h> + +int KDMShutdownBase::curPlugin = -1; +PluginList KDMShutdownBase::pluginList; + +KDMShutdownBase::KDMShutdownBase( int _uid, QWidget *_parent ) + : inherited( _parent ) + , box( new QVBoxLayout( this, KDmh, KDsh ) ) +#ifdef HAVE_VTS + , willShut( true ) +#endif + , mayNuke( false ) + , doesNuke( false ) + , mayOk( true ) + , maySched( false ) + , rootlab( 0 ) + , verify( 0 ) + , needRoot( -1 ) + , uid( _uid ) +{ +} + +KDMShutdownBase::~KDMShutdownBase() +{ + hide(); + delete verify; +} + +void +KDMShutdownBase::complete( QWidget *prevWidget ) +{ + QSizePolicy fp( QSizePolicy::Fixed, QSizePolicy::Fixed ); + + if (uid && + ((willShut && _allowShutdown == SHUT_ROOT) || + (mayNuke && _allowNuke == SHUT_ROOT))) + { + rootlab = new QLabel( i18n("Root authorization required."), this ); + box->addWidget( rootlab ); + if (curPlugin < 0) { + curPlugin = 0; + pluginList = KGVerify::init( _pluginsShutdown ); + } + verify = new KGStdVerify( this, this, + prevWidget, "root", + pluginList, KGreeterPlugin::Authenticate, + KGreeterPlugin::Shutdown ); + verify->selectPlugin( curPlugin ); + box->addLayout( verify->getLayout() ); + QAccel *accel = new QAccel( this ); + accel->insertItem( ALT+Key_A, 0 ); + connect( accel, SIGNAL(activated( int )), SLOT(slotActivatePlugMenu()) ); + } + + box->addWidget( new KSeparator( KSeparator::HLine, this ) ); + + QBoxLayout *hlay = new QHBoxLayout( box, KDsh ); + hlay->addStretch( 1 ); + if (mayOk) { + okButton = new KPushButton( KStdGuiItem::ok(), this ); + okButton->setSizePolicy( fp ); + okButton->setDefault( true ); + hlay->addWidget( okButton ); + hlay->addStretch( 1 ); + connect( okButton, SIGNAL(clicked()), SLOT(accept()) ); + } + if (maySched) { + KPushButton *schedButton = + new KPushButton( KGuiItem( i18n("&Schedule...") ), this ); + schedButton->setSizePolicy( fp ); + hlay->addWidget( schedButton ); + hlay->addStretch( 1 ); + connect( schedButton, SIGNAL(clicked()), SLOT(slotSched()) ); + } + cancelButton = new KPushButton( KStdGuiItem::cancel(), this ); + cancelButton->setSizePolicy( fp ); + if (!mayOk) + cancelButton->setDefault( true ); + hlay->addWidget( cancelButton ); + hlay->addStretch( 1 ); + connect( cancelButton, SIGNAL(clicked()), SLOT(reject()) ); + + updateNeedRoot(); +} + +void +KDMShutdownBase::slotActivatePlugMenu() +{ + if (needRoot) { + QPopupMenu *cmnu = verify->getPlugMenu(); + if (!cmnu) + return; + QSize sh( cmnu->sizeHint() / 2 ); + cmnu->exec( geometry().center() - QPoint( sh.width(), sh.height() ) ); + } +} + +void +KDMShutdownBase::accept() +{ + if (needRoot == 1) + verify->accept(); + else + accepted(); +} + +void +KDMShutdownBase::slotSched() +{ + done( Schedule ); +} + +void +KDMShutdownBase::updateNeedRoot() +{ + int nNeedRoot = uid && + (((willShut && _allowShutdown == SHUT_ROOT) || + (_allowNuke == SHUT_ROOT && doesNuke))); + if (verify && nNeedRoot != needRoot) { + if (needRoot == 1) + verify->abort(); + needRoot = nNeedRoot; + rootlab->setEnabled( needRoot ); + verify->setEnabled( needRoot ); + if (needRoot) + verify->start(); + } +} + +void +KDMShutdownBase::accepted() +{ + inherited::done( needRoot ? (int)Authed : (int)Accepted ); +} + +void +KDMShutdownBase::verifyPluginChanged( int id ) +{ + curPlugin = id; + adjustSize(); +} + +void +KDMShutdownBase::verifyOk() +{ + accepted(); +} + +void +KDMShutdownBase::verifyFailed() +{ + okButton->setEnabled( false ); + cancelButton->setEnabled( false ); +} + +void +KDMShutdownBase::verifyRetry() +{ + okButton->setEnabled( true ); + cancelButton->setEnabled( true ); +} + +void +KDMShutdownBase::verifySetUser( const QString & ) +{ +} + + +static void +doShutdown( int type, const char *os ) +{ + GSet( 1 ); + GSendInt( G_Shutdown ); + GSendInt( type ); + GSendInt( 0 ); + GSendInt( 0 ); + GSendInt( SHUT_FORCE ); + GSendInt( 0 ); /* irrelevant, will timeout immediately anyway */ + GSendStr( os ); + GSet( 0 ); +} + + + +KDMShutdown::KDMShutdown( int _uid, QWidget *_parent ) + : inherited( _uid, _parent ) +{ + QSizePolicy fp( QSizePolicy::Fixed, QSizePolicy::Fixed ); + + QHBoxLayout *hlay = new QHBoxLayout( box, KDsh ); + + howGroup = new QVButtonGroup( i18n("Shutdown Type"), this ); + hlay->addWidget( howGroup, 0, AlignTop ); + + QRadioButton *rb; + rb = new KDMRadioButton( i18n("&Turn off computer"), howGroup ); + rb->setChecked( true ); + rb->setFocus(); + + restart_rb = new KDMRadioButton( i18n("&Restart computer"), howGroup ); + + connect( rb, SIGNAL(doubleClicked()), SLOT(accept()) ); + connect( restart_rb, SIGNAL(doubleClicked()), SLOT(accept()) ); + + GSet( 1 ); + GSendInt( G_ListBootOpts ); + if (GRecvInt() == BO_OK) { /* XXX show dialog on failure */ + char **tlist = GRecvStrArr( 0 ); + int defaultTarget = GRecvInt(); + oldTarget = GRecvInt(); + QWidget *hlp = new QWidget( howGroup ); + targets = new QComboBox( hlp ); + for (int i = 0; tlist[i]; i++) + targets->insertItem( QString::fromLocal8Bit( tlist[i] ) ); + freeStrArr( tlist ); + targets->setCurrentItem( oldTarget == -1 ? defaultTarget : oldTarget ); + QHBoxLayout *hb = new QHBoxLayout( hlp, 0, KDsh ); + int spc = kapp->style().pixelMetric( QStyle::PM_ExclusiveIndicatorWidth ) + + howGroup->insideSpacing(); + hb->addSpacing( spc ); + hb->addWidget( targets ); + connect( targets, SIGNAL(activated( int )), SLOT(slotTargetChanged()) ); + } + GSet( 0 ); + + howGroup->setSizePolicy( fp ); + + schedGroup = new QGroupBox( i18n("Scheduling"), this ); + hlay->addWidget( schedGroup, 0, AlignTop ); + + le_start = new QLineEdit( schedGroup ); + QLabel *lab1 = new QLabel( le_start, i18n("&Start:"), schedGroup ); + + le_timeout = new QLineEdit( schedGroup ); + QLabel *lab2 = new QLabel( le_timeout, i18n("T&imeout:"), schedGroup ); + + cb_force = new QCheckBox( i18n("&Force after timeout"), schedGroup ); + if (_allowNuke != SHUT_NONE) { + connect( cb_force, SIGNAL(clicked()), SLOT(slotWhenChanged()) ); + mayNuke = true; + } else + cb_force->setEnabled( false ); + + QGridLayout *grid = new QGridLayout( schedGroup, 0, 0, KDmh, KDsh ); + grid->addRowSpacing( 0, schedGroup->fontMetrics().height() - 5 ); + grid->addWidget( lab1, 1, 0, AlignRight ); + grid->addWidget( le_start, 1, 1 ); + grid->addWidget( lab2, 2, 0, AlignRight ); + grid->addWidget( le_timeout, 2, 1 ); + grid->addMultiCellWidget( cb_force, 3,3, 0,1 ); + + schedGroup->setSizePolicy( fp ); + + le_start->setText( "0" ); + if (_defSdMode == SHUT_SCHEDULE) + le_timeout->setText( "-1" ); + else { + le_timeout->setText( "0" ); + if (_defSdMode == SHUT_FORCENOW && cb_force->isEnabled()) + cb_force->setChecked( true ); + } + + complete( schedGroup ); +} + +static int +get_date( const char *str ) +{ + KProcIO prc; + prc << "/bin/date" << "+%s" << "-d" << str; + prc.start( KProcess::Block, false ); + QString dstr; + if (prc.readln( dstr, false, 0 ) < 0) + return -1; + return dstr.toInt(); +} + +void +KDMShutdown::accept() +{ + if (le_start->text() == "0" || le_start->text() == "now") + sch_st = time( 0 ); + else if (le_start->text()[0] == '+') + sch_st = time( 0 ) + le_start->text().toInt(); + else if ((sch_st = get_date( le_start->text().latin1() )) < 0) { + MsgBox( errorbox, i18n("Entered start date is invalid.") ); + le_start->setFocus(); + return; + } + if (le_timeout->text() == "-1" || le_timeout->text().startsWith( "inf" )) + sch_to = TO_INF; + else if (le_timeout->text()[0] == '+') + sch_to = sch_st + le_timeout->text().toInt(); + else if ((sch_to = get_date( le_timeout->text().latin1() )) < 0) { + MsgBox( errorbox, i18n("Entered timeout date is invalid.") ); + le_timeout->setFocus(); + return; + } + + inherited::accept(); +} + +void +KDMShutdown::slotTargetChanged() +{ + restart_rb->setChecked( true ); +} + +void +KDMShutdown::slotWhenChanged() +{ + doesNuke = cb_force->isChecked(); + updateNeedRoot(); +} + +void +KDMShutdown::accepted() +{ + GSet( 1 ); + GSendInt( G_Shutdown ); + GSendInt( restart_rb->isChecked() ? SHUT_REBOOT : SHUT_HALT ); + GSendInt( sch_st ); + GSendInt( sch_to ); + GSendInt( cb_force->isChecked() ? SHUT_FORCE : SHUT_CANCEL ); + GSendInt( _allowShutdown == SHUT_ROOT ? 0 : -2 ); + GSendStr( (restart_rb->isChecked() && + targets && targets->currentItem() != oldTarget) ? + targets->currentText().local8Bit().data() : 0 ); + GSet( 0 ); + inherited::accepted(); +} + +void +KDMShutdown::scheduleShutdown( QWidget *_parent ) +{ + GSet( 1 ); + GSendInt( G_QueryShutdown ); + int how = GRecvInt(); + int start = GRecvInt(); + int timeout = GRecvInt(); + int force = GRecvInt(); + int uid = GRecvInt(); + char *os = GRecvStr(); + GSet( 0 ); + if (how) { + int ret = + KDMCancelShutdown( how, start, timeout, force, uid, os, + _parent ).exec(); + if (!ret) + return; + doShutdown( 0, 0 ); + uid = ret == Authed ? 0 : -1; + } else + uid = -1; + if (os) + free( os ); + KDMShutdown( uid, _parent ).exec(); +} + + +KDMRadioButton::KDMRadioButton( const QString &label, QWidget *parent ) + : inherited( label, parent ) +{ +} + +void +KDMRadioButton::mouseDoubleClickEvent( QMouseEvent * ) +{ + emit doubleClicked(); +} + + +KDMDelayedPushButton::KDMDelayedPushButton( const KGuiItem &item, + QWidget *parent, + const char *name ) + : inherited( item, parent, name ) + , pop( 0 ) +{ + connect( this, SIGNAL(pressed()), SLOT(slotPressed()) ); + connect( this, SIGNAL(released()), SLOT(slotReleased()) ); + connect( &popt, SIGNAL(timeout()), SLOT(slotTimeout()) ); +} + +void KDMDelayedPushButton::setPopup( QPopupMenu *p ) +{ + pop = p; + setIsMenuButton( p != 0 ); +} + +void KDMDelayedPushButton::slotPressed() +{ + if (pop) + popt.start( QApplication::startDragTime() ); +} + +void KDMDelayedPushButton::slotReleased() +{ + popt.stop(); +} + +void KDMDelayedPushButton::slotTimeout() +{ + popt.stop(); + pop->popup( mapToGlobal( rect().bottomLeft() ) ); + setDown( false ); +} + + +KDMSlimShutdown::KDMSlimShutdown( QWidget *_parent ) + : inherited( _parent ) + , targetList( 0 ) +{ + QHBoxLayout *hbox = new QHBoxLayout( this, KDmh, KDsh ); + + QFrame *lfrm = new QFrame( this ); + lfrm->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + hbox->addWidget( lfrm, AlignCenter ); + QLabel *icon = new QLabel( lfrm ); + icon->setPixmap( QPixmap( locate( "data", "kdm/pics/shutdown.jpg" ) ) ); + QVBoxLayout *iconlay = new QVBoxLayout( lfrm ); + iconlay->addWidget( icon ); + + QVBoxLayout *buttonlay = new QVBoxLayout( hbox, KDsh ); + + buttonlay->addStretch( 1 ); + + KPushButton *btnHalt = new + KPushButton( KGuiItem( i18n("&Turn Off Computer"), "exit" ), this ); + buttonlay->addWidget( btnHalt ); + connect( btnHalt, SIGNAL(clicked()), SLOT(slotHalt()) ); + + buttonlay->addSpacing( KDialog::spacingHint() ); + + KDMDelayedPushButton *btnReboot = new + KDMDelayedPushButton( KGuiItem( i18n("&Restart Computer"), "reload" ), this ); + buttonlay->addWidget( btnReboot ); + connect( btnReboot, SIGNAL(clicked()), SLOT(slotReboot()) ); + + GSet( 1 ); + GSendInt( G_ListBootOpts ); + if (GRecvInt() == BO_OK) { + targetList = GRecvStrArr( 0 ); + /*int def =*/ GRecvInt(); + int cur = GRecvInt(); + QPopupMenu *targets = new QPopupMenu( this ); + for (int i = 0; targetList[i]; i++) { + QString t( QString::fromLocal8Bit( targetList[i] ) ); + targets->insertItem( i == cur ? + i18n("current option in boot loader", + "%1 (current)").arg( t ) : + t, i ); + } + btnReboot->setPopup( targets ); + connect( targets, SIGNAL(activated(int)), SLOT(slotReboot(int)) ); + } + GSet( 0 ); + + buttonlay->addStretch( 1 ); + + if (_scheduledSd != SHUT_NEVER) { + KPushButton *btnSched = new + KPushButton( KGuiItem( i18n("&Schedule...") ), this ); + buttonlay->addWidget( btnSched ); + connect( btnSched, SIGNAL(clicked()), SLOT(slotSched()) ); + + buttonlay->addStretch( 1 ); + } + + buttonlay->addWidget( new KSeparator( this ) ); + + buttonlay->addSpacing( 0 ); + + KPushButton *btnBack = new KPushButton( KStdGuiItem::cancel(), this ); + buttonlay->addWidget( btnBack ); + connect( btnBack, SIGNAL(clicked()), SLOT(reject()) ); + + buttonlay->addSpacing( KDialog::spacingHint() ); +} + +KDMSlimShutdown::~KDMSlimShutdown() +{ + freeStrArr( targetList ); +} + +void +KDMSlimShutdown::slotSched() +{ + reject(); + KDMShutdown::scheduleShutdown(); +} + +void +KDMSlimShutdown::slotHalt() +{ + if (checkShutdown( SHUT_HALT, 0 )) + doShutdown( SHUT_HALT, 0 ); +} + +void +KDMSlimShutdown::slotReboot() +{ + if (checkShutdown( SHUT_REBOOT, 0 )) + doShutdown( SHUT_REBOOT, 0 ); +} + +void +KDMSlimShutdown::slotReboot( int opt ) +{ + if (checkShutdown( SHUT_REBOOT, targetList[opt] )) + doShutdown( SHUT_REBOOT, targetList[opt] ); +} + +bool +KDMSlimShutdown::checkShutdown( int type, const char *os ) +{ + reject(); + dpySpec *sess = fetchSessions( lstRemote | lstTTY ); + if (!sess && _allowShutdown != SHUT_ROOT) + return true; + int ret = KDMConfShutdown( -1, sess, type, os ).exec(); + disposeSessions( sess ); + if (ret == Schedule) { + KDMShutdown::scheduleShutdown(); + return false; + } + return ret; +} + +void +KDMSlimShutdown::externShutdown( int type, const char *os, int uid ) +{ + dpySpec *sess = fetchSessions( lstRemote | lstTTY ); + int ret = KDMConfShutdown( uid, sess, type, os ).exec(); + disposeSessions( sess ); + if (ret == Schedule) + KDMShutdown( uid ).exec(); + else if (ret) + doShutdown( type, os ); +} + + +KDMConfShutdown::KDMConfShutdown( int _uid, dpySpec *sess, int type, const char *os, + QWidget *_parent ) + : inherited( _uid, _parent ) +{ +#ifdef HAVE_VTS + if (type == SHUT_CONSOLE) + willShut = false; +#endif + box->addWidget( new QLabel( QString( "<qt><center><b><nobr>" + "%1%2" + "</nobr></b></center><br></qt>" ) + .arg( (type == SHUT_HALT) ? + i18n("Turn Off Computer") : +#ifdef HAVE_VTS + (type == SHUT_CONSOLE) ? + i18n("Switch to Console") : +#endif + i18n("Restart Computer") ) + .arg( os ? + i18n("<br>(Next boot: %1)") + .arg( QString::fromLocal8Bit( os ) ) : + QString::null ), + this ) ); + + if (sess) { + if (willShut && _scheduledSd != SHUT_NEVER) + maySched = true; + mayNuke = doesNuke = true; + if (_allowNuke == SHUT_NONE) + mayOk = false; + QLabel *lab = new QLabel( mayOk ? + i18n("Abort active sessions:") : + i18n("No permission to abort active sessions:"), + this ); + box->addWidget( lab ); + QListView *lv = new QListView( this ); + lv->setSelectionMode( QListView::NoSelection ); + lv->setAllColumnsShowFocus( true ); + lv->header()->setResizeEnabled( false ); + lv->addColumn( i18n("Session") ); + lv->addColumn( i18n("Location") ); + QListViewItem *itm; + int ns = 0; + QString user, loc; + do { + decodeSess( sess, user, loc ); + itm = new QListViewItem( lv, user, loc ); + sess = sess->next, ns++; + } while (sess); + int fw = lv->frameWidth() * 2; + lv->setFixedHeight( fw + lv->header()->height() + + itm->height() * (ns < 3 ? 3 : ns > 10 ? 10 : ns) ); + box->addWidget( lv ); + complete( lv ); + } else + complete( 0 ); +} + + +KDMCancelShutdown::KDMCancelShutdown( int how, int start, int timeout, + int force, int uid, const char *os, + QWidget *_parent ) + : inherited( -1, _parent ) +{ + if (force == SHUT_FORCE) { + if (_allowNuke == SHUT_NONE) + mayOk = false; + else if (_allowNuke == SHUT_ROOT) + mayNuke = doesNuke = true; + } + QLabel *lab = new QLabel( mayOk ? + i18n("Abort pending shutdown:") : + i18n("No permission to abort pending shutdown:"), + this ); + box->addWidget( lab ); + QDateTime qdt; + QString strt, end; + if (start < time( 0 )) + strt = i18n("now"); + else { + qdt.setTime_t( start ); + strt = qdt.toString( LocalDate ); + } + if (timeout == TO_INF) + end = i18n("infinite"); + else { + qdt.setTime_t( timeout ); + end = qdt.toString( LocalDate ); + } + QString trg = + i18n("Owner: %1" + "\nType: %2%5" + "\nStart: %3" + "\nTimeout: %4") + .arg( uid == -2 ? + i18n("console user") : + uid == -1 ? + i18n("control socket") : + KUser( uid ).loginName() ) + .arg( how == SHUT_HALT ? + i18n("turn off computer") : + i18n("restart computer") ) + .arg( strt ).arg( end ) + .arg( os ? + i18n("\nNext boot: %1").arg( QString::fromLocal8Bit( os ) ) : + QString::null ); + if (timeout != TO_INF) + trg += i18n("\nAfter timeout: %1") + .arg( force == SHUT_FORCE ? + i18n("abort all sessions") : + force == SHUT_FORCEMY ? + i18n("abort own sessions") : + i18n("cancel shutdown") ); + lab = new QLabel( trg, this ); + box->addWidget( lab ); + complete( 0 ); +} + +#include "kdmshutdown.moc" diff --git a/kdm/kfrontend/kdmshutdown.h b/kdm/kfrontend/kdmshutdown.h new file mode 100644 index 000000000..9020b1513 --- /dev/null +++ b/kdm/kfrontend/kdmshutdown.h @@ -0,0 +1,196 @@ +/* + +Shutdown dialog + +Copyright (C) 1997, 1998 Steffen Hansen <[email protected]> +Copyright (C) 2000-2003,2005 Oswald Buddenhagen <[email protected]> + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + + +#ifndef KDMSHUTDOWN_H +#define KDMSHUTDOWN_H + +#include "kdmconfig.h" // for HAVE_VTS +#include "kgverify.h" + +#include <kpushbutton.h> + +#include <qradiobutton.h> + +class QLabel; +class KPushButton; +class QButtonGroup; +class QGroupBox; +class QComboBox; +class QCheckBox; +class QLineEdit; + +enum { Authed = QDialog::Accepted + 1, Schedule }; + +class KDMShutdownBase : public FDialog, public KGVerifyHandler { + Q_OBJECT + typedef FDialog inherited; + + public: + KDMShutdownBase( int _uid, QWidget *_parent ); + virtual ~KDMShutdownBase(); + + protected slots: + virtual void accept(); + + protected: + virtual void accepted(); + + protected: + void updateNeedRoot(); + void complete( QWidget *prevWidget ); + + QVBoxLayout *box; +#ifdef HAVE_VTS + bool willShut; +#else + static const bool willShut = true; +#endif + bool mayNuke, doesNuke, mayOk, maySched; + + private slots: + void slotSched(); + void slotActivatePlugMenu(); + + private: + KPushButton *okButton, *cancelButton; + QLabel *rootlab; + KGStdVerify *verify; + int needRoot, uid; + + static int curPlugin; + static PluginList pluginList; + + public: // from KGVerifyHandler + virtual void verifyPluginChanged( int id ); + virtual void verifyOk(); + virtual void verifyFailed(); + virtual void verifyRetry(); + virtual void verifySetUser( const QString &user ); +}; + + +class KDMShutdown : public KDMShutdownBase { + Q_OBJECT + typedef KDMShutdownBase inherited; + + public: + KDMShutdown( int _uid, QWidget *_parent = 0 ); + static void scheduleShutdown( QWidget *_parent = 0 ); + + protected slots: + virtual void accept(); + + protected: + virtual void accepted(); + + private slots: + void slotTargetChanged(); + void slotWhenChanged(); + + private: + QButtonGroup *howGroup; + QGroupBox *schedGroup; + QRadioButton *restart_rb; + QLineEdit *le_start, *le_timeout; + QCheckBox *cb_force; + QComboBox *targets; + int oldTarget; + int sch_st, sch_to; + +}; + +class KDMRadioButton : public QRadioButton { + Q_OBJECT + typedef QRadioButton inherited; + + public: + KDMRadioButton( const QString &label, QWidget *parent ); + + private: + virtual void mouseDoubleClickEvent( QMouseEvent * ); + + signals: + void doubleClicked(); + +}; + +class KDMDelayedPushButton : public KPushButton { + Q_OBJECT + typedef KPushButton inherited; + + public: + KDMDelayedPushButton( const KGuiItem &item, QWidget *parent, const char *name = 0 ); + void setPopup( QPopupMenu *pop ); + + private slots: + void slotTimeout(); + void slotPressed(); + void slotReleased(); + + private: + QPopupMenu *pop; + QTimer popt; +}; + +class KDMSlimShutdown : public FDialog { + Q_OBJECT + typedef FDialog inherited; + + public: + KDMSlimShutdown( QWidget *_parent = 0 ); + ~KDMSlimShutdown(); + static void externShutdown( int type, const char *os, int uid ); + + private slots: + void slotHalt(); + void slotReboot(); + void slotReboot( int ); + void slotSched(); + + private: + bool checkShutdown( int type, const char *os ); + char **targetList; + +}; + +class KDMConfShutdown : public KDMShutdownBase { + Q_OBJECT + typedef KDMShutdownBase inherited; + + public: + KDMConfShutdown( int _uid, struct dpySpec *sess, int type, const char *os, + QWidget *_parent = 0 ); +}; + +class KDMCancelShutdown : public KDMShutdownBase { + Q_OBJECT + typedef KDMShutdownBase inherited; + + public: + KDMCancelShutdown( int how, int start, int timeout, int force, int uid, + const char *os, QWidget *_parent ); +}; + +#endif /* KDMSHUTDOWN_H */ diff --git a/kdm/kfrontend/kfdialog.cpp b/kdm/kfrontend/kfdialog.cpp new file mode 100644 index 000000000..e3bb9d9c8 --- /dev/null +++ b/kdm/kfrontend/kfdialog.cpp @@ -0,0 +1,149 @@ +/* + +Dialog class that handles input focus in absence of a wm + +Copyright (C) 1997, 1998 Steffen Hansen <[email protected]> +Copyright (C) 2000-2003 Oswald Buddenhagen <[email protected]> + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include "kfdialog.h" +#include "kdmconfig.h" + +#include <klocale.h> +#include <kpushbutton.h> +#include <kstdguiitem.h> + +#include <qlabel.h> +#include <qlayout.h> +#include <qapplication.h> +#include <qcursor.h> + +FDialog::FDialog( QWidget *parent, bool framed ) + : inherited( parent, 0, true/*, framed ? 0 : WStyle_NoBorder*/ ) +{ + if (framed) { + winFrame = new QFrame( this, 0, WNoAutoErase ); + winFrame->setFrameStyle( QFrame::WinPanel | QFrame::Raised ); + winFrame->setLineWidth( 2 ); + } else + winFrame = 0; +} + +void +FDialog::resizeEvent( QResizeEvent *e ) +{ + inherited::resizeEvent( e ); + if (winFrame) { + winFrame->resize( size() ); + winFrame->erase(); + } +} + +void +FDialog::adjustGeometry() +{ + QDesktopWidget *dsk = qApp->desktop(); + + if (_greeterScreen < 0) + _greeterScreen = _greeterScreen == -2 ? + dsk->screenNumber( QPoint( dsk->width() - 1, 0 ) ) : + dsk->screenNumber( QPoint( 0, 0 ) ); + + QRect scr = dsk->screenGeometry( _greeterScreen ); + if (!winFrame) + setFixedSize( scr.size() ); + else { + setMaximumSize( scr.size() * .9 ); + adjustSize(); + } + + if (parentWidget()) + return; + + QRect grt( rect() ); + if (winFrame) { + unsigned x = 50, y = 50; + sscanf( _greeterPos, "%u,%u", &x, &y ); + grt.moveCenter( QPoint( scr.x() + scr.width() * x / 100, + scr.y() + scr.height() * y / 100 ) ); + int di; + if ((di = scr.right() - grt.right()) < 0) + grt.moveBy( di, 0 ); + if ((di = scr.left() - grt.left()) > 0) + grt.moveBy( di, 0 ); + if ((di = scr.bottom() - grt.bottom()) < 0) + grt.moveBy( 0, di ); + if ((di = scr.top() - grt.top()) > 0) + grt.moveBy( 0, di ); + setGeometry( grt ); + } + + if (dsk->screenNumber( QCursor::pos() ) != _greeterScreen) + QCursor::setPos( grt.center() ); +} + +struct WinList { + struct WinList *next; + QWidget *win; +}; + +int +FDialog::exec() +{ + static WinList *wins; + WinList *win; + + win = new WinList; + win->win = this; + win->next = wins; + wins = win; + show(); + setActiveWindow(); + inherited::exec(); + hide(); + wins = win->next; + delete win; + if (wins) + wins->win->setActiveWindow(); + return result(); +} + +void +FDialog::box( QWidget *parent, QMessageBox::Icon type, const QString &text ) +{ + KFMsgBox dlg( parent, type, text.stripWhiteSpace() ); + dlg.exec(); +} + +KFMsgBox::KFMsgBox( QWidget *parent, QMessageBox::Icon type, const QString &text ) + : inherited( parent ) +{ + QLabel *label1 = new QLabel( this ); + label1->setPixmap( QMessageBox::standardIcon( type ) ); + QLabel *label2 = new QLabel( text, this ); + KPushButton *button = new KPushButton( KStdGuiItem::ok(), this ); + button->setDefault( true ); + button->setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ) ); + connect( button, SIGNAL(clicked()), SLOT(accept()) ); + + QGridLayout *grid = new QGridLayout( this, 2, 2, 10 ); + grid->addWidget( label1, 0, 0, Qt::AlignCenter ); + grid->addWidget( label2, 0, 1, Qt::AlignCenter ); + grid->addMultiCellWidget( button, 1,1, 0,1, Qt::AlignCenter ); +} diff --git a/kdm/kfrontend/kfdialog.h b/kdm/kfrontend/kfdialog.h new file mode 100644 index 000000000..7ab456f22 --- /dev/null +++ b/kdm/kfrontend/kfdialog.h @@ -0,0 +1,63 @@ +/* + +Dialog class that handles input focus in absence of a wm + +Copyright (C) 1997, 1998 Steffen Hansen <[email protected]> +Copyright (C) 2000-2003 Oswald Buddenhagen <[email protected]> + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + + +#ifndef FDIALOG_H +#define FDIALOG_H + +#include <qdialog.h> +#include <qmessagebox.h> + +class QFrame; + +class FDialog : public QDialog { + typedef QDialog inherited; + + public: + FDialog( QWidget *parent = 0, bool framed = true ); + virtual int exec(); + + static void box( QWidget *parent, QMessageBox::Icon type, + const QString &text ); +#define errorbox QMessageBox::Critical +#define sorrybox QMessageBox::Warning +#define infobox QMessageBox::Information + void MsgBox( QMessageBox::Icon typ, const QString &msg ) { box( this, typ, msg ); } + + protected: + virtual void resizeEvent( QResizeEvent *e ); + void adjustGeometry(); + + private: + QFrame *winFrame; +}; + +class KFMsgBox : public FDialog { + typedef FDialog inherited; + + public: + KFMsgBox( QWidget *parent, QMessageBox::Icon type, const QString &text ); +}; + +#endif /* FDIALOG_H */ diff --git a/kdm/kfrontend/kgapp.cpp b/kdm/kfrontend/kgapp.cpp new file mode 100644 index 000000000..711853c37 --- /dev/null +++ b/kdm/kfrontend/kgapp.cpp @@ -0,0 +1,253 @@ +/* + +Greeter module for xdm + +Copyright (C) 1997, 1998 Steffen Hansen <[email protected]> +Copyright (C) 2000-2003 Oswald Buddenhagen <[email protected]> + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include "kdm_greet.h" +#include "kdmshutdown.h" +#include "kdmconfig.h" +#include "kgapp.h" +#include "kgreeter.h" +#ifdef XDMCP +# include "kchooser.h" +#endif + +#include <kprocess.h> +#include <kcmdlineargs.h> +#include <kcrash.h> +#include <kstandarddirs.h> +#include <ksimpleconfig.h> + +#include <qtimer.h> +#include <qcursor.h> +#include <qpalette.h> + +#include <stdlib.h> // free(), exit() +#include <unistd.h> // alarm() + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> +#include <X11/cursorfont.h> + +extern "C" { + +static void +sigAlarm( int ) +{ + exit( EX_RESERVER_DPY ); +} + +} + +GreeterApp::GreeterApp() +{ + pingInterval = _isLocal ? 0 : _pingInterval; + if (pingInterval) { + struct sigaction sa; + sigemptyset( &sa.sa_mask ); + sa.sa_flags = 0; + sa.sa_handler = sigAlarm; + sigaction( SIGALRM, &sa, 0 ); + alarm( pingInterval * 70 ); // sic! give the "proper" pinger enough time + startTimer( pingInterval * 60000 ); + } +} + +void +GreeterApp::timerEvent( QTimerEvent * ) +{ + alarm( 0 ); + if (!PingServer( qt_xdisplay() )) + ::exit( EX_RESERVER_DPY ); + alarm( pingInterval * 70 ); // sic! give the "proper" pinger enough time +} + +bool +GreeterApp::x11EventFilter( XEvent * ev ) +{ + KeySym sym; + + switch (ev->type) { + case FocusIn: + case FocusOut: + // Hack to tell dialogs to take focus when the keyboard is grabbed + ev->xfocus.mode = NotifyNormal; + break; + case KeyPress: + sym = XLookupKeysym( &ev->xkey, 0 ); + if (sym != XK_Return && !IsModifierKey( sym )) + emit activity(); + break; + case ButtonPress: + emit activity(); + /* fall through */ + case ButtonRelease: + // Hack to let the RMB work as LMB + if (ev->xbutton.button == 3) + ev->xbutton.button = 1; + /* fall through */ + case MotionNotify: + if (ev->xbutton.state & Button3Mask) + ev->xbutton.state = (ev->xbutton.state & ~Button3Mask) | Button1Mask; + break; + } + return false; +} + +extern bool kde_have_kipc; + +extern "C" { + +static int +xIOErr( Display * ) +{ + exit( EX_RESERVER_DPY ); +} + +void +kg_main( const char *argv0 ) +{ + static char *argv[] = { (char *)"kdmgreet", 0 }; + KCmdLineArgs::init( 1, argv, *argv, 0, 0, 0, true ); + + kde_have_kipc = false; + KApplication::disableAutoDcopRegistration(); + KCrash::setSafer( true ); + GreeterApp app; + XSetIOErrorHandler( xIOErr ); + + Display *dpy = qt_xdisplay(); + + if (!_GUIStyle.isEmpty()) + app.setStyle( _GUIStyle ); + + _colorScheme = locate( "data", "kdisplay/color-schemes/" + _colorScheme + ".kcsrc" ); + if (!_colorScheme.isEmpty()) { + KSimpleConfig config( _colorScheme, true ); + config.setGroup( "Color Scheme" ); + app.setPalette( app.createApplicationPalette( &config, 7 ) ); + } + + app.setFont( _normalFont ); + + setup_modifiers( dpy, _numLockStatus ); + SecureDisplay( dpy ); + KProcess *proc = 0; + if (!_grabServer) { + if (_useBackground) { + proc = new KProcess; + *proc << QCString( argv0, strrchr( argv0, '/' ) - argv0 + 2 ) + "krootimage"; + *proc << _backgroundCfg; + proc->start(); + } + GSendInt( G_SetupDpy ); + GRecvInt(); + } + + GSendInt( G_Ready ); + + setCursor( dpy, app.desktop()->winId(), XC_left_ptr ); + + for (;;) { + int rslt, cmd = GRecvInt(); + + if (cmd == G_ConfShutdown) { + int how = GRecvInt(), uid = GRecvInt(); + char *os = GRecvStr(); + KDMSlimShutdown::externShutdown( how, os, uid ); + if (os) + free( os ); + GSendInt( G_Ready ); + _autoLoginDelay = 0; + continue; + } + + if (cmd == G_ErrorGreet) { + if (KGVerify::handleFailVerify( qApp->desktop()->screen( _greeterScreen ) )) + break; + _autoLoginDelay = 0; + cmd = G_Greet; + } + + KProcess *proc2 = 0; + app.setOverrideCursor( Qt::WaitCursor ); + FDialog *dialog; +#ifdef XDMCP + if (cmd == G_Choose) { + dialog = new ChooserDlg; + GSendInt( G_Ready ); /* tell chooser to go into async mode */ + GRecvInt(); /* ack */ + } else +#endif + { + if ((cmd != G_GreetTimed && !_autoLoginAgain) || + _autoLoginUser.isEmpty()) + _autoLoginDelay = 0; + if (_useTheme && !_theme.isEmpty()) { + KThemedGreeter *tgrt; + dialog = tgrt = new KThemedGreeter; + if (!tgrt->isOK()) { + delete tgrt; + dialog = new KStdGreeter; + } + } else + dialog = new KStdGreeter; + if (*_preloader) { + proc2 = new KProcess; + *proc2 << _preloader; + proc2->start(); + } + } + app.restoreOverrideCursor(); + Debug( "entering event loop\n" ); + rslt = dialog->exec(); + Debug( "left event loop\n" ); + delete dialog; + delete proc2; +#ifdef XDMCP + switch (rslt) { + case ex_greet: + GSendInt( G_DGreet ); + continue; + case ex_choose: + GSendInt( G_DChoose ); + continue; + default: + break; + } +#endif + break; + } + + KGVerify::done(); + + delete proc; + UnsecureDisplay( dpy ); + restore_modifiers(); + + XSetInputFocus( qt_xdisplay(), PointerRoot, PointerRoot, CurrentTime ); +} + +} // extern "C" + +#include "kgapp.moc" diff --git a/kdm/kfrontend/kgapp.h b/kdm/kfrontend/kgapp.h new file mode 100644 index 000000000..9174cd5c8 --- /dev/null +++ b/kdm/kfrontend/kgapp.h @@ -0,0 +1,49 @@ +/* + +Greeter module for xdm + +Copyright (C) 1997, 1998 Steffen Hansen <[email protected]> +Copyright (C) 2000-2003 Oswald Buddenhagen <[email protected]> + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + + +#ifndef KGAPP_H +#define KGAPP_H + +#include <kapplication.h> + +class GreeterApp : public KApplication { + Q_OBJECT + typedef KApplication inherited; + + public: + GreeterApp(); + virtual bool x11EventFilter( XEvent * ); + + protected: + virtual void timerEvent( QTimerEvent * ); + + signals: + void activity(); + + private: + int pingInterval; +}; + +#endif /* KGAPP_H */ diff --git a/kdm/kfrontend/kgdialog.cpp b/kdm/kfrontend/kgdialog.cpp new file mode 100644 index 000000000..636b20dec --- /dev/null +++ b/kdm/kfrontend/kgdialog.cpp @@ -0,0 +1,238 @@ +/* + +Base class for various kdm greeter dialogs + +Copyright (C) 1997, 1998 Steffen Hansen <[email protected]> +Copyright (C) 2000-2004 Oswald Buddenhagen <[email protected]> + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include "kgdialog.h" +#include "kgverify.h" +#include "kconsole.h" +#include "kdmshutdown.h" +#include "kdm_greet.h" + +#include <klocale.h> + +#include <qaccel.h> +#include <qlayout.h> +#include <qpushbutton.h> +#include <qpopupmenu.h> +#include <qapplication.h> + +#include <stdlib.h> + +KGDialog::KGDialog( bool themed ) : inherited( 0, !themed ) +{ +#ifdef WITH_KDM_XCONSOLE + consoleView = _showLog ? new KConsole( this ) : 0; +#endif + + optMenu = 0; + verify = 0; +} + +void +#ifdef XDMCP +KGDialog::completeMenu( int _switchIf, int _switchCode, const QString &_switchMsg, int _switchAccel ) +#else +KGDialog::completeMenu() +#endif +{ +#ifdef HAVE_VTS + if (_isLocal) { + dpyMenu = new QPopupMenu( this ); + int id = inserten( i18n("Sw&itch User"), ALT+Key_I, dpyMenu ); + connect( dpyMenu, SIGNAL(activated( int )), + SLOT(slotDisplaySelected( int )) ); + connect( dpyMenu, SIGNAL(aboutToShow()), + SLOT(slotPopulateDisplays()) ); + QAccel *accel = new QAccel( this ); + accel->insertItem( ALT+CTRL+Key_Insert, id ); + connect( accel, SIGNAL(activated( int )), SLOT(slotActivateMenu( int )) ); + } +#endif + + if (_allowClose) + inserten( _isLocal ? i18n("R&estart X Server") : i18n("Clos&e Connection"), + ALT+Key_E, SLOT(slotExit()) ); + +#ifdef XDMCP + if (_isLocal && _loginMode != _switchIf) { + switchCode = _switchCode; + inserten( _switchMsg, _switchAccel, SLOT(slotSwitch()) ); + } +#endif + + if (_hasConsole) + inserten( i18n("Co&nsole Login"), ALT+Key_N, SLOT(slotConsole()) ); + + if (_allowShutdown != SHUT_NONE) { + inserten( i18n("&Shutdown..."), ALT+Key_S, SLOT(slotShutdown( int )) ); + QAccel *accel = new QAccel( this ); + accel->insertItem( ALT+CTRL+Key_Delete ); + connect( accel, SIGNAL(activated( int )), SLOT(slotShutdown( int )) ); + accel = new QAccel( this ); + accel->insertItem( SHIFT+ALT+CTRL+Key_PageUp, SHUT_REBOOT ); + connect( accel, SIGNAL(activated( int )), SLOT(slotShutdown( int )) ); + accel = new QAccel( this ); + accel->insertItem( SHIFT+ALT+CTRL+Key_PageDown, SHUT_HALT ); + connect( accel, SIGNAL(activated( int )), SLOT(slotShutdown( int )) ); + } +} + +void +KGDialog::ensureMenu() +{ + if (!optMenu) { + optMenu = new QPopupMenu( this ); + optMenu->setCheckable( false ); + needSep = false; + } else if (needSep) { + optMenu->insertSeparator(); + needSep = false; + } +} + +void +KGDialog::inserten( const QString& txt, int accel, const char *member ) +{ + ensureMenu(); + optMenu->insertItem( txt, this, member, accel ); +} + +int +KGDialog::inserten( const QString& txt, int accel, QPopupMenu *cmnu ) +{ + ensureMenu(); + int id = optMenu->insertItem( txt, cmnu ); + optMenu->setAccel( accel, id ); + optMenu->connectItem( id, this, SLOT(slotActivateMenu( int )) ); + optMenu->setItemParameter( id, id ); + return id; +} + +void +KGDialog::slotActivateMenu( int id ) +{ + QPopupMenu *cmnu = optMenu->findItem( id )->popup(); + QSize sh( cmnu->sizeHint() / 2 ); + cmnu->exec( geometry().center() - QPoint( sh.width(), sh.height() ) ); +} + +void +KGDialog::slotExit() +{ + if (verify) + verify->abort(); + ::exit( EX_RESERVER_DPY ); +} + +void +KGDialog::slotSwitch() +{ +#ifdef XDMCP + // workaround for Qt bug + QTimer::singleShot( 0, this, SLOT(slotReallySwitch()) ); +#endif +} + +void +KGDialog::slotReallySwitch() +{ +#ifdef XDMCP + done( switchCode ); +#endif +} + +void +KGDialog::slotConsole() +{ +#ifdef HAVE_VTS + dpySpec *sess = fetchSessions( 0 ); + if (sess) { + if (verify) + verify->suspend(); + int ret = KDMConfShutdown( -1, sess, SHUT_CONSOLE, 0 ).exec(); + if (verify) + verify->resume(); + disposeSessions( sess ); + if (!ret) + return; + } +#else + if (verify) + verify->abort(); +#endif + GSet( 1 ); + GSendInt( G_Console ); + GSet( 0 ); +} + +void +KGDialog::slotShutdown( int id ) +{ + if (verify) + verify->suspend(); + if (id < 0) { + if (_scheduledSd == SHUT_ALWAYS) + KDMShutdown::scheduleShutdown( this ); + else + KDMSlimShutdown( this ).exec(); + } else + KDMSlimShutdown::externShutdown( id, 0, -1 ); + if (verify) + verify->resume(); +} + +void +KGDialog::slotDisplaySelected( int vt ) +{ +#ifdef HAVE_VTS + GSet( 1 ); + GSendInt( G_Activate ); + GSendInt( vt ); + GSet( 0 ); +#else + (void)vt; +#endif +} + +void +KGDialog::slotPopulateDisplays() +{ +#ifdef HAVE_VTS + dpyMenu->clear(); + dpySpec *sessions = fetchSessions( lstPassive | lstTTY ); + QString user, loc; + for (dpySpec *sess = sessions; sess; sess = sess->next) { + decodeSess( sess, user, loc ); + int id = dpyMenu->insertItem( + i18n("session (location)", "%1 (%2)").arg( user ).arg( loc ), + sess->vt ? sess->vt : -1 ); + if (!sess->vt) + dpyMenu->setItemEnabled( id, false ); + if (sess->flags & isSelf) + dpyMenu->setItemChecked( id, true ); + } + disposeSessions( sessions ); +#endif +} + +#include "kgdialog.moc" diff --git a/kdm/kfrontend/kgdialog.h b/kdm/kfrontend/kgdialog.h new file mode 100644 index 000000000..05a4be5cf --- /dev/null +++ b/kdm/kfrontend/kgdialog.h @@ -0,0 +1,87 @@ +/* + +Base class for various kdm greeter dialogs + +Copyright (C) 1997, 1998 Steffen Hansen <[email protected]> +Copyright (C) 2000-2004 Oswald Buddenhagen <[email protected]> + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + + +#ifndef KGDIALOG_H +#define KGDIALOG_H + +#include <config.h> // for WITH_KDM_XCONSOLE + +#include "kdmconfig.h" +#include "kfdialog.h" + +class QPopupMenu; +class QGridLayout; +class KConsole; +class KGVerify; + +#define ex_exit 1 +#define ex_greet 2 +#define ex_choose 3 + +class KGDialog : public FDialog { + Q_OBJECT + typedef FDialog inherited; + + public: + KGDialog( bool themed = false ); + + public slots: + void slotActivateMenu( int id ); + void slotExit(); + void slotSwitch(); + void slotReallySwitch(); + void slotConsole(); + void slotShutdown( int id ); + + protected: +#ifdef XDMCP + void completeMenu( int _switchIf, int _switchCode, const QString &_switchMsg, int _switchAccel ); +#else + void completeMenu(); +#endif + void inserten( const QString& txt, int accel, const char *member ); + int inserten( const QString& txt, int accel, QPopupMenu *cmnu ); + + bool needSep; + QPopupMenu *optMenu; + KGVerify *verify; +#ifdef WITH_KDM_XCONSOLE + KConsole *consoleView; +#endif + + private slots: + void slotDisplaySelected( int vt ); + void slotPopulateDisplays(); + + private: + void ensureMenu(); + +#ifdef HAVE_VTS + QPopupMenu *dpyMenu; +#endif + int switchCode; +}; + +#endif /* KGDIALOG_H */ diff --git a/kdm/kfrontend/kgreeter.cpp b/kdm/kfrontend/kgreeter.cpp new file mode 100644 index 000000000..574f4e340 --- /dev/null +++ b/kdm/kfrontend/kgreeter.cpp @@ -0,0 +1,998 @@ +/* + +Greeter widget for kdm + +Copyright (C) 1997, 1998, 2000 Steffen Hansen <[email protected]> +Copyright (C) 2000-2004 Oswald Buddenhagen <[email protected]> + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include "kgreeter.h" +#include "kconsole.h" +#include "kdmconfig.h" +#include "kdmclock.h" +#include "kdm_greet.h" +#include "themer/kdmthemer.h" +#include "themer/kdmitem.h" +#include "themer/kdmlabel.h" + +#include <kapplication.h> +#include <klocale.h> +#include <kstandarddirs.h> +#include <kseparator.h> +#include <klistview.h> +#include <ksimpleconfig.h> +#include <kstringhandler.h> + +#undef Unsorted // x headers suck - make qdir.h work with --enable-final +#include <qdir.h> +#include <qfile.h> +#include <qbuffer.h> +#include <qmemarray.h> +#include <qimage.h> +#include <qmovie.h> +#include <qpopupmenu.h> +#include <qtimer.h> +#include <qheader.h> +#include <qstyle.h> +#include <qlayout.h> +#include <qlabel.h> +#include <qpushbutton.h> +#include <qtooltip.h> +#include <qaccel.h> +#include <qeventloop.h> + +#include <pwd.h> +#include <grp.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> + +#include <X11/Xlib.h> + +class UserListView : public KListView { + public: + UserListView( QWidget *parent = 0, const char *name = 0 ) + : KListView( parent, name ) + , cachedSizeHint( -1, 0 ) + { + setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Ignored ); + header()->hide(); + addColumn( QString::null ); + setColumnAlignment( 0, AlignVCenter ); + setResizeMode( QListView::LastColumn ); + } + + mutable QSize cachedSizeHint; + + protected: + virtual QSize sizeHint() const + { + if (!cachedSizeHint.isValid()) { + constPolish(); + uint maxw = 0; + for (QListViewItem *itm = firstChild(); itm; itm = itm->nextSibling()) { + uint thisw = itm->width( fontMetrics(), this, 0 ); + if (thisw > maxw) + maxw = thisw; + } + cachedSizeHint.setWidth( + style().pixelMetric( QStyle::PM_ScrollBarExtent ) + + frameWidth() * 2 + maxw ); + } + return cachedSizeHint; + } +}; + + +int KGreeter::curPlugin = -1; +PluginList KGreeter::pluginList; + +KGreeter::KGreeter( bool framed ) + : inherited( framed ) + , dName( dname ) + , userView( 0 ) + , userList( 0 ) + , nNormals( 0 ) + , nSpecials( 0 ) + , curPrev( -1 ) + , curSel( -1 ) + , prevValid( true ) + , needLoad( false ) +{ + stsFile = new KSimpleConfig( _stsFile ); + stsFile->setGroup( "PrevUser" ); + + if (_userList) { + userView = new UserListView( this ); + connect( userView, SIGNAL(clicked( QListViewItem * )), + SLOT(slotUserClicked( QListViewItem * )) ); + connect( userView, SIGNAL(doubleClicked( QListViewItem * )), + SLOT(accept()) ); + } + if (_userCompletion) + userList = new QStringList; + if (userView || userList) + insertUsers(); + + sessMenu = new QPopupMenu( this ); + connect( sessMenu, SIGNAL(activated( int )), + SLOT(slotSessionSelected( int )) ); + insertSessions(); + + if (curPlugin < 0) { + curPlugin = 0; + pluginList = KGVerify::init( _pluginsLogin ); + } +} + +KGreeter::~KGreeter() +{ + hide(); + delete userList; + delete verify; + delete stsFile; +} + +class UserListViewItem : public KListViewItem { + public: + UserListViewItem( UserListView *parent, const QString &text, + const QPixmap &pixmap, const QString &username ) + : KListViewItem( parent ) + , login( username ) + { + setPixmap( 0, pixmap ); + setMultiLinesEnabled( true ); + setText( 0, text ); + parent->cachedSizeHint.setWidth( -1 ); + } + + QString login; +}; + +#define FILE_LIMIT_ICON 20 +#define FILE_LIMIT_IMAGE 200 + +void +KGreeter::insertUser( const QImage &default_pix, + const QString &username, struct passwd *ps ) +{ + if (userList) + userList->append( username ); + if (!userView) + return; + + int dp = 0, nd = 0; + if (_faceSource == FACE_USER_ONLY || + _faceSource == FACE_PREFER_USER) + dp = 1; + if (_faceSource != FACE_USER_ONLY && + _faceSource != FACE_ADMIN_ONLY) + nd = 1; + QImage p; + do { + dp ^= 1; + QCString fn = !dp ? + QCString( ps->pw_dir ) + '/' : + QFile::encodeName( _faceDir + '/' + username ); + fn += ".face.icon"; + int fd, ico; + if ((fd = open( fn.data(), O_RDONLY | O_NONBLOCK )) < 0) { + fn.truncate( fn.length() - 5 ); + if ((fd = open( fn.data(), O_RDONLY | O_NONBLOCK )) < 0) + continue; + ico = 0; + } else + ico = 1; + QFile f; + f.open( IO_ReadOnly, fd ); + int fs = f.size(); + if (fs > (ico ? FILE_LIMIT_ICON : FILE_LIMIT_IMAGE) * 1000) { + LogWarn( "%s exceeds file size limit (%dkB)\n", + fn.data(), ico ? FILE_LIMIT_ICON : FILE_LIMIT_IMAGE ); + continue; + } + QByteArray fc( fs ); + int rfs = f.readBlock( fc.data(), fs ); + ::close( fd ); + fc.resize( rfs > 0 ? rfs : 0 ); + QBuffer buf( fc ); + buf.open( IO_ReadOnly ); + QImageIO ir; + ir.setIODevice( &buf ); + if (!ir.read()) { + LogInfo( "%s is no valid image\n", fn.data() ); + continue; + } + p = ir.image(); + QSize ns( 48, 48 ); + if (p.size() != ns) + p = p.convertDepth( 32 ).smoothScale( ns, QImage::ScaleMin ); + goto gotit; + } while (--nd >= 0); + p = default_pix; + gotit: + QString realname = KStringHandler::from8Bit( ps->pw_gecos ); + realname.truncate( realname.find( ',' ) ); + if (realname.isEmpty() || realname == username) + new UserListViewItem( userView, username, QPixmap( p ), username ); + else { + realname.append( "\n" ).append( username ); + new UserListViewItem( userView, realname, QPixmap( p ), username ); + } +} + +class KCStringList : public QValueList<QCString> { + public: + bool contains( const char *str ) const + { + for (ConstIterator it = begin(); it != end(); ++it) + if (*it == str) + return true; + return false; + } +}; + +class UserList { + public: + UserList( char **in ); + bool hasUser( const char *str ) const { return users.contains( str ); } + bool hasGroup( gid_t gid ) const + { return groups.find( gid ) != groups.end(); } + bool hasGroups() const { return !groups.isEmpty(); } + KCStringList users; + + private: + QValueList<gid_t> groups; +}; + +UserList::UserList( char **in ) +{ + struct group *grp; + + for (; *in; in++) + if (**in == '@') { + if ((grp = getgrnam( *in + 1 ))) { + for (; *grp->gr_mem; grp->gr_mem++) + users.append( *grp->gr_mem ); + groups.append( grp->gr_gid ); + } + } else + users.append( *in ); +} + +void +KGreeter::insertUsers() +{ + struct passwd *ps; + + // XXX remove seteuid-voodoo when we run as nobody + if (!(ps = getpwnam( "nobody" ))) + return; + if (setegid( ps->pw_gid )) + return; + if (seteuid( ps->pw_uid )) { + setegid(0); + return; + } + + QImage default_pix; + if (userView) { + if (!default_pix.load( _faceDir + "/.default.face.icon" )) + if (!default_pix.load( _faceDir + "/.default.face" )) + LogError( "Can't open default user face\n" ); + QSize ns( 48, 48 ); + if (default_pix.size() != ns) + default_pix = + default_pix.convertDepth( 32 ).smoothScale( ns, QImage::ScaleMin ); + } + if (_showUsers == SHOW_ALL) { + UserList noUsers( _noUsers ); + QDict<int> dupes( 1000 ); + for (setpwent(); (ps = getpwent()) != 0;) { + if (*ps->pw_dir && *ps->pw_shell && + (ps->pw_uid >= (unsigned)_lowUserId || + !ps->pw_uid && _showRoot) && + ps->pw_uid <= (unsigned)_highUserId && + !noUsers.hasUser( ps->pw_name ) && + !noUsers.hasGroup( ps->pw_gid )) + { + QString username( QFile::decodeName( ps->pw_name ) ); + if (!dupes.find( username )) { + dupes.insert( username, (int *)-1 ); + insertUser( default_pix, username, ps ); + } + } + } + } else { + UserList users( _users ); + if (users.hasGroups()) { + QDict<int> dupes( 1000 ); + for (setpwent(); (ps = getpwent()) != 0;) { + if (*ps->pw_dir && *ps->pw_shell && + (ps->pw_uid >= (unsigned)_lowUserId || + !ps->pw_uid && _showRoot) && + ps->pw_uid <= (unsigned)_highUserId && + (users.hasUser( ps->pw_name ) || + users.hasGroup( ps->pw_gid ))) + { + QString username( QFile::decodeName( ps->pw_name ) ); + if (!dupes.find( username )) { + dupes.insert( username, (int *)-1 ); + insertUser( default_pix, username, ps ); + } + } + } + } else { + KCStringList::ConstIterator it = users.users.begin(); + for (; it != users.users.end(); ++it) + if ((ps = getpwnam( (*it).data() )) && + (ps->pw_uid || _showRoot)) + insertUser( default_pix, QFile::decodeName( *it ), ps ); + } + } + endpwent(); + if (_sortUsers) { + if (userView) + userView->sort(); + if (userList) + userList->sort(); + } + + // XXX remove seteuid-voodoo when we run as nobody + seteuid( 0 ); + setegid( 0 ); +} + +void +KGreeter::putSession( const QString &type, const QString &name, bool hid, const char *exe ) +{ + int prio = exe ? (!strcmp( exe, "default" ) ? 0 : + !strcmp( exe, "custom" ) ? 1 : + !strcmp( exe, "failsafe" ) ? 3 : 2) : 2; + for (uint i = 0; i < sessionTypes.size(); i++) + if (sessionTypes[i].type == type) { + sessionTypes[i].prio = prio; + return; + } + sessionTypes.append( SessType( name, type, hid, prio ) ); +} + +void +KGreeter::insertSessions() +{ + for (char **dit = _sessionsDirs; *dit; ++dit) { + QStringList ents = QDir( *dit ).entryList(); + for (QStringList::ConstIterator it = ents.begin(); it != ents.end(); ++it) + if ((*it).endsWith( ".desktop" )) { + KSimpleConfig dsk( QString( *dit ).append( '/' ).append( *it ) ); + dsk.setGroup( "Desktop Entry" ); + putSession( (*it).left( (*it).length() - 8 ), + dsk.readEntry( "Name" ), + (dsk.readBoolEntry( "Hidden", false ) || + (dsk.hasKey( "TryExec" ) && + KStandardDirs::findExe( dsk.readEntry( "TryExec" ) ).isEmpty())), + dsk.readEntry( "Exec" ).latin1() ); + } + } + putSession( "default", i18n("Default"), false, "default" ); + putSession( "custom", i18n("Custom"), false, "custom" ); + putSession( "failsafe", i18n("Failsafe"), false, "failsafe" ); + qBubbleSort( sessionTypes ); + for (uint i = 0; i < sessionTypes.size() && !sessionTypes[i].hid; i++) { + sessMenu->insertItem( sessionTypes[i].name, i ); + switch (sessionTypes[i].prio) { + case 0: case 1: nSpecials++; break; + case 2: nNormals++; break; + } + } +} + +void +KGreeter::slotUserEntered() +{ + if (userView) { + QListViewItem *item; + for (item = userView->firstChild(); item; item = item->nextSibling()) + if (((UserListViewItem *)item)->login == curUser) { + userView->setSelected( item, true ); + userView->ensureItemVisible( item ); + goto oke; + } + userView->clearSelection(); + } + oke: + if (isVisible()) + slotLoadPrevWM(); + else + QTimer::singleShot( 0, this, SLOT(slotLoadPrevWM()) ); +} + +void +KGreeter::slotUserClicked( QListViewItem *item ) +{ + if (item) { + curUser = ((UserListViewItem *)item)->login; + verify->setUser( curUser ); + slotLoadPrevWM(); + } +} + +void +KGreeter::slotSessionSelected( int id ) +{ + if (id != curSel) { + sessMenu->setItemChecked( curSel, false ); + sessMenu->setItemChecked( id, true ); + curSel = id; + verify->gplugActivity(); + } +} + +void +KGreeter::reject() +{ + verify->reject(); +} + +void +KGreeter::accept() +{ + if (userView && userView->hasFocus()) + slotUserClicked( userView->currentItem() ); + else + verify->accept(); +} + +void // private +KGreeter::setPrevWM( int wm ) +{ + if (curPrev != wm) { + if (curPrev != -1) + sessMenu->changeItem( curPrev, sessionTypes[curPrev].name ); + if (wm != -1) + sessMenu->changeItem( wm, sessionTypes[wm].name + i18n(" (previous)") ); + curPrev = wm; + } +} + +void +KGreeter::slotLoadPrevWM() +{ + int len, i, b; + unsigned long crc, by; + QCString name; + char *sess; + + if (verify->coreLock) { + needLoad = true; + return; + } + needLoad = false; + + prevValid = true; + name = curUser.local8Bit(); + GSendInt( G_ReadDmrc ); + GSendStr( name.data() ); + GRecvInt(); // ignore status code ... + if ((len = name.length())) { + GSendInt( G_GetDmrc ); + GSendStr( "Session" ); + sess = GRecvStr(); + if (!sess) { /* no such user */ + if (!userView && !userList) { // don't fake if user list shown + prevValid = false; + /* simple crc32 */ + for (crc = _forgingSeed, i = 0; i < len; i++) { + by = (crc & 255) ^ name[i]; + for (b = 0; b < 8; b++) + by = (by >> 1) ^ (-(by & 1) & 0xedb88320); + crc = (crc >> 8) ^ by; + } + /* forge a session with this hash - default & custom more probable */ + /* XXX - this should do a statistical analysis of the real users */ +#if 1 + setPrevWM( crc % (nSpecials * 2 + nNormals) % (nSpecials + nNormals) ); +#else + i = crc % (nSpecials * 2 + nNormals); + if (i < nNormals) + setPrevWM( i + nSpecials ); + else + setPrevWM( (i - nNormals) / 2 ); +#endif + return; + } + } else { + for (uint i = 0; i < sessionTypes.count() && !sessionTypes[i].hid; i++) + if (sessionTypes[i].type == sess) { + free( sess ); + setPrevWM( i ); + return; + } + if (curSel == -1) + MsgBox( sorrybox, i18n("Your saved session type '%1' is not valid any more.\n" + "Please select a new one, otherwise 'default' will be used.").arg( sess ) ); + free( sess ); + prevValid = false; + } + } + setPrevWM( -1 ); +} + +void // protected +KGreeter::pluginSetup() +{ + int field = 0; + QString ent, pn( verify->pluginName() ), dn( dName + '_' + pn ); + + if (_preselUser != PRESEL_PREV) + stsFile->deleteEntry( verify->entitiesLocal() ? dName : dn, false ); + if (_preselUser != PRESEL_NONE && verify->entityPresettable()) { + if (verify->entitiesLocal()) + ent = _preselUser == PRESEL_PREV ? + stsFile->readEntry( dName ) : _defaultUser; + else + ent = _preselUser == PRESEL_PREV ? + stsFile->readEntry( dn ) : + verify->getConf( 0, (pn + ".DefaultEntity").latin1(), QVariant() ).toString(); + field = verify->entitiesFielded() ? + verify->getConf( 0, (pn + ".FocusField").latin1(), QVariant( 0 ) ).toInt() : + _focusPasswd; + } + verify->presetEntity( ent, field ); + if (userList) + verify->loadUsers( *userList ); +} + +void +KGreeter::verifyPluginChanged( int id ) +{ + curPlugin = id; + pluginSetup(); +} + +void +KGreeter::verifyClear() +{ + curUser = QString::null; + slotUserEntered(); + slotSessionSelected( -1 ); +} + +void +KGreeter::verifyOk() +{ + if (_preselUser == PRESEL_PREV && verify->entityPresettable()) + stsFile->writeEntry( verify->entitiesLocal() ? + dName : + dName + '_' + verify->pluginName(), + verify->getEntity() ); + if (curSel != -1) { + GSendInt( G_PutDmrc ); + GSendStr( "Session" ); + GSendStr( sessionTypes[curSel].type.utf8() ); + } else if (!prevValid) { + GSendInt( G_PutDmrc ); + GSendStr( "Session" ); + GSendStr( "default" ); + } + GSendInt( G_Ready ); + done( ex_exit ); +} + +void +KGreeter::verifyFailed() +{ + if (needLoad) + slotLoadPrevWM(); +} + +void +KGreeter::verifySetUser( const QString &user ) +{ + curUser = user; + slotUserEntered(); +} + + +KStdGreeter::KStdGreeter() + : KGreeter() + , clock( 0 ) + , pixLabel( 0 ) +{ + QBoxLayout *main_box; +#ifdef WITH_KDM_XCONSOLE + if (consoleView) { + QBoxLayout *ex_box = new QVBoxLayout( this, 10, 10 ); + main_box = new QHBoxLayout( ex_box, 10 ); + ex_box->addWidget( consoleView ); + } else +#endif + main_box = new QHBoxLayout( this, 10, 10 ); + + if (userView) + main_box->addWidget( userView ); + + QBoxLayout *inner_box = new QVBoxLayout( main_box, 10 ); + + if (!_authorized && _authComplain) { + QLabel *complainLabel = new QLabel( + i18n("Warning: this is an unsecured session"), this ); + QToolTip::add( complainLabel, + i18n("This display requires no X authorization.\n" + "This means that anybody can connect to it,\n" + "open windows on it or intercept your input.") ); + complainLabel->setAlignment( AlignCenter ); + complainLabel->setFont( _failFont ); + complainLabel->setPaletteForegroundColor( Qt::red ); + inner_box->addWidget( complainLabel ); + } + if (!_greetString.isEmpty()) { + QLabel *welcomeLabel = new QLabel( _greetString, this ); + welcomeLabel->setAlignment( AlignCenter ); + welcomeLabel->setFont( _greetFont ); + inner_box->addWidget( welcomeLabel ); + } + + switch (_logoArea) { + case LOGO_CLOCK: + clock = new KdmClock( this, "clock" ); + break; + case LOGO_LOGO: + { + QMovie movie( _logo ); + kapp->eventLoop()->processEvents( QEventLoop::ExcludeUserInput | QEventLoop::ExcludeSocketNotifiers, 100 ); + QPixmap pixmap; + if (!movie.framePixmap().isNull() || pixmap.load( _logo )) { + pixLabel = new QLabel( this ); + if (!movie.framePixmap().isNull()) { + pixLabel->setMovie( movie ); + if (!movie.framePixmap().hasAlpha()) + pixLabel->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + } else { + pixLabel->setPixmap( pixmap ); + if (!pixmap.hasAlpha()) + pixLabel->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + } + pixLabel->setIndent( 0 ); + } + } + break; + } + + if (userView) { + if (clock) + inner_box->addWidget( clock, 0, AlignCenter ); + else if (pixLabel) + inner_box->addWidget( pixLabel, 0, AlignCenter ); + } else { + if (clock) + main_box->addWidget( clock, 0, AlignCenter ); + else if (pixLabel) + main_box->addWidget( pixLabel, 0, AlignCenter ); + } + + goButton = new QPushButton( i18n("L&ogin"), this ); + goButton->setDefault( true ); + connect( goButton, SIGNAL(clicked()), SLOT(accept()) ); + menuButton = new QPushButton( i18n("&Menu"), this ); + //helpButton + + QWidget *prec; + if (userView) + prec = userView; +#ifdef WITH_KDM_XCONSOLE + else if (consoleView) + prec = consoleView; +#endif + else + prec = menuButton; + KGStdVerify *sverify = + new KGStdVerify( this, this, prec, QString::null, + pluginList, KGreeterPlugin::Authenticate, + KGreeterPlugin::Login ); + inner_box->addLayout( sverify->getLayout() ); + QPopupMenu *plugMenu = sverify->getPlugMenu(); + sverify->selectPlugin( curPlugin ); + verify = sverify; + + inner_box->addWidget( new KSeparator( KSeparator::HLine, this ) ); + + QBoxLayout *hbox2 = new QHBoxLayout( inner_box, 10 ); + hbox2->addWidget( goButton ); + hbox2->addStretch( 1 ); + hbox2->addWidget( menuButton ); + hbox2->addStretch( 1 ); + + if (sessMenu->count() > 1) { + inserten( i18n("Session &Type"), ALT+Key_T, sessMenu ); + needSep = true; + } + + if (plugMenu) { + inserten( i18n("&Authentication Method"), ALT+Key_A, plugMenu ); + needSep = true; + } + +#ifdef XDMCP + completeMenu( LOGIN_LOCAL_ONLY, ex_choose, i18n("&Remote Login"), ALT+Key_R ); +#else + completeMenu(); +#endif + + if (optMenu) + menuButton->setPopup( optMenu ); + else + menuButton->hide(); + + pluginSetup(); + + verify->start(); +} + +void +KStdGreeter::pluginSetup() +{ + inherited::pluginSetup(); + if (userView) { + if (verify->entitiesLocal() && verify->entityPresettable()) + userView->show(); + else + userView->hide(); + } + adjustGeometry(); + update(); +} + +void +KStdGreeter::verifyFailed() +{ + goButton->setEnabled( false ); + menuButton->setEnabled( false ); + if (userView) + userView->setEnabled( false ); + inherited::verifyFailed(); +} + +void +KStdGreeter::verifyRetry() +{ + goButton->setEnabled( true ); + menuButton->setEnabled( true ); + if (userView) + userView->setEnabled( true ); +} + + +KThemedGreeter::KThemedGreeter() + : KGreeter( true ) + , themer( 0 ) +// , clock( 0 ) +{ + // We do all painting ourselves + setBackgroundMode( NoBackground ); + // Allow tracking the mouse position + setMouseTracking( true ); + + adjustGeometry(); + + themer = new KdmThemer( _theme, "console", this ); + if (!themer->isOK()) { + delete themer; + themer = 0; + return; + } + + connect( themer, SIGNAL(activated( const QString & )), + SLOT(slotThemeActivated( const QString & )) ); + + console_rect = themer->findNode( "xconsole" ); // kdm ext + userlist_rect = themer->findNode( "userlist" ); + caps_warning = themer->findNode( "caps-lock-warning" ); + xauth_warning = themer->findNode( "xauth-warning" ); // kdm ext + pam_error = themer->findNode( "pam-error" ); + timed_label = themer->findNode( "timed-label" ); + if (pam_error && pam_error->isA( "KdmLabel" )) + static_cast<KdmLabel*>(pam_error)->setText( i18n("Login Failed.") ); + + KdmItem *itm; + if ((itm = themer->findNode( "pam-message" ))) // done via msgboxes + itm->hide( true ); + if ((itm = themer->findNode( "language_button" ))) // not implemented yet + itm->hide( true ); + +#ifdef WITH_KDM_XCONSOLE + if (console_rect) { + if (consoleView) + console_rect->setWidget( consoleView ); + else + console_rect->hide( true ); + } +#endif + + if (xauth_warning && (_authorized || !_authComplain)) + xauth_warning->hide( true ); + +// if (!_greetString.isEmpty()) { +// } +// clock = new KdmClock( this, "clock" ); + + QWidget *prec; + if (userView) + prec = userView; +#ifdef WITH_KDM_XCONSOLE + else if (consoleView) + prec = consoleView; +#endif + else + prec = 0; + KGThemedVerify *tverify = + new KGThemedVerify( this, themer, this, prec, QString::null, + pluginList, KGreeterPlugin::Authenticate, + KGreeterPlugin::Login ); + QPopupMenu *plugMenu = tverify->getPlugMenu(); + tverify->selectPlugin( curPlugin ); + verify = tverify; + + session_button = 0; + if ((itm = themer->findNode( "session_button" ))) { + if (sessMenu->count() <= 1) + itm->hide( true ); + else { + session_button = itm; + QAccel *accel = new QAccel( this ); + accel->insertItem( ALT+Key_T, 0 ); + connect( accel, SIGNAL(activated( int )), SLOT(slotSessMenu()) ); + } + } else { + if (sessMenu->count() > 1) { + inserten( i18n("Session &Type"), ALT+Key_T, sessMenu ); + needSep = true; + } + } + + if (plugMenu) { + inserten( i18n("&Authentication Method"), ALT+Key_A, plugMenu ); + needSep = true; + } + +#ifdef XDMCP + completeMenu( LOGIN_LOCAL_ONLY, ex_choose, i18n("&Remote Login"), ALT+Key_R ); +#else + completeMenu(); +#endif + + system_button = themer->findNode( "system_button" ); + QAccel *accel = new QAccel( this ); + accel->insertItem( ALT+Key_M, 0 ); + connect( accel, SIGNAL(activated( int )), SLOT(slotActionMenu()) ); + + pluginSetup(); + + verify->start(); +} + +bool +KThemedGreeter::event( QEvent *e ) +{ + if (themer) + themer->widgetEvent( e ); + return inherited::event( e ); +} + +void +KThemedGreeter::pluginSetup() +{ + inherited::pluginSetup(); + + if (userView && verify->entitiesLocal() && verify->entityPresettable() && userlist_rect) { + userlist_rect->setWidget( userView ); + userView->show(); + } else { + if (userView) + userView->hide(); + if (userlist_rect) + userlist_rect->hide( true ); + } + + update(); +} + +void +KThemedGreeter::verifyFailed() +{ +// goButton->setEnabled( false ); + inherited::verifyFailed(); +} + +void +KThemedGreeter::verifyRetry() +{ +// goButton->setEnabled( true ); +} + +QString KThemedGreeter::timedUser = QString::null; +int KThemedGreeter::timedDelay = -1; + +void +KThemedGreeter::updateStatus( bool fail, bool caps, int timedleft ) +{ + if (pam_error) { + if (fail) + pam_error->show( true ); + else + pam_error->hide( true ); + } + if (caps_warning) { + if (caps) + caps_warning->show( true ); + else + caps_warning->hide( true ); + } + if (timed_label) { + if (timedleft) { + if (timedleft != timedDelay) { + timedDelay = timedleft; + timedUser = curUser; + timed_label->show( true ); + timed_label->update(); + } + } else { + timedDelay = -1; + timed_label->hide( true ); + } + } +} + +void +KThemedGreeter::slotThemeActivated( const QString &id ) +{ + if (id == "login_button") + accept(); + else if (id == "session_button") + slotSessMenu(); + else if (id == "system_button") + slotActionMenu(); +} + +void +KThemedGreeter::slotSessMenu() +{ + sessMenu->popup( mapToGlobal( session_button->rect().center() ) ); +} + +void +KThemedGreeter::slotActionMenu() +{ + if (system_button) + optMenu->popup( mapToGlobal( system_button->rect().center() ) ); + else + optMenu->popup( mapToGlobal( rect().center() ) ); +} + +void +KThemedGreeter::keyPressEvent( QKeyEvent *e ) +{ + inherited::keyPressEvent( e ); + if (!(e->state() & KeyButtonMask) && + (e->key() == Key_Return || e->key() == Key_Enter)) + accept(); +} + +#include "kgreeter.moc" diff --git a/kdm/kfrontend/kgreeter.h b/kdm/kfrontend/kgreeter.h new file mode 100644 index 000000000..32191fafe --- /dev/null +++ b/kdm/kfrontend/kgreeter.h @@ -0,0 +1,164 @@ +/* + +Greeter widget for kdm + +Copyright (C) 1997, 1998 Steffen Hansen <[email protected]> +Copyright (C) 2000-2004 Oswald Buddenhagen <[email protected]> + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + + +#ifndef KGREETER_H +#define KGREETER_H + +#include "kgverify.h" +#include "kgdialog.h" + +class KdmClock; +class UserListView; +class KdmThemer; +class KdmItem; + +class KListView; +class KSimpleConfig; + +class QLabel; +class QPushButton; +class QPopupMenu; +class QListViewItem; + +struct SessType { + QString name, type; + bool hid; + int prio; + + SessType() {} + SessType( const QString &n, const QString &t, bool h, int p ) : + name( n ), type( t ), hid( h ), prio( p ) {} + bool operator<( const SessType &st ) { + return hid != st.hid ? hid < st.hid : + prio != st.prio ? prio < st.prio : + name < st.name; + } +}; + +class KGreeter : public KGDialog, public KGVerifyHandler { + Q_OBJECT + typedef KGDialog inherited; + + public: + KGreeter( bool themed = false ); + ~KGreeter(); + + public slots: + void accept(); + void reject(); + void slotUserClicked( QListViewItem * ); + void slotSessionSelected( int ); + void slotUserEntered(); + + protected: + void installUserList(); + void insertUser( const QImage &, const QString &, struct passwd * ); + void insertUsers(); + void putSession( const QString &, const QString &, bool, const char * ); + void insertSessions(); + virtual void pluginSetup(); + void setPrevWM( int ); + + QString curUser, dName; + KSimpleConfig *stsFile; + UserListView *userView; + QStringList *userList; + QPopupMenu *sessMenu; + QValueVector<SessType> sessionTypes; + int nNormals, nSpecials; + int curPrev, curSel; + bool prevValid; + bool needLoad; + + static int curPlugin; + static PluginList pluginList; + + private slots: + void slotLoadPrevWM(); + + public: // from KGVerifyHandler + virtual void verifyPluginChanged( int id ); + virtual void verifyClear(); + virtual void verifyOk(); + virtual void verifyFailed(); +// virtual void verifyRetry(); + virtual void verifySetUser( const QString &user ); +}; + +class KStdGreeter : public KGreeter { + Q_OBJECT + typedef KGreeter inherited; + + public: + KStdGreeter(); + + protected: + virtual void pluginSetup(); + + private: + KdmClock *clock; + QLabel *pixLabel; + QPushButton *goButton; + QPushButton *menuButton; + + public: // from KGVerifyHandler + virtual void verifyFailed(); + virtual void verifyRetry(); +}; + +class KThemedGreeter : public KGreeter { + Q_OBJECT + typedef KGreeter inherited; + + public: + KThemedGreeter(); + bool isOK() { return themer != 0; } + static QString timedUser; + static int timedDelay; + + public slots: + void slotThemeActivated( const QString &id ); + void slotSessMenu(); + void slotActionMenu(); + + protected: + virtual void updateStatus( bool fail, bool caps, int timedleft ); + virtual void pluginSetup(); + virtual void keyPressEvent( QKeyEvent * ); + virtual bool event( QEvent *e ); + + private: +// KdmClock *clock; + KdmThemer *themer; + KdmItem *caps_warning, *xauth_warning, *pam_error, *timed_label, + *console_rect, *userlist_rect, + *session_button, *system_button; + + public: // from KGVerifyHandler + virtual void verifyFailed(); + virtual void verifyRetry(); +}; + +#endif /* KGREETER_H */ diff --git a/kdm/kfrontend/kgverify.cpp b/kdm/kfrontend/kgverify.cpp new file mode 100644 index 000000000..f9bb77ff8 --- /dev/null +++ b/kdm/kfrontend/kgverify.cpp @@ -0,0 +1,1211 @@ +/* + +Shell for kdm conversation plugins + +Copyright (C) 1997, 1998, 2000 Steffen Hansen <[email protected]> +Copyright (C) 2000-2004 Oswald Buddenhagen <[email protected]> + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include <config.h> + +#include "kgverify.h" +#include "kdmconfig.h" +#include "kdm_greet.h" + +#include "themer/kdmthemer.h" +#include "themer/kdmitem.h" + +#include <kapplication.h> +#include <klocale.h> +#include <klibloader.h> +#include <kseparator.h> +#include <kstdguiitem.h> +#include <kpushbutton.h> + +#include <qregexp.h> +#include <qpopupmenu.h> +#include <qlayout.h> +#include <qfile.h> +#include <qlabel.h> + +#include <pwd.h> +#include <sys/types.h> +#include <stdlib.h> +#include <unistd.h> + +#include <X11/Xlib.h> // for updateLockStatus() +#include <fixx11h.h> // ... and make eventFilter() work again + +#define FULL_GREET_TO 40 // normal inactivity timeout +#define TIMED_GREET_TO 20 // inactivity timeout when persisting timed login +#define MIN_TIMED_TO 5 // minimal timed login delay +#define DEAD_TIMED_TO 2 // <enter> dead time after re-activating timed login +#define SECONDS 1000 // reduce to 100 to speed up testing + +void KGVerifyHandler::verifyClear() +{ +} + +void KGVerifyHandler::updateStatus( bool, bool, int ) +{ +} + +KGVerify::KGVerify( KGVerifyHandler *_handler, KdmThemer *_themer, + QWidget *_parent, QWidget *_predecessor, + const QString &_fixedUser, + const PluginList &_pluginList, + KGreeterPlugin::Function _func, + KGreeterPlugin::Context _ctx ) + : inherited() + , coreLock( 0 ) + , fixedEntity( _fixedUser ) + , pluginList( _pluginList ) + , handler( _handler ) + , themer( _themer ) + , parent( _parent ) + , predecessor( _predecessor ) + , plugMenu( 0 ) + , curPlugin( -1 ) + , timedLeft( 0 ) + , func( _func ) + , ctx( _ctx ) + , enabled( true ) + , running( false ) + , suspended( false ) + , failed( false ) + , isClear( true ) +{ + connect( &timer, SIGNAL(timeout()), SLOT(slotTimeout()) ); + connect( kapp, SIGNAL(activity()), SLOT(slotActivity()) ); + + _parent->installEventFilter( this ); +} + +KGVerify::~KGVerify() +{ + Debug( "delete %s\n", pName.data() ); + delete greet; +} + +QPopupMenu * +KGVerify::getPlugMenu() +{ + // assert( !cont ); + if (!plugMenu) { + uint np = pluginList.count(); + if (np > 1) { + plugMenu = new QPopupMenu( parent ); + connect( plugMenu, SIGNAL(activated( int )), + SLOT(slotPluginSelected( int )) ); + for (uint i = 0; i < np; i++) + plugMenu->insertItem( i18n(greetPlugins[pluginList[i]].info->name), pluginList[i] ); + } + } + return plugMenu; +} + +bool // public +KGVerify::entitiesLocal() const +{ + return greetPlugins[pluginList[curPlugin]].info->flags & kgreeterplugin_info::Local; +} + +bool // public +KGVerify::entitiesFielded() const +{ + return greetPlugins[pluginList[curPlugin]].info->flags & kgreeterplugin_info::Fielded; +} + +bool // public +KGVerify::entityPresettable() const +{ + return greetPlugins[pluginList[curPlugin]].info->flags & kgreeterplugin_info::Presettable; +} + +bool // public +KGVerify::isClassic() const +{ + return !strcmp( greetPlugins[pluginList[curPlugin]].info->method, "classic" ); +} + +QString // public +KGVerify::pluginName() const +{ + QString name( greetPlugins[pluginList[curPlugin]].library->fileName() ); + uint st = name.findRev( '/' ) + 1; + uint en = name.find( '.', st ); + if (en - st > 7 && QConstString( name.unicode() + st, 7 ).string() == "kgreet_") + st += 7; + return name.mid( st, en - st ); +} + +static void +showWidgets( QLayoutItem *li ) +{ + QWidget *w; + QLayout *l; + + if ((w = li->widget())) + w->show(); + else if ((l = li->layout())) { + QLayoutIterator it = l->iterator(); + for (QLayoutItem *itm = it.current(); itm; itm = ++it) + showWidgets( itm ); + } +} + +void // public +KGVerify::selectPlugin( int id ) +{ + if (pluginList.isEmpty()) { + MsgBox( errorbox, i18n("No greeter widget plugin loaded. Check the configuration.") ); + ::exit( EX_UNMANAGE_DPY ); + } + curPlugin = id; + if (plugMenu) + plugMenu->setItemChecked( id, true ); + pName = ("greet_" + pluginName()).latin1(); + Debug( "new %s\n", pName.data() ); + greet = greetPlugins[pluginList[id]].info->create( this, themer, parent, predecessor, fixedEntity, func, ctx ); + timeable = _autoLoginDelay && entityPresettable() && isClassic(); +} + +void // public +KGVerify::loadUsers( const QStringList &users ) +{ + Debug( "%s->loadUsers(...)\n", pName.data() ); + greet->loadUsers( users ); +} + +void // public +KGVerify::presetEntity( const QString &entity, int field ) +{ + presEnt = entity; + presFld = field; +} + +bool // private +KGVerify::applyPreset() +{ + if (!presEnt.isEmpty()) { + Debug( "%s->presetEntity(%\"s, %d)\n", pName.data(), + presEnt.latin1(), presFld ); + greet->presetEntity( presEnt, presFld ); + if (entitiesLocal()) { + curUser = presEnt; + handler->verifySetUser( presEnt ); + } + return true; + } + return false; +} + +bool // private +KGVerify::scheduleAutoLogin( bool initial ) +{ + if (timeable) { + Debug( "%s->presetEntity(%\"s, -1)\n", pName.data(), + _autoLoginUser.latin1(), -1 ); + greet->presetEntity( _autoLoginUser, -1 ); + curUser = _autoLoginUser; + handler->verifySetUser( _autoLoginUser ); + timer.start( 1000 ); + if (initial) { + timedLeft = _autoLoginDelay; + deadTicks = 0; + } else { + timedLeft = QMAX( _autoLoginDelay - TIMED_GREET_TO, MIN_TIMED_TO ); + deadTicks = DEAD_TIMED_TO; + } + updateStatus(); + running = false; + isClear = true; + return true; + } + return false; +} + +void // private +KGVerify::performAutoLogin() +{ +// timer.stop(); + GSendInt( G_AutoLogin ); + handleVerify(); +} + +QString // public +KGVerify::getEntity() const +{ + Debug( "%s->getEntity()\n", pName.data() ); + QString ent = greet->getEntity(); + Debug( " entity: %s\n", ent.latin1() ); + return ent; +} + +void +KGVerify::setUser( const QString &user ) +{ + // assert( fixedEntity.isEmpty() ); + curUser = user; + Debug( "%s->setUser(%\"s)\n", pName.data(), user.latin1() ); + greet->setUser( user ); + gplugActivity(); +} + +void +KGVerify::start() +{ + authTok = (func == KGreeterPlugin::ChAuthTok); + cont = false; + if (func == KGreeterPlugin::Authenticate && ctx == KGreeterPlugin::Login) { + if (scheduleAutoLogin( true )) { + if (!_autoLoginAgain) + _autoLoginDelay = 0, timeable = false; + return; + } else + applyPreset(); + } + running = true; + Debug( "%s->start()\n", pName.data() ); + greet->start(); + if (!(func == KGreeterPlugin::Authenticate || + ctx == KGreeterPlugin::ChangeTok || + ctx == KGreeterPlugin::ExChangeTok)) + { + cont = true; + handleVerify(); + } +} + +void +KGVerify::abort() +{ + Debug( "%s->abort()\n", pName.data() ); + greet->abort(); + running = false; +} + +void +KGVerify::suspend() +{ + // assert( !cont ); + if (running) { + Debug( "%s->abort()\n", pName.data() ); + greet->abort(); + } + suspended = true; + updateStatus(); + timer.suspend(); +} + +void +KGVerify::resume() +{ + timer.resume(); + suspended = false; + updateLockStatus(); + if (running) { + Debug( "%s->start()\n", pName.data() ); + greet->start(); + } else if (delayed) { + delayed = false; + running = true; + Debug( "%s->start()\n", pName.data() ); + greet->start(); + } +} + +void // not a slot - called manually by greeter +KGVerify::accept() +{ + Debug( "%s->next()\n", pName.data() ); + greet->next(); +} + +void // private +KGVerify::doReject( bool initial ) +{ + // assert( !cont ); + if (running) { + Debug( "%s->abort()\n", pName.data() ); + greet->abort(); + } + handler->verifyClear(); + Debug( "%s->clear()\n", pName.data() ); + greet->clear(); + curUser = QString::null; + if (!scheduleAutoLogin( initial )) { + isClear = !(isClear && applyPreset()); + if (running) { + Debug( "%s->start()\n", pName.data() ); + greet->start(); + } + if (!failed) + timer.stop(); + } +} + +void // not a slot - called manually by greeter +KGVerify::reject() +{ + doReject( true ); +} + +void +KGVerify::setEnabled( bool on ) +{ + Debug( "%s->setEnabled(%s)\n", pName.data(), on ? "true" : "false" ); + greet->setEnabled( on ); + enabled = on; + updateStatus(); +} + +void // private +KGVerify::slotTimeout() +{ + if (failed) { + failed = false; + updateStatus(); + Debug( "%s->revive()\n", pName.data() ); + greet->revive(); + handler->verifyRetry(); + if (suspended) + delayed = true; + else { + running = true; + Debug( "%s->start()\n", pName.data() ); + greet->start(); + slotActivity(); + gplugActivity(); + if (cont) + handleVerify(); + } + } else if (timedLeft) { + deadTicks--; + if (!--timedLeft) + performAutoLogin(); + else + timer.start( 1000 ); + updateStatus(); + } else { + // assert( ctx == Login ); + isClear = true; + doReject( false ); + } +} + +void +KGVerify::slotActivity() +{ + if (timedLeft) { + Debug( "%s->revive()\n", pName.data() ); + greet->revive(); + Debug( "%s->start()\n", pName.data() ); + greet->start(); + running = true; + timedLeft = 0; + updateStatus(); + timer.start( TIMED_GREET_TO * SECONDS ); + } else if (timeable) + timer.start( TIMED_GREET_TO * SECONDS ); +} + + +void // private static +KGVerify::VMsgBox( QWidget *parent, const QString &user, + QMessageBox::Icon type, const QString &mesg ) +{ + FDialog::box( parent, type, user.isEmpty() ? + mesg : i18n("Authenticating %1...\n\n").arg( user ) + mesg ); +} + +static const char *msgs[]= { + I18N_NOOP( "You are required to change your password immediately (password aged)." ), + I18N_NOOP( "You are required to change your password immediately (root enforced)." ), + I18N_NOOP( "You are not allowed to login at the moment." ), + I18N_NOOP( "Home folder not available." ), + I18N_NOOP( "Logins are not allowed at the moment.\nTry again later." ), + I18N_NOOP( "Your login shell is not listed in /etc/shells." ), + I18N_NOOP( "Root logins are not allowed." ), + I18N_NOOP( "Your account has expired; please contact your system administrator." ) +}; + +void // private static +KGVerify::VErrBox( QWidget *parent, const QString &user, const char *msg ) +{ + QMessageBox::Icon icon; + QString mesg; + + if (!msg) { + mesg = i18n("A critical error occurred.\n" + "Please look at KDM's logfile(s) for more information\n" + "or contact your system administrator."); + icon = errorbox; + } else { + mesg = QString::fromLocal8Bit( msg ); + QString mesg1 = mesg + '.'; + for (uint i = 0; i < as(msgs); i++) + if (mesg1 == msgs[i]) { + mesg = i18n(msgs[i]); + break; + } + icon = sorrybox; + } + VMsgBox( parent, user, icon, mesg ); +} + +void // private static +KGVerify::VInfoBox( QWidget *parent, const QString &user, const char *msg ) +{ + QString mesg = QString::fromLocal8Bit( msg ); + QRegExp rx( "^Warning: your account will expire in (\\d+) day" ); + if (rx.search( mesg ) >= 0) { + int expire = rx.cap( 1 ).toInt(); + mesg = expire ? + i18n("Your account expires tomorrow.", + "Your account expires in %n days.", expire) : + i18n("Your account expires today."); + } else { + rx.setPattern( "^Warning: your password will expire in (\\d+) day" ); + if (rx.search( mesg ) >= 0) { + int expire = rx.cap( 1 ).toInt(); + mesg = expire ? + i18n("Your password expires tomorrow.", + "Your password expires in %n days.", expire) : + i18n("Your password expires today."); + } + } + VMsgBox( parent, user, infobox, mesg ); +} + +bool // public static +KGVerify::handleFailVerify( QWidget *parent ) +{ + Debug( "handleFailVerify ...\n" ); + char *msg = GRecvStr(); + QString user = QString::fromLocal8Bit( msg ); + free( msg ); + + for (;;) { + int ret = GRecvInt(); + + // non-terminal status + switch (ret) { + /* case V_PUT_USER: cannot happen - we are in "classic" mode */ + /* case V_PRE_OK: cannot happen - not in ChTok dialog */ + /* case V_CHTOK: cannot happen - called by non-interactive verify */ + case V_CHTOK_AUTH: + Debug( " V_CHTOK_AUTH\n" ); + { + QStringList pgs( _pluginsLogin ); + pgs += _pluginsShutdown; + QStringList::ConstIterator it; + for (it = pgs.begin(); it != pgs.end(); ++it) + if (*it == "classic" || *it == "modern") { + pgs = *it; + goto gotit; + } else if (*it == "generic") { + pgs = "modern"; + goto gotit; + } + pgs = "classic"; + gotit: + KGChTok chtok( parent, user, init( pgs ), 0, + KGreeterPlugin::AuthChAuthTok, + KGreeterPlugin::Login ); + return chtok.exec(); + } + case V_MSG_ERR: + Debug( " V_MSG_ERR\n" ); + msg = GRecvStr(); + Debug( " message %\"s\n", msg ); + VErrBox( parent, user, msg ); + if (msg) + free( msg ); + continue; + case V_MSG_INFO: + Debug( " V_MSG_INFO\n" ); + msg = GRecvStr(); + Debug( " message %\"s\n", msg ); + VInfoBox( parent, user, msg ); + free( msg ); + continue; + } + + // terminal status + switch (ret) { + case V_OK: + Debug( " V_OK\n" ); + return true; + case V_AUTH: + Debug( " V_AUTH\n" ); + VMsgBox( parent, user, sorrybox, i18n("Authentication failed") ); + return false; + case V_FAIL: + Debug( " V_FAIL\n" ); + return false; + default: + LogPanic( "Unknown V_xxx code %d from core\n", ret ); + } + } +} + +void // private +KGVerify::handleVerify() +{ + QString user; + + Debug( "handleVerify ...\n" ); + for (;;) { + char *msg; + int ret, echo, ndelay; + KGreeterPlugin::Function nfunc; + + ret = GRecvInt(); + + // requests + coreLock = 1; + switch (ret) { + case V_GET_TEXT: + Debug( " V_GET_TEXT\n" ); + msg = GRecvStr(); + Debug( " prompt %\"s\n", msg ); + echo = GRecvInt(); + Debug( " echo = %d\n", echo ); + ndelay = GRecvInt(); + Debug( " ndelay = %d\n%s->textPrompt(...)\n", ndelay, pName.data() ); + greet->textPrompt( msg, echo, ndelay ); + if (msg) + free( msg ); + return; + case V_GET_BINARY: + Debug( " V_GET_BINARY\n" ); + msg = GRecvArr( &ret ); + Debug( " %d bytes prompt\n", ret ); + ndelay = GRecvInt(); + Debug( " ndelay = %d\n%s->binaryPrompt(...)\n", ndelay, pName.data() ); + greet->binaryPrompt( msg, ndelay ); + if (msg) + free( msg ); + return; + } + + // non-terminal status + coreLock = 2; + switch (ret) { + case V_PUT_USER: + Debug( " V_PUT_USER\n" ); + msg = GRecvStr(); + curUser = user = QString::fromLocal8Bit( msg ); + // greet needs this to be able to return something useful from + // getEntity(). but the backend is still unable to tell a domain ... + Debug( " %s->setUser(%\"s)\n", pName.data(), user.latin1() ); + greet->setUser( curUser ); + handler->verifySetUser( curUser ); + if (msg) + free( msg ); + continue; + case V_PRE_OK: // this is only for func == AuthChAuthTok + Debug( " V_PRE_OK\n" ); + // With the "classic" method, the wrong user simply cannot be + // authenticated, even with the generic plugin. Other methods + // could do so, but this applies only to ctx == ChangeTok, which + // is not implemented yet. + authTok = true; + cont = true; + Debug( "%s->succeeded()\n", pName.data() ); + greet->succeeded(); + continue; + case V_CHTOK_AUTH: + Debug( " V_CHTOK_AUTH\n" ); + nfunc = KGreeterPlugin::AuthChAuthTok; + user = curUser; + goto dchtok; + case V_CHTOK: + Debug( " V_CHTOK\n" ); + nfunc = KGreeterPlugin::ChAuthTok; + user = QString::null; + dchtok: + { + timer.stop(); + Debug( "%s->succeeded()\n", pName.data() ); + greet->succeeded(); + KGChTok chtok( parent, user, pluginList, curPlugin, nfunc, KGreeterPlugin::Login ); + if (!chtok.exec()) + goto retry; + handler->verifyOk(); + return; + } + case V_MSG_ERR: + Debug( " V_MSG_ERR\n" ); + msg = GRecvStr(); + Debug( " %s->textMessage(%\"s, true)\n", pName.data(), msg ); + if (!greet->textMessage( msg, true )) { + Debug( " message passed\n" ); + VErrBox( parent, user, msg ); + } else + Debug( " message swallowed\n" ); + if (msg) + free( msg ); + continue; + case V_MSG_INFO: + Debug( " V_MSG_INFO\n" ); + msg = GRecvStr(); + Debug( " %s->textMessage(%\"s, false)\n", pName.data(), msg ); + if (!greet->textMessage( msg, false )) { + Debug( " message passed\n" ); + VInfoBox( parent, user, msg ); + } else + Debug( " message swallowed\n" ); + free( msg ); + continue; + } + + // terminal status + coreLock = 0; + running = false; + timer.stop(); + + if (ret == V_OK) { + Debug( " V_OK\n" ); + if (!fixedEntity.isEmpty()) { + Debug( " %s->getEntity()\n", pName.data() ); + QString ent = greet->getEntity(); + Debug( " entity %\"s\n", ent.latin1() ); + if (ent != fixedEntity) { + Debug( "%s->failed()\n", pName.data() ); + greet->failed(); + MsgBox( sorrybox, + i18n("Authenticated user (%1) does not match requested user (%2).\n") + .arg( ent ).arg( fixedEntity ) ); + goto retry; + } + } + Debug( "%s->succeeded()\n", pName.data() ); + greet->succeeded(); + handler->verifyOk(); + return; + } + + Debug( "%s->failed()\n", pName.data() ); + greet->failed(); + + if (ret == V_AUTH) { + Debug( " V_AUTH\n" ); + failed = true; + updateStatus(); + handler->verifyFailed(); + timer.start( 1500 + kapp->random()/(RAND_MAX/1000) ); + return; + } + if (ret != V_FAIL) + LogPanic( "Unknown V_xxx code %d from core\n", ret ); + Debug( " V_FAIL\n" ); + retry: + Debug( "%s->revive()\n", pName.data() ); + greet->revive(); + running = true; + Debug( "%s->start()\n", pName.data() ); + greet->start(); + if (!cont) + return; + user = QString::null; + } +} + +void +KGVerify::gplugReturnText( const char *text, int tag ) +{ + Debug( "%s: gplugReturnText(%\"s, %d)\n", pName.data(), + tag & V_IS_SECRET ? "<masked>" : text, tag ); + GSendStr( text ); + if (text) { + GSendInt( tag ); + handleVerify(); + } else + coreLock = 0; +} + +void +KGVerify::gplugReturnBinary( const char *data ) +{ + if (data) { + unsigned const char *up = (unsigned const char *)data; + int len = up[3] | (up[2] << 8) | (up[1] << 16) | (up[0] << 24); + Debug( "%s: gplugReturnBinary(%d bytes)\n", pName.data(), len ); + GSendArr( len, data ); + handleVerify(); + } else { + Debug( "%s: gplugReturnBinary(NULL)\n", pName.data() ); + GSendArr( 0, 0 ); + coreLock = 0; + } +} + +void +KGVerify::gplugSetUser( const QString &user ) +{ + Debug( "%s: gplugSetUser(%\"s)\n", pName.data(), user.latin1() ); + curUser = user; + handler->verifySetUser( user ); +} + +void +KGVerify::gplugStart() +{ + // XXX handle func != Authenticate + Debug( "%s: gplugStart()\n", pName.data() ); + GSendInt( ctx == KGreeterPlugin::Shutdown ? G_VerifyRootOK : G_Verify ); + GSendStr( greetPlugins[pluginList[curPlugin]].info->method ); + handleVerify(); +} + +void +KGVerify::gplugActivity() +{ + Debug( "%s: gplugActivity()\n", pName.data() ); + if (func == KGreeterPlugin::Authenticate && + ctx == KGreeterPlugin::Login) + { + isClear = false; + if (!timeable) + timer.start( FULL_GREET_TO * SECONDS ); + } +} + +void +KGVerify::gplugMsgBox( QMessageBox::Icon type, const QString &text ) +{ + Debug( "%s: gplugMsgBox(%d, %\"s)\n", pName.data(), type, text.latin1() ); + MsgBox( type, text ); +} + +bool +KGVerify::eventFilter( QObject *o, QEvent *e ) +{ + switch (e->type()) { + case QEvent::KeyPress: + if (timedLeft) { + QKeyEvent *ke = (QKeyEvent *)e; + if (ke->key() == Key_Return || ke->key() == Key_Enter) { + if (deadTicks <= 0) { + timedLeft = 0; + performAutoLogin(); + } + return true; + } + } + /* fall through */ + case QEvent::KeyRelease: + updateLockStatus(); + /* fall through */ + default: + break; + } + return inherited::eventFilter( o, e ); +} + +void +KGVerify::updateLockStatus() +{ + unsigned int lmask; + Window dummy1, dummy2; + int dummy3, dummy4, dummy5, dummy6; + XQueryPointer( qt_xdisplay(), DefaultRootWindow( qt_xdisplay() ), + &dummy1, &dummy2, &dummy3, &dummy4, &dummy5, &dummy6, + &lmask ); + capsLocked = lmask & LockMask; + updateStatus(); +} + +void +KGVerify::MsgBox( QMessageBox::Icon typ, const QString &msg ) +{ + timer.suspend(); + FDialog::box( parent, typ, msg ); + timer.resume(); +} + + +QVariant // public static +KGVerify::getConf( void *, const char *key, const QVariant &dflt ) +{ + if (!qstrcmp( key, "EchoMode" )) + return QVariant( _echoMode ); + else { + QString fkey = QString::fromLatin1( key ) + '='; + for (QStringList::ConstIterator it = _pluginOptions.begin(); + it != _pluginOptions.end(); ++it) + if ((*it).startsWith( fkey )) + return (*it).mid( fkey.length() ); + return dflt; + } +} + +QValueVector<GreeterPluginHandle> KGVerify::greetPlugins; + +PluginList +KGVerify::init( const QStringList &plugins ) +{ + PluginList pluginList; + + for (QStringList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it) { + GreeterPluginHandle plugin; + QString path = KLibLoader::self()->findLibrary( + ((*it)[0] == '/' ? *it : "kgreet_" + *it ).latin1() ); + if (path.isEmpty()) { + LogError( "GreeterPlugin %s does not exist\n", (*it).latin1() ); + continue; + } + uint i, np = greetPlugins.count(); + for (i = 0; i < np; i++) + if (greetPlugins[i].library->fileName() == path) + goto next; + if (!(plugin.library = KLibLoader::self()->library( path.latin1() ))) { + LogError( "Cannot load GreeterPlugin %s (%s)\n", (*it).latin1(), path.latin1() ); + continue; + } + if (!plugin.library->hasSymbol( "kgreeterplugin_info" )) { + LogError( "GreeterPlugin %s (%s) is no valid greet widget plugin\n", + (*it).latin1(), path.latin1() ); + plugin.library->unload(); + continue; + } + plugin.info = (kgreeterplugin_info*)plugin.library->symbol( "kgreeterplugin_info" ); + if (!plugin.info->init( QString::null, getConf, 0 )) { + LogError( "GreeterPlugin %s (%s) refuses to serve\n", + (*it).latin1(), path.latin1() ); + plugin.library->unload(); + continue; + } + Debug( "GreeterPlugin %s (%s) loaded\n", (*it).latin1(), plugin.info->name ); + greetPlugins.append( plugin ); + next: + pluginList.append( i ); + } + return pluginList; +} + +void +KGVerify::done() +{ + for (uint i = 0; i < greetPlugins.count(); i++) { + if (greetPlugins[i].info->done) + greetPlugins[i].info->done(); + greetPlugins[i].library->unload(); + } +} + + +KGStdVerify::KGStdVerify( KGVerifyHandler *_handler, QWidget *_parent, + QWidget *_predecessor, const QString &_fixedUser, + const PluginList &_pluginList, + KGreeterPlugin::Function _func, + KGreeterPlugin::Context _ctx ) + : inherited( _handler, 0, _parent, _predecessor, _fixedUser, + _pluginList, _func, _ctx ) + , failedLabelState( 0 ) +{ + grid = new QGridLayout; + grid->setAlignment( AlignCenter ); + + failedLabel = new QLabel( parent ); + failedLabel->setFont( _failFont ); + grid->addWidget( failedLabel, 1, 0, AlignCenter ); + + updateLockStatus(); +} + +KGStdVerify::~KGStdVerify() +{ + grid->removeItem( greet->getLayoutItem() ); +} + +void // public +KGStdVerify::selectPlugin( int id ) +{ + inherited::selectPlugin( id ); + grid->addItem( greet->getLayoutItem(), 0, 0 ); + showWidgets( greet->getLayoutItem() ); +} + +void // private slot +KGStdVerify::slotPluginSelected( int id ) +{ + if (failed) + return; + if (id != curPlugin) { + plugMenu->setItemChecked( curPlugin, false ); + parent->setUpdatesEnabled( false ); + grid->removeItem( greet->getLayoutItem() ); + Debug( "delete %s\n", pName.data() ); + delete greet; + selectPlugin( id ); + handler->verifyPluginChanged( id ); + if (running) + start(); + parent->setUpdatesEnabled( true ); + } +} + +void +KGStdVerify::updateStatus() +{ + int nfls; + + if (!enabled) + nfls = 1; + else if (failed) + nfls = 2; + else if (timedLeft) + nfls = -timedLeft; + else if (!suspended && capsLocked) + nfls = 3; + else + nfls = 1; + + if (failedLabelState != nfls) { + failedLabelState = nfls; + if (nfls < 0) { + failedLabel->setPaletteForegroundColor( Qt::black ); + failedLabel->setText( i18n( "Automatic login in 1 second...", + "Automatic login in %n seconds...", + timedLeft ) ); + } else { + switch (nfls) { + default: + failedLabel->clear(); + break; + case 3: + failedLabel->setPaletteForegroundColor( Qt::red ); + failedLabel->setText( i18n("Warning: Caps Lock on") ); + break; + case 2: + failedLabel->setPaletteForegroundColor( Qt::black ); + failedLabel->setText( authTok ? + i18n("Change failed") : + fixedEntity.isEmpty() ? + i18n("Login failed") : + i18n("Authentication failed") ); + break; + } + } + } +} + +KGThemedVerify::KGThemedVerify( KGVerifyHandler *_handler, + KdmThemer *_themer, + QWidget *_parent, QWidget *_predecessor, + const QString &_fixedUser, + const PluginList &_pluginList, + KGreeterPlugin::Function _func, + KGreeterPlugin::Context _ctx ) + : inherited( _handler, _themer, _parent, _predecessor, _fixedUser, + _pluginList, _func, _ctx ) +{ + updateLockStatus(); +} + +KGThemedVerify::~KGThemedVerify() +{ +} + +void // public +KGThemedVerify::selectPlugin( int id ) +{ + inherited::selectPlugin( id ); + QLayoutItem *l; + KdmItem *n; + if (themer && (l = greet->getLayoutItem())) { + if (!(n = themer->findNode( "talker" ))) + MsgBox( errorbox, + i18n("Theme not usable with authentication method '%1'.") + .arg( i18n(greetPlugins[pluginList[id]].info->name) ) ); + else { + n->setLayoutItem( l ); + showWidgets( l ); + } + } + if (themer) + themer->updateGeometry( true ); +} + +void // private slot +KGThemedVerify::slotPluginSelected( int id ) +{ + if (failed) + return; + if (id != curPlugin) { + plugMenu->setItemChecked( curPlugin, false ); + Debug( "delete %s\n", pName.data() ); + delete greet; + selectPlugin( id ); + handler->verifyPluginChanged( id ); + if (running) + start(); + } +} + +void +KGThemedVerify::updateStatus() +{ + handler->updateStatus( enabled && failed, + enabled && !suspended && capsLocked, + timedLeft ); +} + + +KGChTok::KGChTok( QWidget *_parent, const QString &user, + const PluginList &pluginList, int curPlugin, + KGreeterPlugin::Function func, + KGreeterPlugin::Context ctx ) + : inherited( _parent ) + , verify( 0 ) +{ + QSizePolicy fp( QSizePolicy::Fixed, QSizePolicy::Fixed ); + okButton = new KPushButton( KStdGuiItem::ok(), this ); + okButton->setSizePolicy( fp ); + okButton->setDefault( true ); + cancelButton = new KPushButton( KStdGuiItem::cancel(), this ); + cancelButton->setSizePolicy( fp ); + + verify = new KGStdVerify( this, this, cancelButton, user, pluginList, func, ctx ); + verify->selectPlugin( curPlugin ); + + QVBoxLayout *box = new QVBoxLayout( this, 10 ); + + box->addWidget( new QLabel( i18n("Changing authentication token"), this ), 0, AlignHCenter ); + + box->addLayout( verify->getLayout() ); + + box->addWidget( new KSeparator( KSeparator::HLine, this ) ); + + QHBoxLayout *hlay = new QHBoxLayout( box ); + hlay->addStretch( 1 ); + hlay->addWidget( okButton ); + hlay->addStretch( 1 ); + hlay->addWidget( cancelButton ); + hlay->addStretch( 1 ); + + connect( okButton, SIGNAL(clicked()), SLOT(accept()) ); + connect( cancelButton, SIGNAL(clicked()), SLOT(reject()) ); + + QTimer::singleShot( 0, verify, SLOT(start()) ); +} + +KGChTok::~KGChTok() +{ + hide(); + delete verify; +} + +void +KGChTok::accept() +{ + verify->accept(); +} + +void +KGChTok::verifyPluginChanged( int ) +{ + // cannot happen +} + +void +KGChTok::verifyOk() +{ + inherited::accept(); +} + +void +KGChTok::verifyFailed() +{ + okButton->setEnabled( false ); + cancelButton->setEnabled( false ); +} + +void +KGChTok::verifyRetry() +{ + okButton->setEnabled( true ); + cancelButton->setEnabled( true ); +} + +void +KGChTok::verifySetUser( const QString & ) +{ + // cannot happen +} + + +////// helper class, nuke when qtimer supports suspend()/resume() + +QXTimer::QXTimer() + : inherited( 0 ) + , left( -1 ) +{ + connect( &timer, SIGNAL(timeout()), SLOT(slotTimeout()) ); +} + +void +QXTimer::start( int msec ) +{ + left = msec; + timer.start( left, true ); + gettimeofday( &stv, 0 ); +} + +void +QXTimer::stop() +{ + timer.stop(); + left = -1; +} + +void +QXTimer::suspend() +{ + if (timer.isActive()) { + timer.stop(); + struct timeval tv; + gettimeofday( &tv, 0 ); + left -= (tv.tv_sec - stv.tv_sec) * 1000 + (tv.tv_usec - stv.tv_usec) / 1000; + if (left < 0) + left = 0; + } +} + +void +QXTimer::resume() +{ + if (left >= 0 && !timer.isActive()) { + timer.start( left, true ); + gettimeofday( &stv, 0 ); + } +} + +void +QXTimer::slotTimeout() +{ + left = 0; + emit timeout(); +} + + +#include "kgverify.moc" diff --git a/kdm/kfrontend/kgverify.h b/kdm/kfrontend/kgverify.h new file mode 100644 index 000000000..a0d285e3d --- /dev/null +++ b/kdm/kfrontend/kgverify.h @@ -0,0 +1,248 @@ +/* + +Shell for kdm conversation plugins + +Copyright (C) 1997, 1998 Steffen Hansen <[email protected]> +Copyright (C) 2000-2004 Oswald Buddenhagen <[email protected]> + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + + +#ifndef KGVERIFY_H +#define KGVERIFY_H + +#include "kgreeterplugin.h" +#include "kfdialog.h" + +#include <qlayout.h> +#include <qtimer.h> +#include <qvaluevector.h> + +#include <sys/time.h> +#include <time.h> + +// helper class, nuke when qt supports suspend()/resume() +class QXTimer : public QObject { + Q_OBJECT + typedef QObject inherited; + + public: + QXTimer(); + void start( int msec ); + void stop(); + void suspend(); + void resume(); + + signals: + void timeout(); + + private slots: + void slotTimeout(); + + private: + QTimer timer; + struct timeval stv; + long left; +}; + +class KGVerifyHandler { + public: + virtual void verifyPluginChanged( int id ) = 0; + virtual void verifyClear(); + virtual void verifyOk() = 0; + virtual void verifyFailed() = 0; + virtual void verifyRetry() = 0; + virtual void verifySetUser( const QString &user ) = 0; + virtual void updateStatus( bool fail, bool caps, int left ); // for themed only +}; + +class QWidget; +class QLabel; +class QPopupMenu; +class QTimer; +class KPushButton; +class KLibrary; + +struct GreeterPluginHandle { + KLibrary *library; + kgreeterplugin_info *info; +}; + +typedef QValueVector<int> PluginList; + +class KGVerify : public QObject, public KGreeterPluginHandler { + Q_OBJECT + typedef QObject inherited; + + public: + KGVerify( KGVerifyHandler *handler, KdmThemer *themer, + QWidget *parent, QWidget *predecessor, + const QString &fixedEntity, const PluginList &pluginList, + KGreeterPlugin::Function func, KGreeterPlugin::Context ctx ); + virtual ~KGVerify(); + QPopupMenu *getPlugMenu(); + void loadUsers( const QStringList &users ); + void presetEntity( const QString &entity, int field ); + QString getEntity() const; + void setUser( const QString &user ); + /* virtual */ void selectPlugin( int id ); + bool entitiesLocal() const; + bool entitiesFielded() const; + bool entityPresettable() const; + bool isClassic() const; + QString pluginName() const; + void setEnabled( bool on ); + void abort(); + void suspend(); + void resume(); + void accept(); + void reject(); + + int coreLock; + + static bool handleFailVerify( QWidget *parent ); + static PluginList init( const QStringList &plugins ); + static void done(); + + public slots: + void start(); + + protected: + bool eventFilter( QObject *, QEvent * ); + void MsgBox( QMessageBox::Icon typ, const QString &msg ); + void setTimer(); + void updateLockStatus(); + virtual void updateStatus() = 0; + void handleVerify(); + + QXTimer timer; + QString fixedEntity, presEnt, curUser; + PluginList pluginList; + KGVerifyHandler *handler; + KdmThemer *themer; + QWidget *parent, *predecessor; + KGreeterPlugin *greet; + QPopupMenu *plugMenu; + int curPlugin, presFld, timedLeft, deadTicks; + QCString pName; + KGreeterPlugin::Function func; + KGreeterPlugin::Context ctx; + bool capsLocked; + bool enabled, running, suspended, failed, delayed, cont; + bool authTok, isClear, timeable; + + static void VMsgBox( QWidget *parent, const QString &user, QMessageBox::Icon type, const QString &mesg ); + static void VErrBox( QWidget *parent, const QString &user, const char *msg ); + static void VInfoBox( QWidget *parent, const QString &user, const char *msg ); + + static QValueVector<GreeterPluginHandle> greetPlugins; + + private: + bool applyPreset(); + void performAutoLogin(); + bool scheduleAutoLogin( bool initial ); + void doReject( bool initial ); + + private slots: + //virtual void slotPluginSelected( int id ) = 0; + void slotTimeout(); + void slotActivity(); + + public: // from KGreetPluginHandler + virtual void gplugReturnText( const char *text, int tag ); + virtual void gplugReturnBinary( const char *data ); + virtual void gplugSetUser( const QString &user ); + virtual void gplugStart(); + virtual void gplugActivity(); + virtual void gplugMsgBox( QMessageBox::Icon type, const QString &text ); + + static QVariant getConf( void *ctx, const char *key, const QVariant &dflt ); +}; + +class KGStdVerify : public KGVerify { + Q_OBJECT + typedef KGVerify inherited; + + public: + KGStdVerify( KGVerifyHandler *handler, QWidget *parent, + QWidget *predecessor, const QString &fixedEntity, + const PluginList &pluginList, + KGreeterPlugin::Function func, KGreeterPlugin::Context ctx ); + virtual ~KGStdVerify(); + QLayout *getLayout() const { return grid; } + void selectPlugin( int id ); + + protected: + void updateStatus(); + + private: + QGridLayout *grid; + QLabel *failedLabel; + int failedLabelState; + + private slots: + void slotPluginSelected( int id ); +}; + +class KGThemedVerify : public KGVerify { + Q_OBJECT + typedef KGVerify inherited; + + public: + KGThemedVerify( KGVerifyHandler *handler, KdmThemer *themer, + QWidget *parent, QWidget *predecessor, + const QString &fixedEntity, + const PluginList &pluginList, + KGreeterPlugin::Function func, + KGreeterPlugin::Context ctx ); + virtual ~KGThemedVerify(); + void selectPlugin( int id ); + + protected: + void updateStatus(); + + private slots: + void slotPluginSelected( int id ); +}; + +class KGChTok : public FDialog, public KGVerifyHandler { + Q_OBJECT + typedef FDialog inherited; + + public: + KGChTok( QWidget *parent, const QString &user, + const PluginList &pluginList, int curPlugin, + KGreeterPlugin::Function func, KGreeterPlugin::Context ctx ); + ~KGChTok(); + + public slots: + void accept(); + + private: + KPushButton *okButton, *cancelButton; + KGStdVerify *verify; + + public: // from KGVerifyHandler + virtual void verifyPluginChanged( int id ); + virtual void verifyOk(); + virtual void verifyFailed(); + virtual void verifyRetry(); + virtual void verifySetUser( const QString &user ); +}; + +#endif /* KGVERIFY_H */ diff --git a/kdm/kfrontend/krootimage.cpp b/kdm/kfrontend/krootimage.cpp new file mode 100644 index 000000000..c630d9fa7 --- /dev/null +++ b/kdm/kfrontend/krootimage.cpp @@ -0,0 +1,122 @@ +/* + +Copyright (C) 1999 Matthias Hoelzer-Kluepfel <[email protected]> +Copyright (C) 2002,2004 Oswald Buddenhagen <[email protected]> + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License version 2 as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. + +*/ + +#include <config.h> + +#include <kcmdlineargs.h> +#include <ksimpleconfig.h> +#include <klocale.h> + +#include <qfile.h> + +#include "krootimage.h" + +#include <X11/Xlib.h> + +#include <stdlib.h> + +static const char description[] = + I18N_NOOP( "Fancy desktop background for kdm" ); + +static const char version[] = "v2.0"; + +static KCmdLineOptions options[] = { + { "+config", I18N_NOOP( "Name of the configuration file" ), 0 }, + KCmdLineLastOption +}; + + +MyApplication::MyApplication( const char *conf ) + : KApplication(), + renderer( 0, new KSimpleConfig( QFile::decodeName( conf ) ) ) +{ + connect( &timer, SIGNAL(timeout()), SLOT(slotTimeout()) ); + connect( &renderer, SIGNAL(imageDone( int )), this, SLOT(renderDone()) ); + renderer.enableTiling( true ); // optimize + renderer.changeWallpaper(); // cannot do it when we're killed, so do it now + timer.start( 60000 ); + renderer.start(); +} + + +void +MyApplication::renderDone() +{ + desktop()->setBackgroundPixmap( renderer.pixmap() ); + desktop()->repaint( true ); + renderer.saveCacheFile(); + renderer.cleanup(); + for (unsigned i=0; i<renderer.numRenderers(); ++i) + { + KBackgroundRenderer * r = renderer.renderer(i); + if (r->backgroundMode() == KBackgroundSettings::Program || + (r->multiWallpaperMode() != KBackgroundSettings::NoMulti && + r->multiWallpaperMode() != KBackgroundSettings::NoMultiRandom)) + return; + } + quit(); +} + +void +MyApplication::slotTimeout() +{ + bool change = false; + + if (renderer.needProgramUpdate()) { + renderer.programUpdate(); + change = true; + } + + if (renderer.needWallpaperChange()) { + renderer.changeWallpaper(); + change = true; + } + + if (change) + renderer.start(); +} + +int +main( int argc, char *argv[] ) +{ + KApplication::disableAutoDcopRegistration(); + + KLocale::setMainCatalogue( "kdesktop" ); + KCmdLineArgs::init( argc, argv, "krootimage", I18N_NOOP( "KRootImage" ), description, version ); + KCmdLineArgs::addCmdLineOptions( options ); + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + if (!args->count()) + args->usage(); + MyApplication app( args->arg( 0 ) ); + args->clear(); + + app.exec(); + + app.flushX(); + + // Keep color resources after termination + XSetCloseDownMode( qt_xdisplay(), RetainTemporary ); + + return 0; +} + +#include "krootimage.moc" diff --git a/kdm/kfrontend/krootimage.h b/kdm/kfrontend/krootimage.h new file mode 100644 index 000000000..608cfa3a1 --- /dev/null +++ b/kdm/kfrontend/krootimage.h @@ -0,0 +1,48 @@ +/* + +Copyright (C) 1999 Matthias Hoelzer-Kluepfel <[email protected]> +Copyright (C) 2002,2004 Oswald Buddenhagen <[email protected]> + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License version 2 as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. + +*/ + +#ifndef __KDMDESKTOP_H__ +#define __KDMDESKTOP_H__ + + +#include <kapplication.h> +#include <qtimer.h> + +#include <bgrender.h> + + +class MyApplication : public KApplication +{ + Q_OBJECT + + public: + MyApplication( const char * ); + + private slots: + void renderDone(); + void slotTimeout(); + + private: + KVirtualBGRenderer renderer; + QTimer timer; +}; + +#endif diff --git a/kdm/kfrontend/pics/Makefile.am b/kdm/kfrontend/pics/Makefile.am new file mode 100644 index 000000000..d6b8957e8 --- /dev/null +++ b/kdm/kfrontend/pics/Makefile.am @@ -0,0 +1,9 @@ + +picsdir = $(kde_datadir)/kdm/pics +pics_DATA = kdelogo.png kdelogo-crystal.png shutdown.jpg + +usersdir = $(picsdir)/users +users_DATA = default1.png default2.png default3.png root1.png + + +EXTRA_DIST = $(pics_DATA) $(users_DATA) diff --git a/kdm/kfrontend/pics/default1.png b/kdm/kfrontend/pics/default1.png Binary files differnew file mode 100644 index 000000000..ef3aef3f9 --- /dev/null +++ b/kdm/kfrontend/pics/default1.png diff --git a/kdm/kfrontend/pics/default2.png b/kdm/kfrontend/pics/default2.png Binary files differnew file mode 100644 index 000000000..194acfe2c --- /dev/null +++ b/kdm/kfrontend/pics/default2.png diff --git a/kdm/kfrontend/pics/default3.png b/kdm/kfrontend/pics/default3.png Binary files differnew file mode 100644 index 000000000..a8663b15e --- /dev/null +++ b/kdm/kfrontend/pics/default3.png diff --git a/kdm/kfrontend/pics/kdelogo-crystal.png b/kdm/kfrontend/pics/kdelogo-crystal.png Binary files differnew file mode 100644 index 000000000..592a7e321 --- /dev/null +++ b/kdm/kfrontend/pics/kdelogo-crystal.png diff --git a/kdm/kfrontend/pics/kdelogo.png b/kdm/kfrontend/pics/kdelogo.png Binary files differnew file mode 100644 index 000000000..bd4e199e5 --- /dev/null +++ b/kdm/kfrontend/pics/kdelogo.png diff --git a/kdm/kfrontend/pics/root1.png b/kdm/kfrontend/pics/root1.png Binary files differnew file mode 100644 index 000000000..fced75c11 --- /dev/null +++ b/kdm/kfrontend/pics/root1.png diff --git a/kdm/kfrontend/pics/shutdown.jpg b/kdm/kfrontend/pics/shutdown.jpg Binary files differnew file mode 100644 index 000000000..f1353c54b --- /dev/null +++ b/kdm/kfrontend/pics/shutdown.jpg diff --git a/kdm/kfrontend/sessions/9wm.desktop b/kdm/kfrontend/sessions/9wm.desktop new file mode 100644 index 000000000..d17276782 --- /dev/null +++ b/kdm/kfrontend/sessions/9wm.desktop @@ -0,0 +1,76 @@ +[Desktop Entry] +Type=XSession +Exec=9wm +TryExec=9wm +Name=9WM +Name[cy]= 9WM +Name[eo]=9FA +Name[hi]=9डबल्यू-एम +Name[ta]=9 WM +Name[te]=9 డబ్ల్యు ఎం +Name[th]=ตัวจัดการหน้าต่าง gWM +Comment=An emulation of the Plan 9 window manager 8-1/2 +Comment[af]='n Nabootsing van die 'Plan 9' venster bestuurder +Comment[be]=Эмуляцыя кіраўніка вокнаў 8-1/2 для Plan 9 +Comment[bn]=প্ল্যান ৯ উইণ্ডো ম্যানেজার ৮-১/২ -এর এমুলেশন +Comment[bs]=Simulacija Plan 9 window managera 8-1/2 +Comment[ca]=Una emulació del gestor de finestres Plan 9 +Comment[cs]=Emulace Plane 9 správce oken 8-1/2 +Comment[csb]=Emùlacëjô menedżera òknów Plan 9 - 8-1/2 +Comment[cy]=Efelychiad o 8-1/2, y trefnydd ffenestri Plan 9 +Comment[da]=En emulering af Plan 9 vindueshåndteringen 8-1/2 +Comment[de]=Emulation des Plan 9-Fenstermanagers 8-1/2 +Comment[el]=Μια προσομοίωση του Plan 9 διαχειριστή παραθύρων 8-1/2 +Comment[eo]=KDE-fenestroadministrilo de plano 9 +Comment[es]=Una emulación del gestor de ventanas Plan 9 8-1/2 +Comment[et]=Plan 9 aknahalduri 8-1/2 emuleerimine +Comment[eu]=Plan 9 8-1/2 leiho kudeatzailearen emulazioa +Comment[fa]=تقلیدی از نقشۀ مدیر پنجره ۹. ۸-۱/ ۲ +Comment[fi]=Emulaatio Plan9-ikkunaohjelmasta 8-1/2 +Comment[fr]=Une émulation du gestionnaire de fenêtres Plan 9 8-1/2 +Comment[fy]=In emulator foar de Plan9 finstersbehearder 8-1/2 +Comment[gl]=Unha emulación do xestor de fiestras de Plan9 +Comment[he]=מדמה של מנהל חלונות Plan 9 8-1/2 +Comment[hi]=प्लान 9 विंडो प्रबंधक 8-1/2 का एक एमुलेशन +Comment[hr]=Emulacija Plan 9 upravitelja prozora 8-1/2 +Comment[hu]=A Plan 9 operációs rendszer 8-1/2 nevű ablakkezelőjének emulálása +Comment[is]=Eftirlíking af Plan 9 gluggastjóranum 8-1/2 +Comment[it]=Un emulatore del window manager 8-1/2 Plan 9 +Comment[ja]=Plan9 ウィンドウマネージャのエミュレーション 8-1/2 +Comment[ka]=Plan 9 ფანჯრის მმართველის ემულატორი +Comment[kk]=Plan 9 терезе менеджерінің эмуляторы +Comment[km]=ការត្រាប់តាមកម្មវិធីគ្រប់គ្រងបង្អួច Plan 9 8-1/2 +Comment[ko]=Plan 9 창 관리자 8-1/2 에뮬레이션 +Comment[lt]=Plan 9 langų tvarkyklės emuliatorius 8-1/2 +Comment[lv]=Plan 9 logu menedžera emulators 8-1/2 +Comment[mk]=Емулација на менаџерот на прозорци Plan 9 8-1/2 +Comment[ms]=Pelagakan Plan 9 pengurus tetingkap 8-1/2 +Comment[mt]=Emulazzjoni tal-window manager "Plan 9" 8½ +Comment[nb]=En emulering av vindusbehandleren 8 ½ fra Plan 9 +Comment[nds]=Emuleert den Plan-9-Finsterpleger 8-1/2 +Comment[ne]=योजना 9 सञ्झ्याल प्रबन्धक 8-1/2 को इमुलेसन +Comment[nl]=Een emulator voor de Plan9 windowmanager 8-1/2 +Comment[nn]=Emulering av vindaugssjefen 8 ½ frå Plan 9 +Comment[pa]=ਪਲੇਨ 9 ਝਰੋਖਾ ਮੈਨੇਜਰ 8-1/2 ਦਾ ਸਮਰੂਪ +Comment[pl]=Emulacja menedżera okien Plan 9 - 8-1/2 +Comment[pt]=Uma emulação do gestor de janelas do Plan 9 8-1/2 +Comment[pt_BR]=Uma emulação do gerenciador de janelas do Plan 9 +Comment[ro]=Un emulator al managerului de ferestre 8-1/2 din Plan 9 +Comment[ru]=Эмуляция оконного менеджера Plan 9 +Comment[rw]=Ikurura rya mugenga dirishya 8-1/2 Plan 9 +Comment[se]=Lásegieđahalli mii áddestaddá Plan 9 lásegieđahalli 8-1/2. +Comment[sk]=Emulácia správcu okien 8-1/2 systému Plan 9 +Comment[sl]=Emulacija okenskega upravitelja Plan 9 8-1/2 +Comment[sr]=Емулација Plan 9 менаџера прозора 8-1/2 +Comment[sr@Latn]=Emulacija Plan 9 menadžera prozora 8-1/2 +Comment[sv]=Emulering av Plan-9-fönsterhanteraren 8-1/2 +Comment[ta]= திட்டம் 9 சாளர மேலாளர் 8-1/2 இன் முன்மாதிரி +Comment[tg]=Эмулятори нақшаи 9-и мудири тирезаи 8-1/2 +Comment[th]=การจำลองตัวจัดการหน้าต่าง Plan 9 8-1/2 +Comment[tr]=Plan 9 pencere yöneticisi 8-1/2 için bir emülasyon +Comment[tt]=Plan 9 atlı täräzä idäräçenä axşap eşläw +Comment[uk]=Емуляція менеджера вікон Plan 9 "8-1/2" +Comment[vi]=Một bộ mô phỏng bộ quản lý cửa sổ Plan 9 8-1/2 +Comment[wa]=Ene emulåcion do manaedjeu di purneas di Plan 9 +Comment[zh_CN]=Plan 9 窗口管理器 8-1/2 的模拟 +Comment[zh_TW]=模仿 Plan 9 的視窗管理程式 8-1/2 diff --git a/kdm/kfrontend/sessions/Makefile.am b/kdm/kfrontend/sessions/Makefile.am new file mode 100644 index 000000000..14577ac42 --- /dev/null +++ b/kdm/kfrontend/sessions/Makefile.am @@ -0,0 +1,49 @@ +sessionsdir = $(kde_datadir)/kdm/sessions +sessions_DATA = \ + kde.desktop gnome.desktop \ + 9wm.desktop \ + aewm++.desktop \ + aewm.desktop \ + afterstep.desktop \ + amaterus.desktop \ + amiwm.desktop \ + asclassic.desktop \ + blackbox.desktop \ + cde.desktop \ + ctwm.desktop \ + cwwm.desktop \ + enlightenment.desktop \ + evilwm.desktop \ + fluxbox.desktop \ + flwm.desktop \ + fvwm.desktop \ + fvwm95.desktop \ + golem.desktop \ + icewm.desktop \ + ion.desktop \ + larswm.desktop \ + lwm.desktop \ + matchbox.desktop \ + metacity.desktop \ + mwm.desktop \ + olvwm.desktop \ + olwm.desktop \ + openbox.desktop \ + oroborus.desktop \ + phluid.desktop \ + pwm.desktop \ + qvwm.desktop \ + ratpoison.desktop \ + sapphire.desktop \ + sawfish.desktop \ + twm.desktop \ + ude.desktop \ + vtwm.desktop \ + w9wm.desktop \ + waimea.desktop \ + wm2.desktop \ + wmaker.desktop \ + xfce.desktop \ + xfce4.desktop + +EXTRA_DIST = $(sessions_DATA) diff --git a/kdm/kfrontend/sessions/aewm++.desktop b/kdm/kfrontend/sessions/aewm++.desktop new file mode 100644 index 000000000..3eb4ee8e8 --- /dev/null +++ b/kdm/kfrontend/sessions/aewm++.desktop @@ -0,0 +1,74 @@ +[Desktop Entry] +Type=XSession +Exec=aewm++_xsession +TryExec=aewm++_xsession +Name=AEWM++ +Name[eo]=MFA++ +Name[hi]=एईडबल्यूएम++ +Name[te]=ఎ ఈ డబ్ల్యు ఎం ++ +Comment=A minimal window manager based on AEWM, enhanced by virtual desktops and partial GNOME support +Comment[af]='n Minimale venster bestuurder wat op AEWM gebaseer is. Dit is verbeter met virtuale werkskerms en gedeeltelike GNOME ondersteuning +Comment[ar]=مدير نوافذ مصغّر مبني على AEWM، محسّن بأسطح مكتب وهمية ودعم جينوم جزئي +Comment[be]=Мінімалістычны кіраўнік вокнаў, заснаваны на AEWM, з віртуальнымі працоўнымі сталамі і частковай падтрымкай GNOME +Comment[bn]=AEWM ভিত্তিক একটি পরিমিত উইণ্ডো ম্যানেজার, ভার্চুয়াল ডেস্কটপ এবং আংশিক গনোম সমর্থন দ্বারা বর্ধিত +Comment[bs]=Minimalni window manager baziran na AEWM, proširen virtuelnim desktopima i djelomičnom GNOME podrškom +Comment[ca]=Un gestor de finestres minimalista basat en AEWM, orientat a escriptoris virtuals i funcionament parcial per a GNOME +Comment[cs]=Minimalizovaný správce oken založený na AEWM rozšířený o virtuální plochy a částečnou podporu GNOME +Comment[csb]=Prosti menedżer òknów na spòdlém AEWM, zbògacony o wirtualné pùltë ë dzélowé wspiarce dlô GNOME +Comment[cy]= Trefnydd ffenestri lleiafol wed'i seilio ar AEWM, wedi'i wella gan penbyrddau rhith a cynhaliaeth Gnome rhannol. +Comment[da]=En minimal vindueshåndtering baseret på AEWM, udvidet med virtuelle desktoppe og delvis GNOME-støtte +Comment[de]=Minimalistischer Fenstermanager. Beruht auf AEWM, verbessert durch virtuelle Arbeitsflächen und teilweise GNOME-Unterstützung +Comment[el]=Ένας μικρός διαχειριστής παραθύρων βασισμένος στον AEWM, εμπλουτισμένος με εικονικές επιφάνειες εργασίας και μερική υποστήριξη GNOME +Comment[eo]=Minimuma fenestroadministrilo el MFA, plibonigita per virtualaj labortabloj kaj parta helpo por Gnomikuo +Comment[es]=Un gestor de ventanas minimalista basado en AEWM, mejorado con soporte para escritorios virtailes y, parcialmente, GNOME +Comment[et]=Vähenõudlik aknahaldur, mille aluseks on AEWM ja mida on täiendatud virtuaalsete töölaudade ning osalise GNOME toetusega +Comment[eu]=AEWMn oinarritutako leiho kudeatzaile minimoa, mahaigain birtualen euskarriaz eta, zati batez, GNOMEz hobetua +Comment[fa]= مدیر پنجرۀ کمینه بر اساس AEWM، گسترشیافته توسط رومیزیهای مجازی و پشتیبانی جزئی GNOME +Comment[fi]=Minimaalinen AEWM:ään pohjautuva ikkunaohjelma, jota on parannettuna virtuaalityöpöydillä ja osittaisella GNOME-tuella +Comment[fr]=Un gestionnaire de fenêtres minimal fondé sur AEWM avec, en plus, la gestion des bureaux virtuels ainsi qu'un support partiel de GNOME +Comment[fy]=In minimalistyske finstersmanager basearre op AEWM, útbreide mei firtuele buroblêden en gedieltelike GNOME-stipe +Comment[gl]=Un xestor de fiestras mínimo baseado en AEWM, mellorado cos escritórios virtuais e con soporte parcial para GNOME +Comment[he]=מנהל חלונות מינימלי המבוסס על AEWM, המשופר על ידי שולחנות עבודה וירטואליים ותמיכה חלקית ב GNOME +Comment[hi]= एईडबल्यूएम आधारित अल्प विंडो प्रबंधक, आभासी डेस्कटॉप तथा आंशिक ग्नोम समर्थन से बेहतर बनाया गया +Comment[hr]=Minimalistički upravitelj prozora zasnovan na AEWM, unaprijeđen virtualnim radnim površinama i djelomičnom podrškom za GNOME +Comment[hu]=Egy nagyon egyszerű ablakkezelő az AEWM alapján, virtuális munkaasztalokkal és részleges GNOME-támogatással kiegészítve +Comment[is]=Einfaldur gluggastjóri byggður á AEWM en með stuðningi fyrir sýndarskjáborð og takmörkuðum GNOME stuðningi +Comment[it]= Un window manager minimale basato su AEWM, migliorato con desktop virtuali e supporto parziale per GNOME +Comment[ja]=仮想デスクトップと部分的な GNOME サポートを強化した AEWM ベースの小さなウィンドウマネージャ +Comment[ka]=მინიმალური ფანჯრის მენეჯერი AEWM -ის ბაზაზე, имеющий частичную поддержку GNOME. +Comment[kk]=Виртуалды үстелдері және шамалы GNOME қолдауы бар AEWM-негіздеген шағын терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចតូច ដែលផ្អែកលើ AEWM ដែលធ្វើឲ្យប្រសើរដោយផ្ទៃតុនិមិត្ត និងការគាំទ្រ GNOME +Comment[ko]=부분적 그놈 지원과 가상 데스크톱 지원을 사용하는 AEWM 기반 창 관리자 +Comment[lt]=Minimalistinė langų tvarkyklė paremta AEWM, išplėsta virtualių darbastalių ir daliniu GNOME palaikymu +Comment[lv]=Minimālistisks logu menedžeris bāzēts uz AEWM, papildināts ar virtuālajām darbvirsmām un daļēju GNOME atbalstu +Comment[mk]=Минимален менаџер на прозорци базиран на AEWM, подобрен со виртуелни површини и парцијална GNOME поддршка +Comment[ms]=Pengurus tetingkap minima berdasarkan AEWM, dipertingkatkan dengan desktop visual dan sokongan GNOME separa +Comment[mt]=Window manager żgħir ibbażat fuq AEWM, filmkien ma' desktops virtwali u sapport parzjali għal GNOME. +Comment[nb]=En minimal vindusbehandler basert på AEWM, forbedret med virtuelle skrivebord og delvis støtte for GNOME +Comment[nds]=En minimaal Finsterpleger, opbuut op AEWM, verwiedert üm virtuelle Schriefdischen un deelwies Ünnerstütten för GNOME +Comment[ne]=अवास्तविक डेस्कटप र आंशिक GNOME समर्थनद्वारा बृद्धि गरिएको AEWM मा आधारित न्यूनतम सञ्झ्याल प्रबन्धक +Comment[nl]=Een minimalistische windowmanager gebaseerd op AEWM, uitgebreid met virtuele bureaubladen en gedeeltelijke GNOME-ondersteuning +Comment[nn]=Ein minimal vindaugssjef basert på AEWM, forbetra med virtuelle skrivebord og delvis støtte for GNOME +Comment[pa]= AEWM ਤੇ ਅਧਾਰਿਤ ਛੋਟਾ ਝਰੋਖਾ ਮੈਨੇਜਰ, ਫਰਜ਼ੀ ਵੇਹੜਿਆਂ ਨਾਲ ਲੈੱਸ ਤੇ ਥੋੜਾ GNOME ਸਹਾਇਕ +Comment[pl]=Prosty menedżer okien na bazie AEWM, wzbogacony o wirtualne pulpity i częściowe wsparcie dla GNOME +Comment[pt]=Um gestor de janelas simples baseado no AEWM, melhorado com os ecrãs virtuais e com um suporte parcial do GNOME +Comment[pt_BR]=Um gerenciador de janelas pequeno, baseado no AEWM, melhorado pelas áreas de trabalho virtuais e com suporte parcial ao GNOME +Comment[ro]=Un manager de ferestre minimal bazat pe AEWM, îmbunătățit cu ecrane virtuale și suport parțial pentru GNOME +Comment[ru]=Минимальный оконный менеджер на основе AEWM, имеющий частичную поддержку GNOME. +Comment[rw]=Mugenga dirishya nto ishingiye kuri AEWM, rivuguruwe n'ibiro bitagaragara n'iyifashisha GNOME rituzuye +Comment[se]=Geahpes lásegieđahalli vuođđoduvvon AEWM:as, mas leat virtuella čállinbeavddit ja doarju GNOME muhton muddui. +Comment[sk]=Minimálny správca okien založený na AEWM, rozšírrený o virtuálne plochy a čiastočnú podporu GNOME +Comment[sl]=Skromen okenski upavitelj na osnovi AEWM, izboljšan z navideznimi namizji in delno podporo GNOME +Comment[sr]=Минимални менаџер прозора заснован на AEWM-у, побољшан виртуелним радним површинама и делимичном подршком за Гном +Comment[sr@Latn]=Minimalni menadžer prozora zasnovan na AEWM-u, poboljšan virtuelnim radnim površinama i delimičnom podrškom za Gnom +Comment[sv]=Minimal fönsterhanterare baserad på AEWM, utökad med virtuella skrivbord och delvis stöd för Gnome +Comment[ta]=AEWM அடிப்படையிலான சிறிய சாளர மேலாண்மை, மெய்நிகர் மேல்மேசை மற்றும் பகுதி GNOME ஆதரவால் மேப்படுத்தப்பட்டுள்ளது +Comment[tg]=Мудири равзанаҳои хурд дар асоси AEWM дорои нопурраи интерфейси GNOME +Comment[th]=ตัวจัดการหน้าต่างขนาดเล็ก โดยใช้พื้นฐานของ AEWM แล้วเพิ่มความสามารถด้วย พื้นที่ทำงานเสมือน และสนับสนุน GNOME บางส่วน +Comment[tr]=AEWM tabanlı bir pencere yöneticisi +Comment[tt]=AEWM asılında yasalğan, xıyalí öställär belän beraz GNOME totqan ciñel täräzä-idäräçe +Comment[uk]=Мінімальний менеджер вікон, заснований на AEWM, покращений підтримкою віртуальних стільниць та частковою підтримкою GNOME +Comment[vi]=Bộ quản lý cửa sổ tối thiểu dựa trên AEWM, cải tiến với màn hình nền ảo và được hỗ trợ một phần bởi GNOME +Comment[wa]=On manaedjeu di purneas minimå båzé so AEWM, avou sopoirt po les forveyous scribannes eyet ene miete di sopoirt po Gnome +Comment[zh_CN]=基于 AEWM 的小型窗口管理器,增强了虚拟桌面和部分 GNOME 支持 +Comment[zh_TW]=基於 AEWM 的小型視窗管理程式,增強了虛擬桌面及部分的 GNOME 支援 diff --git a/kdm/kfrontend/sessions/aewm.desktop b/kdm/kfrontend/sessions/aewm.desktop new file mode 100644 index 000000000..362fd921f --- /dev/null +++ b/kdm/kfrontend/sessions/aewm.desktop @@ -0,0 +1,76 @@ +[Desktop Entry] +Type=XSession +Exec=aewm +TryExec=aewm +Name=AEWM +Name[eo]=MFA +Name[hi]=एईडबल्यूएम +Name[te]=ఎ ఈ డబ్ల్యు ఎం +Comment=A minimalist window manager +Comment[af]='n Minimale venster bestuurder +Comment[ar]=مسيير نوافذ ذو الميزات الأقل +Comment[be]=Мінімалістычны кіраўнік вокнаў +Comment[bn]=একটি পরিমিত উইণ্ডো ম্যানেজার +Comment[bs]=Minimalistički window manager +Comment[ca]=Un gestor de finestres minimalista +Comment[cs]=Minimalistický správce oken +Comment[csb]=Prosti menedżer òknów +Comment[cy]=Trefnydd ffenestri lleiafol +Comment[da]=En minimalistisk vindueshåndtering +Comment[de]=Minimalistischer Fenstermanager +Comment[el]=Ένας μινιμαλιστικός διαχειριστής παραθύρων +Comment[eo]=Minimumema fenestroadministrilo +Comment[es]=Un gestor de ventanas minimalista +Comment[et]=Vähenõudlik aknahaldur +Comment[eu]=Leiho kudeatzaile minimalista +Comment[fa]=یک مدیر پنجرۀ کمینه +Comment[fi]=Minimalistinen ikkunaohjelma +Comment[fr]=Un gestionnaire de fenêtres minimaliste +Comment[fy]=In minimalistyske finstersmanager +Comment[gl]=Un xestor de fiestras minimalista +Comment[he]=מנהל חלונות מינימליסטי +Comment[hi]=एक अल्पतम विंडो प्रबंधक +Comment[hr]=Minimalistički upravitelj prozora +Comment[hu]=Egy nagyon egyszerű ablakkezelő +Comment[is]=Einfaldur gluggastjóri +Comment[it]=Un window manager minimalista +Comment[ja]=小さなウィンドウマネージャ +Comment[ka]=მინიმალისტური ფანჯრების მნეჯერი +Comment[kk]=Шағын терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចលក្ខណៈពិសេសតិច +Comment[ko]=최소 지향 창 관리자 +Comment[lt]=Minimalistinė langų tvarkyklė +Comment[lv]=Minimālistisks logu menedžeris +Comment[mk]=Минималистички менаџер на прозорци +Comment[mn]=Хамгийн жижиг цонхны удирдагч +Comment[mt]=Window manager żgħir +Comment[nb]=En minimalistisk vindusbehandler +Comment[nds]=En minimalistisch Finsterpleger +Comment[ne]=एक मिनिमलिस्ट सञ्झ्याल प्रबन्धक +Comment[nl]=Een minimalistische windowmanager +Comment[nn]=Ein minimalistisk vindaugssjef +Comment[pa]=ਇੱਕ ਹਲਕਾ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Prosty menedżer okien +Comment[pt]=Um gestor de janelas minimalista +Comment[pt_BR]=Um gerenciador de janelas minimalista +Comment[ro]=Un manager de ferestre minimal +Comment[ru]=Минимальный оконный менеджер +Comment[rw]=Mugenga dirishya igira-bito +Comment[se]=Minimalisttalaš lásegieđahalli +Comment[sk]=Minimálny správca okien +Comment[sl]=Minimalističen okenski upravitelj +Comment[sr]=Минималистички менаџер прозора +Comment[sr@Latn]=Minimalistički menadžer prozora +Comment[sv]=Minimalistisk fönsterhanterare +Comment[ta]=ஒரு சிறிதுப்படுத்தப்பட்ட சாளர மேலாளர் +Comment[tg]=Мудири тирезаи minimalist +Comment[th]=ระบบจัดการหน้าต่างขนาดเล็ก +Comment[tr]=Küçük bir pencere yöneticisi +Comment[tt]=Bik ciñel täräzä-idäräçe +Comment[uk]=Аскетичний менеджер вікон +Comment[uz]=Juda oddiy oyna boshqaruvchi +Comment[uz@cyrillic]=Жуда оддий ойна бошқарувчи +Comment[vi]=Trình quản lý cửa sổ đơn giản +Comment[wa]=On manaedjeu di purneas minimå +Comment[zh_CN]=最小的窗口管理器 +Comment[zh_TW]=一個最小型的視窗管理程式 diff --git a/kdm/kfrontend/sessions/afterstep.desktop b/kdm/kfrontend/sessions/afterstep.desktop new file mode 100644 index 000000000..c3f8d7329 --- /dev/null +++ b/kdm/kfrontend/sessions/afterstep.desktop @@ -0,0 +1,83 @@ +[Desktop Entry] +Type=XSession +Exec=afterstep +TryExec=afterstep +Name=AfterStep +Name[bn]=আফটার-স্টেপ +Name[eo]=Postpaŝo +Name[hi]=आफ्टर-स्टेप +Name[ne]=चरण पछाडि +Name[pa]=ਪਗਬਾਅਦ +Name[rw]=NyumaIntambwe +Name[sv]=Afterstep +Name[ta]=ஆஃப்டர்ஸ்டெப் +Name[te]=ఆఫ్టర్ స్టెప్ +Comment=A window manager with the NeXTStep look and feel, based on FVWM +Comment[af]='n Venster bestuurder wat NeXTStep naboots, gebaseer of FVWM +Comment[ar]=مدير نوافذ ذو مظهر شبيه بـNeXTStep، مبني على FVWM +Comment[be]=Кіраўнік вокнаў з вонкавым выглядам NeXTStep, заснаваны на FVWM +Comment[bn]=FVWM-এর ওপর ভিত্তি করে তৈরি একটি উইণ্ডো ম্যানেজার, যা দেখতে শুনতে অনেকটানেক্সট-স্টেপ (NeXTStep)-এর মত +Comment[bs]=Window manager sa NeXTStep izgledom i osjećajem, baziran na FVWM +Comment[ca]=Un gestor de finestres amb l'aspecte i comportament de NeXTStep, basat en FVWM +Comment[cs]=Správce oken podobný NeXTStepu založený na FVWM +Comment[csb]=Menedżer òknów jidący w szlach NeXTStep, ùsôdzony na spòdlém FVWM +Comment[cy]= Trefnydd ffenestri efo golwg a theimlad CamNesaf (NeXTStep), wedi'i seilio ar FVWM +Comment[da]=En vindueshåndtering med NeXTStep udseende, baseret på FVWM +Comment[de]=Fenstermanager mit der Optik von NeXTStep, basiert auf FVWM +Comment[el]=Ένας διαχειριστής παραθύρων με την όψη και αίσθηση του NeXTStep, βασισμένος στον FVWM +Comment[eo]=Fenestroadministrilo, kiu aperas kiel Venontpaŝo, farita de FVWM +Comment[es]=Un gestor de ventanas con el aspecto de NeXTStep, basado en FVWM +Comment[et]=Aknahaldur NeXTStep välimuse ja vaimuga, aluseks FVWM +Comment[eu]=FVWMn oinarritutako, eta NeXTStep-en itxura eta portaera duen leiho kudeatzailea +Comment[fa]=یک مدیر پنجره توسط ظاهر و احساس NeXTStep، براساس FVWM +Comment[fi]=NeXTStep-tyylinen ja -tuntuinen FVWM:ään pohjautuva ikkunaohjelma +Comment[fr]=Un gestionnaire de fenêtres similaire à NeXTStep et fondé sur FVWM +Comment[fy]=In finstersmanager mei it úterlik en gedrach fan NeXTStep; basearre op FVWM +Comment[gl]=Un xestor de fiestras de aspeito NeXTStep, baseado en FVWM +Comment[he]=מנהל חלונות עם מראה ומרגש כמו של NeXTStep המבוסס על, FVWM +Comment[hi]=एफ़वीडबल्यूएम आधारित नेक्स्टस्टेप की तरह दिखने और महसूस होने वाला विंडो प्रबंधक +Comment[hr]=Upravitelj prozora s NeXTStep izgledom i načinom rada, zasnovan na FVWM-u +Comment[hu]=Egy FVWM-alapú ablakkezelő, megjelenése a NeXTStepére hasonlít +Comment[is]=Gluggastjóri sem líkist þeim sem er í NeXTStep, byggður á FVWM +Comment[it]=Un window manager con lo stile NeXTStep, basato su FVWM +Comment[ja]=NeXTStep のルック&フィールをもった FVWM ベースのウィンドウマネージャ +Comment[ka]=ფანჯრის მენეჯერი FVWM -ის ბაზაზე, რომელიც NeXTStep-ს ჰგავს +Comment[kk]=FVWM-негіздеген, көрінісі NeXTStep-те сияқты, терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចមានរូបរាង និងមុខងារ NeXTStep ដែលផ្អែកលើ FVWM +Comment[ko]=FVWM 기반 NeXTStep 모양 창 관리자 +Comment[lt]=Langų tvarkyklė su NeXTStep išvaizda ir jausena, paremta FVWM +Comment[lv]=Logu menedžeris ar NeXTStep izskatu un izturēšanos, bāzēts uz FVWM +Comment[mk]=Менаџер на прозорци со изгледот и чувството на NeXTStep, базиран на FVWM +Comment[mn]=NeXTStep look болон feel, based бүхий FVWM-д суурилсан цонх удирдагч +Comment[ms]=Pengurus tetingkap dengan rupa dan rasa NeXTStep berdasarkan FVWM +Comment[mt]=Window manager ibbażat fuq FVWM, jixbaħ lil NeXTStep +Comment[nb]=En vindusbehandler som ligner på NeXTStep, basert på FVWM +Comment[nds]=En Finsterpleger mit dat Utsehn vun NeXTStep, opbuut op FVWM +Comment[ne]=FVWMA आधारित एउटा सञ्झ्याल प्रबन्धक NeXTStep मा हेर्छ र थाहा पाउँछ +Comment[nl]=Een windowmanager met het uiterlijk en gedrag van NeXTStep; gebaseerd op FVWM +Comment[nn]=Ein vindaugssjef som liknar på NeXTStep, basert på FVWM +Comment[pa]=NeXTStep ਦੀ ਦਿੱਖ ਤੇ ਦਰਿਸ਼ ਵਾਲਾ FVWM ਆਧਾਰਿਤ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Menedżer okien naśladujący NeXTStep, stworzony na podstawie FVWM +Comment[pt]=Um gestor de janelas com a aparência e comportamento do NeXTStep. Baseado no FVWM. +Comment[pt_BR]=Um gerenciador de janelas com a aparência do NeXTSep, baseado no FVWM +Comment[ro]=Un manager de ferestre cu aspect NeXTStep, bazat pe FVWM +Comment[ru]=Оконный менеджер на основе FVWM, повторяющий внешний вид NeXTStep +Comment[rw]=Mugenga dirishya ifite imboneko n'ukumva NtambweIkurikira, ishingiye kuri FVWM +Comment[se]=Lásegieđahalli mii sulastahttá NeXTStep, vuođđoduvvon FVWM:as +Comment[sk]=Správca okien podobný NeXTStep založený na FVWM +Comment[sl]=Okenski upravitelj z občutkom in izgledom NeXTStep-a, na osnovi FVWM +Comment[sr]=Менаџер прозора са NeXTStep-овим изгледом и осећајем, заснован на FVWM-у +Comment[sr@Latn]=Menadžer prozora sa NeXTStep-ovim izgledom i osećajem, zasnovan na FVWM-u +Comment[sv]=Fönsterhanterare med Nextstep-utseende och -känsla, baserad på FVWM +Comment[ta]=FVWM அடிப்படையிலான NeXTStep உடனான சாளர மேலாளர். +Comment[tg]=Мудири равзанаҳо дар асоси FVWM дорои намуди NeXTStep +Comment[th]=ระบบจัดการหน้าต่างที่มีรูปแบบและสัมผัสของระบบปฏิบัติการ NeXTStep โดยใช้พื้นฐานของ FVWM +Comment[tr]=NeXTStep görünümlü bir pencere yöneticisi +Comment[tt]=FVWM asılında qorılğan, NeXTStep küreneşe belän täräzä-idäräçe +Comment[uk]=Менеджер вікон з виглядом та поведінкою NeXTStep, заснований на FVWM +Comment[uz]=FVWM asosida yaratilgan NeXTStep'ga oʻxshash oyna boshqaruvchi +Comment[uz@cyrillic]=FVWM асосида яратилган NeXTStep'га ўхшаш ойна бошқарувчи +Comment[vi]=Trình quản lý cửa sổ với giao diện NeXTStep, dựa trên FVWM +Comment[wa]=On manaedjeu di purneas avou l' rivnance di NeXTStep, båzé so FVWM +Comment[zh_CN]=一个带有 NeXTStep 观感的窗口管理器,基于 FVWM +Comment[zh_TW]=一個基於 FVWM 並擁有 NeXTStep 外觀及感覺的視窗管理程式 diff --git a/kdm/kfrontend/sessions/amaterus.desktop b/kdm/kfrontend/sessions/amaterus.desktop new file mode 100644 index 000000000..3c52ee180 --- /dev/null +++ b/kdm/kfrontend/sessions/amaterus.desktop @@ -0,0 +1,75 @@ +[Desktop Entry] +Type=XSession +Exec=amaterus +TryExec=amaterus +Name=AMATERUS +Name[hi]=एमेच्योर्स +Name[te]=అమాటెరస్ +Comment=A GTK+ based window manager with a window grouping feature +Comment[af]='n GTK+ gebaseerde venster bestuurder met 'n venster groepering funksie +Comment[ar]=مدير نوافذ مبني على GTK+ له ميزة تجميع النوافذ +Comment[be]=Кіраўнік вокнаў, заснаваны на GTK+, са здольнасцю групавання вокнаў +Comment[bn]=GTK+ ভিত্তিল উইণ্ডো ম্যানেজার, যাতে উইণ্ডো গ্রুপিং করা যায় +Comment[bs]=Window manager baziran na GTK+ sa mogućnošću grupisanja prozora +Comment[ca]=Un gestor de finestres de GTK+ amb una característica per a l'agrupament de finestres +Comment[cs]=Správce oken založený na GTK+ s funkcí seskupování okne +Comment[csb]=Menedżer òknów brëkùjący GTK+, z optacëją grëpòwaniô òknów +Comment[cy]=Trefnydd ffenestri wedi'i seilio ar GTK+, efo nodwedd casglu ffenestri +Comment[da]=En GTK+ baseret vindueshåndtering med en vinduesgrupperingsegenskab +Comment[de]=Auf GTK+ basierender Fenstermanager mit Gruppierungsfunktion für die Fenster +Comment[el]=Ένας διαχειριστής παραθύρων βασισμένος στο GTK+ με ένα χαρακτηριστικό ομαδοποίησης παραθύρων +Comment[eo]=Fenestroadministrilo kun ebleco kunigi fenestrojn, kiu uzas GTK+ +Comment[es]=Un gestor de ventanas basado en GTK+ con la posibilidad de agrupar ventanas +Comment[et]=GTK+-le tuginev aknahaldur akende grupeerimise võimalusega +Comment[eu]=GTK+-n oinarritutako leiho kudeatzailea, leihoak taldeka biltzeko gaitasuna duena +Comment[fa]=یک GTK+ بر مبنای مدیر پنجره توسط ویژگی گروهی کردن پنجرهها +Comment[fi]=GTK+-pohjainen ikkunaohjelma ikkunoiden ryhmittely -ominaisuudella +Comment[fr]=Un gestionnaire de fenêtres écrit en GTK+, avec une fonctionnalité de groupement des fenêtres +Comment[fy]=In op GTK+ basearre finstersmanager mei finsterkeppelfûnksje +Comment[gl]=Un xestor de fiestras baseado en GTK+ con agrupamento de fiestras +Comment[he]=מנהל חלונות מבוסס GTK+ עם אפשרות לקבץ חלונות +Comment[hi]=जीटीके+ आधारित विंडो प्रबंधक, विंडो समूह विशेषता सहित +Comment[hr]=Upravitelj prozora zasnovan na GTK+ s mogućnošću grupiranja prozora +Comment[hu]=Egy GTK+-alapú ablakkezelő ablakcsoportosítási lehetőséggel +Comment[is]=Gluggastjóri sem hópar saman glugga og er byggður á GTK+ +Comment[it]=Un window manager basato su GTK+ con la possibilità di raggruppare le finestre +Comment[ja]=ウィンドウのグループ化が可能な GTK+ ベースのウィンドウマネージャ +Comment[ka]=ფანჯრის მენეჯერი GTK+ ს ბაზაზე ფანჯრების დაჯგუფების ფუნქციით +Comment[kk]=GTK+ негіздеген, терезелерді топтастыру қасиеті бар, терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចផ្អែកលើ GTK+ ដែលមានលក្ខណៈពិសេសដាក់បង្អួចជាក្រុម +Comment[ko]=창 그룹 기능을 사용하는 GTK+ 기반 창 관리자 +Comment[lt]=GTK+ paremta langų tvarkyklė su langų grupavimo galimybe +Comment[lv]=GTK+ bāzēts logu menedžeris ar logu grupēšanas iespēju +Comment[mk]=GTK+ базиран менаџер на прозорци со можност за групирање на прозорци +Comment[mn]=A GTK+ суурилсан цонх бүлэглэгчтэй цонх удирдагч +Comment[mt]=Window manager ibbażat fuq GTK+ b'faċilitajiet ta' gruppi ta' windows +Comment[nb]=En vindusbehandler basert på GTK+ med vindusgruppering +Comment[nds]=En Finsterpleger opbuut op GTK+, kann Finstern in Koppeln tosamenfaten +Comment[ne]=सञ्झ्याल समूह विशेषतासँग GTK+ मा आधारित सञ्झ्याल प्रबन्धक +Comment[nl]=Een op GTK+ gebaseerde windowmanager met venstergroeperingfunctionaliteit +Comment[nn]=Ein GTK+-basert vindaugssjef med vindaugsgruppering +Comment[pa]=ਇੱਕ GTK+ ਤੇ ਆਧਾਰਿਤ ਝਰੋਖਾ ਮੈਨੇਜਰ, ਜੋ ਕਿ ਝਰੋਖਿਆਂ ਨੂੰ ਇੱਕ ਥਾਂ ਸੰਭਾਲਣ ਦੀ ਸਹੂਲਤ ਨਾਲ ਲੈੱਸ ਹੈ। +Comment[pl]=Menedżer okien korzystający z GTK+, z opcją grupowania okien +Comment[pt]=Um gestor de janelas baseado em GTK+ com uma funcionalidade de agrupamento de janelas +Comment[pt_BR]=Um gerenciador de janelas baseado em GTK+, com o recurso de agrupamento de janelas +Comment[ro]=Un manager de ferestre bazat pe GTK+ și cu posibilitatea de grupare a ferestrelor +Comment[ru]=Оконный менеджер на основе GTK+ c функцией группировки окон +Comment[rw]=GTK + bishingiye mugenga dirishya ifite idirishya rihuza ibiranga +Comment[se]=GTK+-vuođđoduvvon lásegieđahalli mii sáhttá sierra lásiid bidjat seamma joavkui +Comment[sk]=Správca okien založený na GTK+ s funkciou zoskupovania okien +Comment[sl]=Okenski upravitelj na osnovi GTK+ z možnostjo združevanja oken +Comment[sr]=Менаџер прозора заснован на GTK+-у са особином груписања прозора +Comment[sr@Latn]=Menadžer prozora zasnovan na GTK+-u sa osobinom grupisanja prozora +Comment[sv]=GTK+-baserad fönsterhanterare med en funktion för fönstergruppering +Comment[ta]=சாளர குழுப்பிரித்தல் தன்மையுடனான சாளர மேலாளர் அடிப்படையிலான GTK+. +Comment[tg]=Мудири равзанаҳо дар асоси GTK+ дорои фаъолияти гурӯҳ кардан равзанаҳо +Comment[th]=ตัวจัดการหน้าต่างที่ใช้พื้นฐานจาก GTK+ พร้อมด้วยความสามารถในการจัดกลุ่มหน้าต่าง +Comment[tr]=GTK+ tabanlı bir pencere yöneticisi +Comment[tt]=GTK+ asılında qorılğan, täräzälärne törkemli ala torğan täräzä-idäräçe +Comment[uk]=Менеджер вікон заснований на GTK+ з підтримкою групування вікон +Comment[uz]=A GTK+ asosida yaratilgan, oynalarni guruhlash imkoniyatiga ega oyna boshqaruvchi +Comment[uz@cyrillic]=A GTK+ асосида яратилган, ойналарни гуруҳлаш имкониятига эга ойна бошқарувчи +Comment[vi]=Trình quản lý cửa sổ với khả năng tạo nhóm cửa sổ dựa trên GTK+ +Comment[wa]=On manaedjeu di purneas båzé so GTK+, avou ene fonccionålité di rgroupaedje des purneas +Comment[zh_CN]=一个基于 GTK+ 的窗口管理器,带有窗口成组特性 +Comment[zh_TW]=一個基於 GTK+ 的視窗管理程式並擁有視窗群組功能 diff --git a/kdm/kfrontend/sessions/amiwm.desktop b/kdm/kfrontend/sessions/amiwm.desktop new file mode 100644 index 000000000..ced73c346 --- /dev/null +++ b/kdm/kfrontend/sessions/amiwm.desktop @@ -0,0 +1,78 @@ +[Desktop Entry] +Type=XSession +Exec=amiwm +TryExec=amiwm +Name=AmiWM +Name[eo]=AmiFA +Name[hi]=एमी-डबल्यूएम +Name[sv]=Ami WM +Name[te]=ఎమి డబ్ల్యు ఎం +Comment=The Amiga look-alike window manager +Comment[af]='n Amiga gebaseerde venster bestuurder +Comment[ar]=مدبِر نوافذ مشابه لِــ Amiga +Comment[be]=Кіраўнік вокнаў, падобны на Amiga +Comment[bn]=অ্যামিগার মত দেখতে উইণ্ডো ম্যানেজার +Comment[bs]=Window manager nalik na Amigu +Comment[ca]=Un gestor de finestres que dona l'aspecte d'un Amiga +Comment[cs]=Správce oken podobný Amize +Comment[csb]=Menedżer òknów szlachùjący za Amiga +Comment[cy]=Trefnydd ffenestri sy'n edrych yn debyg i'r Amiga +Comment[da]=Amiga-lignende vindueshåndtering +Comment[de]=Fenstermanager im Stil des Amiga +Comment[el]=Ο διαχειριστής παραθύρων με όψη αλά Amiga +Comment[eo]=Fenestroadministrilo kiel tiu de Amiga +Comment[es]=Un gestor de ventanas con el aspecto de Amiga +Comment[et]=Amiga välimusega aknahaldur +Comment[eu]=Amigaren itxura duen leiho kudeatzailea +Comment[fa]=Amiga شبیه مدیر پنجره +Comment[fi]=Amigan tyylinen ikkunaohjelma +Comment[fr]=Le gestionnaire de fenêtres ressemblant à Amiga +Comment[fy]=In Amiga-likens finstersmanager +Comment[gl]=Un xestor de fiestras coma o de Amiga +Comment[he]=מנהל החלונות הדומה ל־Amiga +Comment[hi]=अमीगा की तरह दिखने वाला विंडो प्रबंधक +Comment[hr]=Upravitelj prozora koji podsjeća na Amigu +Comment[hu]=Egy Amiga-szerű ablakkezelő +Comment[is]=Gluggastjóri sem líkist Amiga tölvunum +Comment[it]=Un window manager in stile Amiga +Comment[ja]=Amiga に似たウィンドウマネージャ +Comment[ka]=ფანჯრის მენეჯერი Amiga-ს სტილში +Comment[kk]=Amiga секілді терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រប់បង្អួចស្រដៀង Amiga +Comment[ko]=Amiga를 닮은 창 관리자 +Comment[lt]=Langų tvarkyklė, panaši į Amiga +Comment[lv]=Amiga izskata logu menedžeris +Comment[mk]=Менаџер на прозорци со изглед на Amiga +Comment[mn]=Amiga look-alike Цонхны удирдагч +Comment[ms]=Pengurus tetingkap serupa Amiga +Comment[mt]=Window manager jixbaħ lill-Amiga +Comment[nb]=Vindusbehandler som ligner på Amiga +Comment[nds]=De Finsterpleger mit dat Utsehn vun den Amiga +Comment[ne]=अमिगा सञ्झ्याल प्रबन्धक जस्तो छ +Comment[nl]=Een Amiga-achtige windowmanager +Comment[nn]=Vindaugssjef som liknar på Amiga +Comment[pa]=ਅਮੀਗਾ ਵਰਗਾ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Menedżer okien naśladujący Amigę +Comment[pt]=O gestor de janelas com o visual do Amiga +Comment[pt_BR]=Um gerenciador de janelas com a aparência do Amiga +Comment[ro]=Manager de ferestre cu aspect Amiga +Comment[ru]=Оконный менеджер в стиле Amiga +Comment[rw]=Amiga ijya gusa na mugenga dirishya +Comment[se]=Amiga-lágan lásegieđahalli +Comment[sk]=Správca okien podobný systému Amiga +Comment[sl]=Okenski upravitelj, podoben Amiginemu +Comment[sr]=Менаџер прозора који подсећа на Амигу +Comment[sr@Latn]=Menadžer prozora koji podseća na Amigu +Comment[sv]=Fönsterhanteraren som ser ut som Amiga +Comment[ta]=சாளர மேலாளரை ஒத்த அமிகா +Comment[tg]=Монанди мудири тирезаи Amiga look +Comment[th]=ระบบจัดการหน้าต่างที่ดูเหมือน Amiga +Comment[tr]=Amiga görünümlü bir pencere yöneticisi +Comment[tt]=Amiga küreneşendä täräzä idäräçe +Comment[uk]=Менеджер вікон на штиб Amiga +Comment[uz]=Amiga'ga oʻxshash oyna boshqaruvchi +Comment[uz@cyrillic]=Amiga'га ўхшаш ойна бошқарувчи +Comment[vi]=Trình quản lý cửa sổ nhìn giống Amiga +Comment[wa]=On manaedjeu di purneas rishonnant l' ci di l' Amiga +Comment[zh_CN]=Amiga 外观的窗口管理器 +Comment[zh_TW]=一個看起來像 Amiga 視窗管理程式 diff --git a/kdm/kfrontend/sessions/asclassic.desktop b/kdm/kfrontend/sessions/asclassic.desktop new file mode 100644 index 000000000..e84a0977e --- /dev/null +++ b/kdm/kfrontend/sessions/asclassic.desktop @@ -0,0 +1,81 @@ +[Desktop Entry] +Type=XSession +Exec=asclassic +TryExec=asclassic +Name=ASClassic +Name[af]= ASClassic +Name[cy]=ASClasurol +Name[eo]=KlasikaPP +Name[hi]=एएस-क्लॉसिक +Name[ne]=AS शास्त्रीय +Name[sv]=AS klassisk +Name[ta]=ASதரமான +Name[te]=ఏఎస్ క్లాసిక్ +Comment=AfterStep Classic, a window manager based on AfterStep v1.1 +Comment[af]=AfterStep Classic, 'n venster bestuurder wat op AfterStep v1.1 gebaseer is. +Comment[ar]=AfterStep كلاسيك، وهو مدير نوافذ مبني على AfterStep الإصدارة 1.1 +Comment[be]=Класічны AfterStep, кіраўнік вокнаў, заснаваны на AfterStep 1.1 +Comment[bn]=আফটার-স্টেপ ক্লাসিক: আফটার-স্টেপ ১.১ ভিত্তিক একটি উইণ্ডো ম্যানেজার +Comment[bs]=AfterStep Classic, window manager baziran na AfterStep v1.1 +Comment[ca]=El clàssic AfterStep, un gestor de finestres basat en AfterStep v1.1 +Comment[cs]=AfterStep Classic, správce oken založený na AfterStepu v1.1 +Comment[csb]=AfterStep Classic, menedżer òknów ùsôdzony na spòdlém AfterStep v1.1 +Comment[cy]=ÔlGam Clasurol, trefnydd ffenestri wedi'i seilio ar AfterStep v1.1 +Comment[da]=AfterStep Classic, en vindueshåndtering baseret på AfterStep v1.1 +Comment[de]=AfterStep Classic, ein Fenstermanager, der auf AfterStep v1.1 basiert +Comment[el]=AfterStep κλασικός, ένας διαχειριστής παραθύρων βασισμένος στον AfterStep v1.1 +Comment[eo]=Klasika Postpaŝo, fenestroadministrilo kiel Postpaŝo v1.1 +Comment[es]=AfterStep Classic, un gestor de ventanas basado en AfterStep v1.1 +Comment[et]=AfterStep Classic - aknahaldur, mille aluseks on AfterStep v1.1 +Comment[eu]=AfterStep Classic, AfterStep v1.1-en oinarrituta dagoen leiho kudeatzailea +Comment[fa]=AfterStep کلاسیک، مدیر پنجره بر اساس AfterStep نسخه ۱.۱ +Comment[fi]=AfterStep Classic, After Step v1.1:een pohjautuva ikkunaohjelma +Comment[fr]=AfterStep Classic, un gestionnaire de fenêtres fondé sur AfterStep v1.1 +Comment[fy]=AfterStep Classic, In finstersmanager basearre op AfterStep 1.1 +Comment[gl]=AfterStep Clásico, un xestor de fiestras baseado en AfterSetp v1.1 +Comment[he]=AfterStep Classic, מנהל חלונות המבוסס על AfterStep v1.1 +Comment[hi]=आफ्टरस्टेप क्लासिक, एक विंडो प्रबंधक जो आफ्टरस्टेप व.1 पर आधारित है +Comment[hr]=Klasični AfterStep, upravitelj prozora zasnovan na AfterStepu verzija 1.1 +Comment[hu]=AfterStep Classic ablakkezelő, az AfterStep v1.1 alapján +Comment[is]=Klassískur AfterStep gluggastjóri byggður á AfterStep v1.1 +Comment[it]=AfterStep Classico, un window manager basato su AfterStep v1.1 +Comment[ja]=AfterStep クラシック, AfterStep v1.1 ベースのウィンドウマネージャ +Comment[ka]=AfterStep Classic, ფანჯრის მენეჯერი AfterStep v1.1 -ის ბაზაზე +Comment[kk]=AfterStep v1.1 негіздеген AfterStep Classic терезе менеджері +Comment[km]=AfterStep បុរាណ,កម្មវិធីគ្រប់គ្រងបង្អួចដែលផ្អែកលើ AfterStep v1.1 +Comment[ko]=AfterStrp 1.1 기반 창 관리자 +Comment[lt]=AfterStep Classic, langų tvarkyklė, paremta AfterStep v1.1 +Comment[lv]=Klasiskais Afterstep, logu menedžeris bāzēts uz AfterStem v1.1 +Comment[mk]=AfterStep Classic, менаџер на прозорци базиран на AfterStep v1.1 +Comment[mn]=AfterStep Classic, AfterStep v1.1 дээр суурилсан цонх удирдагч +Comment[mt]=AfterStep klassiku, window manager ibbażat fuq AfterStep v1.1 +Comment[nb]=AfterStep Classic, en vindusbehandler basert på AfterStep v1.1 +Comment[nds]=De AfterStepClassic-Finsterpleger is opbuut op AfterStep v1.1 +Comment[ne]=AfterStep शास्त्रीय, AfterStep v१.१ मा आधारित सञ्झ्याल प्रबन्धक +Comment[nl]=AfterStep Classic, een windowmanager gebaseerd op AfterStep 1.1 +Comment[nn]=AfterStep Classic, ein vindaugssjef som byggjer på AfterStep 1.1 +Comment[pa]=AfterStep ਟਕਸਾਲੀ, AfterStep v1.1 ਤੇ ਆਧਾਰਿਤ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=AfterStep Classic, menedżer okien stworzony na podstawie AfterStep v1.1 +Comment[pt]=AfterStep Classic, um gestor de janelas baseado no AfterStep v1.1 +Comment[pt_BR]=AfterSep clássico, um gerenciador de janelas baseado no AfterStep v1.1 +Comment[ro]=AfterStep Classic, un manager de ferestre bazat pe AfterStep v1.1 +Comment[ru]=AfterStep Classic, оконный менеджер на основе AfterStep v1.1 +Comment[rw]=AfterStep Classic, mugenga dirishya ishingiye kuri AfterStep v1.1 +Comment[se]=AfterStep Classic, lásegieđahalli ráhkaduvvon AfterStep 1.1 vuođul +Comment[sk]=AfterStep Classic, správca okien založený na AfterStep v1.1 +Comment[sl]=AfterStep Classic, okenski upravitelj na osnovi AfterStep različice 1.1 +Comment[sr]=Класични AfterStep, менаџер прозора заснован на AfterStep-у верзије 1.1 +Comment[sr@Latn]=Klasični AfterStep, menadžer prozora zasnovan na AfterStep-u verzije 1.1 +Comment[sv]=Afterstep klassisk, en fönsterhanterare baserad på Afterstep version 1.1 +Comment[ta]=ஆஃப்டர்ஸ்டெப் க்ளாசிக், ஆஃப்டர்ஸ்டெப் க்ளாசிக் v1.1 அடிப்படையிலான சாளர மேலாளர் +Comment[tg]=Мудири равзанаҳои AfterStep Classic дар асоси AfterStep v1.1 +Comment[th]=AfterStep Classic คือระบบจัดการหน้าต่างที่ใช้ฐานของอาฟเตอร์สเตปเวอร์ชั่น 1.1 +Comment[tr]=AfterStep Classic pencere yöneticisi +Comment[tt]=AfterStep Classic, AfterStep v1.1 asılında täräzä-idäräçe +Comment[uk]=AfterStep Classic, менеджер вікон, заснований на AfterStep v1.1 +Comment[uz]=AfterStep Classic - AfterStep v1.1 asosida yaratilgan oyna boshqaruvchi +Comment[uz@cyrillic]=AfterStep Classic - AfterStep v1.1 асосида яратилган ойна бошқарувчи +Comment[vi]=AfterStep Classic, một trình quản lý cửa sổ dựa trên AfterStep v1.1 +Comment[wa]=AfterStep Classic, on manaedjeu di purneas båzé so AfterStep v1.1 +Comment[zh_CN]=AfterStep 经典,一个基于 AfterStep v1.1 的窗口管理器 +Comment[zh_TW]=AfterStep 經典, 一個基於 AfterStep v1.1 的視窗管理程式 diff --git a/kdm/kfrontend/sessions/blackbox.desktop b/kdm/kfrontend/sessions/blackbox.desktop new file mode 100644 index 000000000..457ec7614 --- /dev/null +++ b/kdm/kfrontend/sessions/blackbox.desktop @@ -0,0 +1,88 @@ +[Desktop Entry] +Type=XSession +Exec=blackbox +TryExec=blackbox +Name=Blackbox +Name[bn]=ব্ল্যাকবক্স +Name[cy]= Du-flwch (Blackbox) +Name[eo]=Negrujo +Name[hi]=ब्लेकबॉक्स +Name[ja]=BlackBox +Name[mn]=Хар хайрцаг +Name[ne]=कालो बाकस +Name[pa]=ਕਾਲਾਬਕਸਾ +Name[rw]=AgasandukuUmukara +Name[ta]=கறுப்புப் பெட்டி +Name[te]=నల్లడబ్బా +Name[tg]=Қуттии сиёҳ +Comment=A fast & light window manager +Comment[af]='n Vinnige, lig gewig venster bestuurder +Comment[ar]=مدير نوافذ خفيف وسريع +Comment[be]=Хуткі і лёгкі кіраўнік вокнаў +Comment[bg]=Бърз и лек мениджър на прозорци +Comment[bn]=একটি হালকা এবং দ্রুত উইণ্ডো ম্যানেজার +Comment[bs]=Brz i lagan window manager +Comment[ca]=Un gestor de finestres ràpid i clar +Comment[cs]=Rychlý a malý správce oken +Comment[csb]=Chùtczi menedżer òknów o môłëch żądaniach +Comment[cy]=Trefnydd ffenestri cyflym ac ysgafn +Comment[da]=En hurtig & let vindueshåndtering +Comment[de]=Kleiner, schneller Fenstermanager +Comment[el]=Ένας γρήγορος και ελαφρύς διαχειριστής παραθύρων +Comment[eo]=Rapida kaj malpeza fenestroadministrilo +Comment[es]=Un gestor de ventanas rápido y ligero +Comment[et]=Kiire ja vähenõudlik aknahaldur +Comment[eu]=Leiho kudeatzaile bizkor eta arina +Comment[fa]=یک مدیر پنجرۀ سبک و سریع +Comment[fi]=Kevyt ja nopea ikkunaohjelma +Comment[fr]=Un gestionnaire de fenêtres rapide et léger +Comment[fy]=In flugge lichtgewicht finstersmanager +Comment[ga]=Bainisteoir fuinneoga gasta éadrom +Comment[gl]=Un xestor de fiestras lixeiro e rápido +Comment[he]=מנהל חלונות מהיר וקל +Comment[hi]=तेज और सरल विंडो प्रबंधक +Comment[hr]=Brzi i lagani upravitelj prozora +Comment[hu]=Egy gyors, egyszerű ablakkezelő +Comment[is]=Léttur og hraðvirkur gluggastjóri +Comment[it]=Un window manager veloce e leggero +Comment[ja]=軽快なウィンドウマネージャ +Comment[ka]=სწრაფი და მსუბუქი ფანჯრის მენეჯერი +Comment[kk]=Жедел және жеңіл терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចលឿន ហើយភ្លឺ +Comment[ko]=빠르고 가벼운 창 관리자 +Comment[lt]=Greita ir nedaug resursų naudojanti langų tvarkyklė +Comment[lv]=Ātrs un viegls logu menedžeris +Comment[mk]=Брз и лесен менаџер на прозорци +Comment[mn]=Хурдан & хөнгөн цонхны удирдагч +Comment[ms]=Pengurus tetingkap yang pantas & ringan +Comment[mt]=Window manager ħafif u żgħir +Comment[nb]=En rask og lett vindusbehandler +Comment[nds]=En gaue un lütte Finsterpleger +Comment[ne]=छिटो र हल्का सञ्झ्याल प्रबन्धक +Comment[nl]=Een snelle lichtgewicht windowmanager +Comment[nn]=Ein rask og lett vindaugssjef +Comment[pa]=ਇੱਕ ਤੇਜ਼ ਅਤੇ ਹਲਕਾ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Szybki menedżer okien o małych wymaganiach +Comment[pt]=Um gestor de janelas rápido e leve +Comment[pt_BR]=Um gerenciador de janelas rápido e leve +Comment[ro]=Un manager de ferestre mic și rapid +Comment[ru]=Быстрый и лёгкий оконный менеджер +Comment[rw]=Mugenga dirishya yihuta & yoroshye +Comment[se]=Jođánis ja geahpes lásegieđahalli +Comment[sk]=Rýchly a nenáročný správca okien +Comment[sl]=Hiter in lahek okenski uporavljalnik +Comment[sr]=Брзи и лагани менаџер прозора +Comment[sr@Latn]=Brzi i lagani menadžer prozora +Comment[sv]=Snabb och lättviktig fönsterhanterare +Comment[ta]=விரைவான மற்றும் இலகுவான KDE சாளர மேலாளர் +Comment[tg]=Суст ва мудири тирезаи равшан +Comment[th]=ระบบจัดการหน้าต่างที่เร็วและเบา +Comment[tr]=Hızlı ve hafif bir pencere yöneticisi +Comment[tt]=Citez ciñel täräzä-idäräçe +Comment[uk]=Легкий та швидкий менеджер вікон +Comment[uz]=Tez va oddiy oyna boshqaruvchi +Comment[uz@cyrillic]=Тез ва оддий ойна бошқарувчи +Comment[vi]=Một trình quản lý cửa sổ nhẹ và nhanh +Comment[wa]=On ledjir et roed manaedjeu di purneas +Comment[zh_CN]=又快又轻巧的窗口管理器 +Comment[zh_TW]=一個快速及輕量化的視窗管理程式 diff --git a/kdm/kfrontend/sessions/cde.desktop b/kdm/kfrontend/sessions/cde.desktop new file mode 100644 index 000000000..1f2dee088 --- /dev/null +++ b/kdm/kfrontend/sessions/cde.desktop @@ -0,0 +1,74 @@ +[Desktop Entry] +Type=XSession +Exec=/usr/dt/bin/Xsession +TryExec=/usr/dt/bin/Xsession +Name=CDE +Name[hi]=सीडीई +Name[mn]=КДE +Name[te]=సిడిఈ +Name[tg]=Муҳити графикии муштарак (CDE) +Name[th]=แบบ CDE +Comment=The Common Desktop Environment, a proprietary industry standard desktop environment +Comment[af]=Die 'Common Desktop Environment', 'n beskermde, industrie standaard werkskerm omgewing +Comment[ar]=محيط سطح المكتب الشائع، محيط سطح المكتب الصناعي المعايير +Comment[be]=Common Desktop Environment, прапрыетарнае стандартнае працоўнае асяроддзе +Comment[bn]=কমন ডেস্কটপ এনভায়রনমেন্ট (Common Desktop Environment), একটি মালিকানাধীন ইনডাস্ট্রি স্ট্যান্ডার্ড +Comment[bs]=Common Desktop Environment, vlasnička desktop okolina, industrijski standard +Comment[ca]=The Common Desktop Environment, l'entorn d'escriptori estàndard de la indústria propietària +Comment[csb]=Common Desktop Environment, sztandardowé industrëjné òkrãże pùltu +Comment[cy]=Yr Amgylchedd Penbwrdd Cyffredin (Common Desktop Environment), amgylchedd penbwrdd perchnogol sy'n safonol yn y diwydiant +Comment[da]=Common Desktop Environment, et privatejet industristandard desktopmiljø +Comment[de]=Das Common Desktop Environment, eine proprietäre Arbeitsumgebung und ein Industriestandard +Comment[el]=To Κοινό Περιβάλλον Επιφάνειας εργασίας, ένα βιομηχανικό πρότυπο επιφάνειας εργασίας +Comment[eo]=La Komuna Labortablo Ĉirkaŭaĵo +Comment[es]=El Common Desktop Environment, un estándar en los entornos de escritorio propietarios +Comment[et]=Üldine töölaua keskond (Common Desktop Environment) on kaubanduslik standardne töölaua keskkond +Comment[eu]=Common Desktopo Environment, mahaigain jabedun inguruneetako estandarra +Comment[fa]=محیط رومیزی مشترک، محیط رومیزی استاندارد صنعت اختصاصی +Comment[fi]=Common Desktop Environment, patentoitu työpöytäympäristöjen teollisuusstandardi +Comment[fr]=Le Common Desktop Environment, un environnement de bureau propriétaire standard dans l'industrie +Comment[fy]=The Common Desktop Environment, In kommersjele yndustrieel standerdisearre buroblêd omwrâld +Comment[gl]=O Common Desktop Environment, un entorno de escritório proprietario estándar para industria +Comment[he]=The Common Desktop Environment, סביבת עבודה מסחרית וקניינית סטנדרטית +Comment[hi]=सामूहिक डेस्कटॉप माहौल, एक स्वामित्व युक्त औद्योगिक मानक डेस्कटॉप माहौल +Comment[hr]=Opće okruženje radne površine, standardizirano industrijskim vlasništvima +Comment[hu]=The Common Desktop Environment, egy kereskedelmi, kváziszabványnak számító grafikus környezet +Comment[is]=Common Desktop Environment er lokað skjáborðsumhverfi sem var staðlað umhverfi til skamms tíma +Comment[it]=Il Common Desktop Environment, un desktop environment proprietario standard. +Comment[ja]=Common Desktop Environment,プロプライエタリな業界標準のデスクトップ環境 +Comment[ka]=Common Desktop Environment, UNIX -ის სამუშაო სფეროს სამრეწვლო სტანდარტი +Comment[kk]=Common Desktop Environment - UNIX жұмыс ортаның өнеркәсіп стандарты +Comment[km]=The Common Desktop Environment, បរិស្ថានផ្ទៃតុខ្នាតគំរូដែលមានកម្មសិទ្ធិ +Comment[lt]=Common Desktop Environment, nuosavybinių sistemų standartinė darbastalio tvarkyklė +Comment[mk]=Common Desktop Environment, сопственичка индустриски стандардна работна околина +Comment[mn]=Нийтлэг дэлгэцийн системийн орчин, үйлдвэрийн стандарт дэлгэцийн системийн орчин +Comment[ms]=Persekitaran Desktop Biasa, persekitaran desktop standard industri proprietari +Comment[mt]=Common Desktop Environment, ambjent grafiku propjetarju u standard tal-industrija +Comment[nb]=Common desktop Environment, et godseid skrivebordsmiljø som er standard i programvareindustrien +Comment[nds]=De Common Desktop Environment, de Schriefdisch-Ümgeven vun en proprieteren Industrie-Standard +Comment[ne]=साझा डेस्कटप वातावरण, श: शुल्क उद्योग मानक डेस्कटप वातावरण +Comment[nl]=The Common Desktop Environment, een commerciële industrieel gestandariseerde desktop environment +Comment[nn]=Common Desktop Environment, eit godseigd skrivebordsmiljø som er standard i programvareindustrien +Comment[pa]=ਇੱਕ ਆਮ ਵੇਹੜਾ ਵਾਤਾਵਰਣ, ਇੱਕ ਵਪਾਰਿਕ ਮਿਆਰ ਦਾ ਵੇਹੜਾ ਵਾਤਾਵਰਣ +Comment[pl]=Common Desktop Environment, standardowe przemysłowe środowisko pulpitu +Comment[pt]=O Common Desktop Environment, um ambiente de trabalho gráfico padrão e proprietário +Comment[pt_BR]=O Ambiente de Trabalho Comum (CDE), um ambiente de trabalho proprietário padrão da indústria +Comment[ro]=Common Desktop Environment, un mediu grafic proprietar și devenit standard industrial +Comment[ru]=Common Desktop Environment, промышленный стандарт рабочей среды UNIX +Comment[rw]=Ibikikije Ibiro Rusange, ibikikije ibiro bisanzwe bwite by'isosiyete +Comment[se]=Common Desktop Environment, čállinbeavdebiras mii lea standárda prográmmagálvoindustriijas +Comment[sk]=The Common Desktop Environment, proprietárne štandardné pracovné prostredie +Comment[sl]=Common Desktop Environment, lastniško standardno industrijsko namizno okolje +Comment[sr]=„Common Desktop Environment“, власничко индустријски стандардно радно окружење +Comment[sr@Latn]=„Common Desktop Environment“, vlasničko industrijski standardno radno okruženje +Comment[sv]=Common Desktop Environment, en privatägd industristandard skrivbordsmiljö +Comment[ta]=பொதுவான மேல்மேசை, தன்உரிமை உடைய நிறுவனத்தின் நிலையான மேல்மேசை சூழல் +Comment[tg]=Common Desktop Environment дар асоси UNIX +Comment[th]=Common Desktop Environment คือ สภาพแวดล้อมของพื้นที่ทำงานที่ได้มาตรฐานอุตสาหกรรม ที่ไม่ใช่ซอฟต์แวร์เสรี +Comment[tr]=Common Desktop Environment (CDE) +Comment[tt]=Common Desktop Environment, UNIX öçen citeşterü standardı +Comment[uk]=The Common Desktop Environment, закритий промисловий стандарт графічного середовища +Comment[vi]=Môi trường Màn hình nền Chung, một môi trường màn hình nền giữ bản quyền, tuân thủ chuẩn công nghiệp +Comment[wa]=Li Comon Evironmint d' Sicribanne (Common Desktop Environment), on evironmint d' sicribanne nén libe po l' industreye +Comment[zh_CN]=通用窗口环境(CDE),私有工业标准的桌面环境 +Comment[zh_TW]=The Common Desktop Environment, 一個有專利的工業標準 diff --git a/kdm/kfrontend/sessions/ctwm.desktop b/kdm/kfrontend/sessions/ctwm.desktop new file mode 100644 index 000000000..e7ee84471 --- /dev/null +++ b/kdm/kfrontend/sessions/ctwm.desktop @@ -0,0 +1,72 @@ +[Desktop Entry] +Type=XSession +Exec=ctwm +TryExec=ctwm +Name=CTWM +Name[eo]=TFAC +Name[hi]=सीटीडबल्यूएम +Name[te]=సి టి డబ్ల్యు ఎం +Comment=Claude's Tab Window Manager, TWM enhanced by virtual screens, etc. +Comment[af]=Claude se Tab venster bestuurder. Dis TWM wat met virtuele skerms verbeter is +Comment[ar]=مدير نوافذ Claude's Tab، وهي TWM محسّن بشاشات وهمية، إلخ. +Comment[be]=Кіраўнік вокнаў з укладкамі ад Claude, TWM з падтрымкай віртуальных экранаў і інш. +Comment[bg]=Claude"s Tab Window Manager, TWM enhanced by virtual screens, etc. +Comment[bn]=ক্লড-এর ট্যাব উইণ্ডো ম্যানেজার +Comment[bs]=Claude's Tab Window Manager, TWM proširen virtuelnim ekranima itd. +Comment[ca]=Gestor de finestres amb pestanyes d'en Claude, millores TWM per a pantalles virtuals, etc. +Comment[csb]=Menedżer òknów Claude, TWM zbògacony ò wirtualné pùltë, ëtp. +Comment[cy]=Trefnydd Ffenestri Tab Claude, TWM wedi ei wella gan sgriniau rhith, ayyb. +Comment[da]=Claude's Tab vindueshåndtering, TWM udvidet med virtuelle skærme osv. +Comment[de]=Claudes Fenstermanager mit Karteikartenfenstern, eine verbesserte Fassung von TWM mit virtuellen Ansichten usw. +Comment[el]=Ο διαχειριστής παραθύρων Tab του Claude, ο TWM εμπλουτισμένος με εικονικές οθόνες, κτλ. +Comment[eo]=Taba Fenestroadministrilo de Claude, TWM, bonigita per virtualaj ekranoj ktp +Comment[es]=Claude's Tab Window Manager, TWM mejorado con pantallas virtuales, etc. +Comment[et]=Claude kaartidega aknahaldur, aluseks TWM, mida on täiendatud virtuaaltöölaudadega jne. +Comment[eu]=Claude's Tab leiho kudeatzailea, pantaila birtual eta abarrez hobetutako TWMa +Comment[fa]=مدیر پنجرۀ تب Claude، TWM، گسترشیافته توسط پردههای مجازی و غیره. +Comment[fi]=Clauden välilehtiikkunaohjelma. TWM, johon lisätty muun muassa virtuaalityöpöydät. +Comment[fr]=Claude's Tab Window Manager, TWM avec en plus les bureaux virtuels, etc. +Comment[fy]=Claude's Tab _indow Manager, TWM útbreide mei firtuele skermen etc. +Comment[gl]=Xestor de Fiestras de Claude, TWM mellorado con pantallas virtuais, etc. +Comment[he]=Claude's Tab Window Manager, TWM המשופרת על ידי מסכים וירטואליים וכו' +Comment[hi]=क्लाउडे का टैब युक्त विंडो प्रबंधक, टीडबल्यूएम को आभासी स्क्रीन इत्यादि से बेहतर बनाया गया. +Comment[hr]=Claudov Tab upravitelj prozora, TWM poboljšan virtualnim zaslonima, itd. +Comment[hu]=Claude lapozós ablakkezelője, lényegében a TWM, kiegészítve virtuális képernyőkkel, egyebekkel +Comment[is]=Tab gluggastjórinn eftir Claude sem hefur verið endurbættur með sýndarskjáum og fl. +Comment[it]=Window manager con linguette di Claude, TWM migliorato con schermi virtuali, ecc. +Comment[ja]=TWM に仮想デスクトップなどを強化した Claude のウィンドウマネージャ +Comment[ka]=Claude's Tab Window Manager - TWM-ის გაუმჯობესებული ვერსია +Comment[kk]=Claude's Tab Window Manager, TWM-ның жетілдірген нұсқасы. +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចជាផ្ទាំងរបស់ Claude, TWM ដែលធ្វើឲ្យប្រសើរដោយអេក្រង់និមិត្តជាដើម +Comment[lt]=Claude kortelių langų tvarkyklė, TWM praplėsta virtualių ekranų palaikymu ir t.t. +Comment[mk]=Claude's Tab Window Manager, TWM подобрен менаџер со виртуелни екрани итн. +Comment[mn]=Клаудиагийн ТАВ цонхны удирдагч, TWM виртуал дэлгэцээр өргөтгөгдсөн, гэх мэт. +Comment[mt]=Claude's Tab Window Manager, TWM flimkien ma' virtual screens, eċċ. +Comment[nb]=Claude's Tab Window Manager, TWM forbedret med virtuelle skrivebord,osv. +Comment[nds]=Claude's Tab Finsterpleger, TWM verbetert üm virtuelle Schriefdischen usw. +Comment[ne]=क्लाउडको ट्याब सञ्झ्याल प्रबन्धक, अवास्तविक पर्दाद्वारा बृद्धि गरिएको TWM, आदि +Comment[nl]=Claude's Tab Window Manager, TWM uitgebreid met virtuele schermen etc. +Comment[nn]=Claude's Tab Window Manager, TWM med virtuelle skrivebord og andre forbetringar +Comment[pa]=ਕਲਾਉਡੀ ਦਾ ਟੈਬ ਝਰੋਖਾ ਮੈਨੈਜਰ, TWM ਫਰਜ਼ੀ ਪਰਦਿਆਂ ਆਦਿ ਨਾਲ ਲੈੱਸ ਹੈ। +Comment[pl]=Menedżer okien Claude, TWM wzbogacony o wirtualne pulpity, itp. +Comment[pt]=O Tab Window Manager do Claude, um TWM melhorado com ecrãs virtuais, etc. +Comment[pt_BR]=O gerenciador de janelas em abas do Claude, o TWM melhorado pelas telas virtuais, etc. +Comment[ro]=Managerul de ferestre al lui Claude, o versiune îmbunătățită de TWM cu ecrane virtuale etc. +Comment[ru]=Claude's Tab Window Manager - улучшенная версия TWM +Comment[rw]=Mugenga Dirishya y'Agafishi ya Claude, TWM ivuguruwe na mugaragaza zitagaragara, n'ibindi +Comment[se]=Claudea Tab Window Manager, TWM mas lea virtuella čállinbeavddit ja eará buorideamit. +Comment[sk]=Claude's Tab Window Manager, TWM rozšírený o virtuálne plochy, atď. +Comment[sl]=Claude's Tab Window Manager, TWM izboljšan z navideznimi zasloni ipd. +Comment[sr]=„Claude's Tab Window Manager“, TWM побољшан виртуелним екранима и сл. +Comment[sr@Latn]=„Claude's Tab Window Manager“, TWM poboljšan virtuelnim ekranima i sl. +Comment[sv]=Claudes fönsterhanterare med flikar, TWM förbättrad med virtuella skärmar, etc. +Comment[ta]= க்ளூடின் தத்தல் சாளர மேளாளர், TWM ஆல் மேம்படுத்தப்பட்ட மெய்நிகர் திரைகள்.. +Comment[tg]=Claude's Tab Window Manager - Версияи навтарини TWM +Comment[th]=ตัวจัดการแท็บหน้าต่างของ Claude คือ TWM ที่เพิ่มความสามารถด้วยหน้าจอเสมือน และอื่นๆ +Comment[tr]=Claude's Sekme Pencere Yöneticisi, sanal ekranlar ile TWM genişletilmiş, vb. +Comment[tt]=Claude's Tab Window Manager, TWM'nıñ qulaylanğan töre +Comment[uk]=Claude's Tab Window Manager, TWM з підтримкою віртуальних екранів, тощо. +Comment[vi]=Trình quản lý cửa sổ kiểu Thẻ của Claude, TWM cải tiến với màn hình ảo v.v. +Comment[wa]=Li Manaedjeu di Purneas a Linwetes di Claude (Claude's Tab Window Manager). TWM permete des forveyowès waitroûles, evnd. +Comment[zh_CN]=Claude 的标签式窗口管理器,加强了虚拟屏幕等功能的 TWM。 +Comment[zh_TW]=Claude's Tab 視窗管理程式, 基於 TWM 並加強虛擬螢幕等功能 diff --git a/kdm/kfrontend/sessions/cwwm.desktop b/kdm/kfrontend/sessions/cwwm.desktop new file mode 100644 index 000000000..51b60abd3 --- /dev/null +++ b/kdm/kfrontend/sessions/cwwm.desktop @@ -0,0 +1,74 @@ +[Desktop Entry] +Type=XSession +Exec=cwwm +TryExec=cwwm +Name=CWWM +Name[eo]=CWFA +Name[hi]=सीडबल्यूडबल्यूएम +Name[te]=సి డబ్ల్యు డబ్ల్యు ఎం +Comment=The ChezWam Window Manager, a minimalist window manager based on EvilWM +Comment[af]=Die ChezWam venster bestuurder. Dis 'n minimalistiese venster bestuurder wat op EvilWM gebaseer is. +Comment[ar]=مدير نوافذ ChezWam، وهو مدير نوافذ مصغّر مبني على EvilWM +Comment[be]=Кіраўнік вокнаў ChezWam, мінімалістычны, заснаваны на EvilWM +Comment[bn]=ChezWam উইণ্ডো ম্যানেজার, EvilWM ভিত্তিক একটি উইণ্ডো ম্যানেজার +Comment[bs]=ChezWam Window Manager, minimalistički window manager baziran na EvilWM +Comment[ca]=El gestor de finestres ChezWam, un gestor de finestres minimalista basat en EvilWM +Comment[cs]=ChezWam, minimalistický správce oken založený na EvilWM +Comment[csb]=Menedżer òknów ChezWam, prosti menedżer òknów ùsôdzony na spòdlém EvilWM +Comment[cy]=Y Trefnydd Ffenestri ChezWam, trefnydd ffenestri lleiafol wedi'i seilio ar EvilWM +Comment[da]=ChezWam vindueshåndteringen, en minimalistisk vindueshåndtering baseret på EvilWM +Comment[de]=Deer ChezWam-Fenstermanager, eine minimalistische Lösung, die auf EvilWM basiert +Comment[el]=Ο ChezWam διαχειριστής παραθύρων, ένας μινιμαλιστικός διαχειριστής παραθύρων βασισμένος στον EvilWM +Comment[eo]=La ChezWam Fenestroadministrilo, devenigita de MalbonaFA +Comment[es]=El gestor de ventanas ChezWam, un gestor minimalista basado en EvilWM +Comment[et]=ChezWami aknahaldur on vähenõudlik aknahaldur, mille aluseks on EvilWM +Comment[eu]=ChezWarn leiho kudeatzailea, EvilWM-en oinarritutako leiho kudeatzaile minimalista +Comment[fa]=مدیر پنجره ChezWam، مدیر پنجرۀ کمینه بر اساس EvilWM +Comment[fi]=ChezWam, minimalistinen EvilWM:ään pohjautuva ikkunaohjelma. +Comment[fr]=Le ChezWam Window Manager, un gestionnaire de fenêtres minimaliste fondé sur EvilWM +Comment[fy]=The ChezWam Window Manager, in minimalistyske finstersmanager baseare op EvilWM +Comment[gl]=O Xestor de Fiestras ChezWarm, un xestor minimalista baseado en EvilWM +Comment[he]=The ChezWam Window Manager, מנהל חלונות מינימליסטי המבוסס על EvilWM +Comment[hi]=चेकवाम विंडो प्रबंधक, एक अल्पतम विंडो प्रबंधक EvilWM पर आधारित +Comment[hr]=ChezWam, minimalistički upravitelj prozora zasnovan na EvilWM +Comment[hu]=ChezWam ablakkezelő, egy nagyon egyszerű ablakkezelő az EvilWM alapján +Comment[is]=ChezWam gluggastjórinn er lágmarks gluggastjóri sem er byggður á EvilWM +Comment[it]=Il ChezWam Window Manager, un window manager minimalista basato su EvilWM +Comment[ja]=ChezWam ウィンドウマネージャ, EvilWM ベースの小さなウィンドウマネージャ +Comment[ka]=ChezWam Window Manager - მინიმალისტური ფანჯრის მენეჯერი EvilWM -ის ბაზაზე +Comment[kk]=ChezWam Window Manager, EvilWM-негіздеген шағын терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួច ChezWam ដែលជាកម្មវិធីគ្រប់គ្រងបង្អួចមិនសូវសម្បូរបែបហើយផ្អែកលើ EvilWM +Comment[lt]=ChezWam langų tvarkyklė, minimalistinė langų tvarkyklė, paremta EvilWM +Comment[mk]=ChezWam Window Manager, минималистички менаџер на прозорци базиран на EvilWM +Comment[mn]=ChezWam Цонхны удирдагч, EvilWM дээр суурилсан хамгийн жижиг цонхны удирдагч +Comment[mt]=ChezWam Window Manager - window manager minimu ibbażat fuq EvilWM +Comment[nb]=The ChezWam Window Manager, en minimalistisk vindusbehandler basert på EvilWM +Comment[nds]=De ChezWam Finsterpleger is minimalistisch un buut op EvilWM op +Comment[ne]=चेजवाम सञ्झ्याल प्रबन्धक, EvilWM आधारित मिनिमलिस्ट सञ्झ्याल प्रबन्धक +Comment[nl]=The ChezWam Window Manager, een minimalistische windowmanager gebaseerd op EvilWM +Comment[nn]=ChezWam Window Manager, ein minimalistisk vindaugssjef som byggjer på EvilWM +Comment[pa]=ChezWam ਝਰੋਖਾ ਮੈਨੇਜਰ, ਇੱਕ EvilWM ਤੇ ਆਧਾਰਿਤ ਹਲਕਾ ਝਰੋਖਾ ਮੈਨੇਜਰ ਹੈ +Comment[pl]=Menedżer okien ChezWam, prosty menedżer okien stworzony na podstawie EvilWM +Comment[pt]=O ChezWam Window Manager, um gestor de janelas minimalista baseado no EvilWM +Comment[pt_BR]=O gerenciador de janelas de ChezWam, um gerenciador de janelas minimalista baseado no EvilWM +Comment[ro]=Managerul de ferestre ChezWam, o versiune minimalistică bazată pe EvilWM +Comment[ru]=ChezWam Window Manager - минимальный оконный менеджер на основе EvilWM +Comment[rw]=Mugenga Dirishya ChezWam, mugenga dirishya igira-nto ishingiye kuri EvilWM +Comment[se]=ChezWam Window Manager, ein minimalisttalaš lásegieđahalli, ráhkaduvvon EvilWM vuođul +Comment[sk]=The ChezWam Window Manager, minimálny správca okien založený na EvilWM +Comment[sl]=ChezWam Window Manager, skromen okenski upravitelj na osnovi EvilWM +Comment[sr]=„ChezWam Window Manager“, минималистички менаџер прозора заснован на EvilWM-у +Comment[sr@Latn]=„ChezWam Window Manager“, minimalistički menadžer prozora zasnovan na EvilWM-u +Comment[sv]=Fönsterhanteraren Chezwam, en minimalistisk fönsterhanterare baserad på Ond WM +Comment[ta]=செஸ்வாம் சாளர மேளாளர்,EvilWM ஐ அடிப்படையிலான குறைந்தபட்ச சாளர மேளாளர் +Comment[tg]=ChezWam Window Manager - Мудири равзанаҳои хурд дар асоси EvilWM +Comment[th]=ตัวจัดการหน้าต่าง ChezWarm คือ ระบบจัดการ หน้าต่างขนาดเล็ก สร้างจากพื้นฐานของ EvilWM +Comment[tr]=ChezWam Pencere Yöneticisi +Comment[tt]=ChezWam Window Manager, EvilWM asılında ciñel täräzä-idäräçe +Comment[uk]=Мінімалістичний менеджер вікон ChezWam, заснований на EvilWM +Comment[uz]=EvilWM asosida yaratilgan juda oddiy oyna boshqaruvchi +Comment[uz@cyrillic]=EvilWM асосида яратилган жуда оддий ойна бошқарувчи +Comment[vi]=Trình quản lý cửa sổ ChezWam, một trình quản lý cửa sổ đơn giản dựa trên EvilWM +Comment[wa]=Li Manaedjeu di Purneas di ChezWam (ChezWam Window Manager). On manaedjeu di purneas tot simpe, båzé so EvilWM +Comment[zh_CN]=ChezWam 窗口管理器,基于 EvilWM 的最小化窗口管理器 +Comment[zh_TW]=The ChezWam 視窗管理程式,基於 EvilWM 的小型化視窗管理程式 diff --git a/kdm/kfrontend/sessions/enlightenment.desktop b/kdm/kfrontend/sessions/enlightenment.desktop new file mode 100644 index 000000000..66dcdd574 --- /dev/null +++ b/kdm/kfrontend/sessions/enlightenment.desktop @@ -0,0 +1,86 @@ +[Desktop Entry] +Type=XSession +Exec=enlightenment_start +TryExec=enlightenment +Name=Enlightenment +Name[bn]=এনলাইটেনমেন্ট +Name[cy]=Goleuni (Enlightenment) +Name[eo]=Lumaĵo +Name[fa]=روشنفکری +Name[hi]=एनलाइटनमेंट +Name[km]=ភ្លឺ +Name[mn]=Гэгээрэл +Name[ne]=बुद्धत्व +Name[pa]=ਗਿਆਨ +Name[rw]=Imurika +Name[ta]=விளக்கிக்கூறுதல் +Name[te]=జ్ఞానొదయం +Comment=An extremely themable very feature-rich window manager +Comment[af]='n Uitbreibare, tema geaktiveerde venster bestuurder +Comment[ar]=مدير نوافذ غني بالمزايا وقابل لاستخدام السّمات بشكل كبير. +Comment[be]=Абсалютна змяняльны, багаты на здольнасці кіраўнік вокнаў +Comment[bn]=প্রচুর বৈশিষ্ট্য সম্বলিত একটি উইণ্ডো ম্যানেজার, যা বহুভাবে নিজের পছন্দমত বদলে নেওয়া যায় +Comment[bs]=Izuzetno prilagodljiv window manager bogat opcijama +Comment[ca]=Un gestor de finestres extremadament temible molt ric en característiques +Comment[cs]=Na funkce bohatý správce oken s širokou škálou témat +Comment[csb]=Bògati w fùnkcëje menedżer òknów ò wiôldżich mòżnotach zjinaczi wëzdrzatkù +Comment[cy]=Trefnydd ffenestri sy'n llawn o nodweddion, efo llawer o themau. +Comment[da]=En ekstremt tema-fleksibel egenskabsrig vindueshåndtering +Comment[de]=Sehr design- und funktionsreicher Fenstermanager +Comment[el]=Ένας εξαιρετικά παραμετροποιήσιμος και πλούσιος σε χαρακτηριστικά διαχειριστής παραθύρων +Comment[eo]=Fenestroadministrilo kun tre multaj eblecoj +Comment[es]=Un gestor de ventanas lleno de características y extremadamente personalizable +Comment[et]=Äärmiselt paljude teemade ja väga laialdaste võimalustega aknahaldur +Comment[eu]=Gaitasun ugari dituen, eta zeharo pertsonalizagarria den leiho kudeatzailea +Comment[fa]=مدیر پنجره با ویژگی زیاد و شدیداً موضوعی +Comment[fi]=Erittäin muokattavissa oleva ominaisuusrikas ikkunaohjelma +Comment[fr]=Un gestionnaire de fenêtres avec beaucoup de thèmes et de fonctionnalités +Comment[fy]=In tige rike en ynstelbere finstersmanager +Comment[gl]=Un xestor de fiestras moi configurábel en apariencia e rico en características +Comment[he]=מנהל חלונות פונקציונלי המכיל אפשרויות רבות להגדרת ערכות נושא +Comment[hi]=एक विंडो प्रबंधक जो अत्यंत प्रसंग युक्त तथा विशेषता से भरपूर है +Comment[hr]=Izuzetno prilagodljiv upravitelj prozora, bogat mogućnostima +Comment[hu]=Egy nagyon sokoldalúan témázható, sok lehetőséget biztosító ablakkezelő +Comment[is]=Afskaplega öflugur gluggastjóri með góðum þemastuðningi +Comment[it]=Un window manager estremamente temabile con molte funzionalità +Comment[ja]=詳細な部分までもテーマ化が可能な非常に多機能のウィンドウマネージャ +Comment[ka]=მრავალფუნქციური ფანჯრის მენეჯერი თემების მხარდაჭერით +Comment[kk]=Қасиеттер жағынан бай, көп тақырыпты терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចមានលក្ខណៈពិសេសសម្បូរបែប ដែលអាចប្ដូររូបរាងបានតាមចិត្ត +Comment[ko]=다양한 곳을 꾸밀 수 있는 기능이 풍부한 창 관리자 +Comment[lt]=Daug temų ir savybių turinti langų tvarkyklė +Comment[lv]=Tēmām un iespējām bagāts logu menedžeris +Comment[mk]=Менаџер на прозорци богат со можности и екстремно подложен на стилизирање со теми +Comment[mn]=Маш ирээдүйтэй цонхны удирдагч +Comment[ms]=Pengurus tetingkap kaya ciri yang amat boleh tema +Comment[mt]=Window manager rikk u b'ħafna effetti viżwali. +Comment[nb]=En svært omfattende vindusbehandler som kan tilpasses med temaer +Comment[nds]=En Finsterpleger mit bannig vele Funkschonen un Mustern +Comment[ne]=अत्याधिक विषयवस्तु योग्य धेरै आकृति सञ्झ्याल प्रबन्धक +Comment[nl]=Een zeer rijke en configureerbare windowmanager +Comment[nn]=Ein svært omfattande vindaugssjef som kan tilpassast med tema +Comment[pa]=ਇੱਕ ਸਰੂਪ ਦੇ ਪੂਰੇ ਫੀਚਰਾਂ ਨਾਲ ਭਰਪੂਰ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Bogaty w funkcje menedżer okien o dużych możliwościach zmiany wyglądu +Comment[pt]=Um gestor de janelas muito rico em funcionalidades e extremamente personalizado na sua aparência +Comment[pt_BR]=Um gerenciador de janelas rico em recursos e extremamente configurável através de temas +Comment[ro]=Un manager de ferestre extrem de versatil și cu multe facilități +Comment[ru]=Многофункциональный, поддерживающий темы оконный менеджер +Comment[rw]=Mugenga Dirishya biranga-bikize cyane ngirwa-nsanganyamatsiko mu buryo burenze +Comment[se]=Lášegieđahalli mas leat erenoamáš ollu doaimmat ja maid sáhttá heivehit fáttáin +Comment[sk]=Správca okien s veľkým množstvom funkcií a extrémnou podporou tém +Comment[sl]=Okenski upravitelj, zelo prilagodljiv in poln možnosti +Comment[sr]=Изузетно прилагодљив менаџер прозора са пуно могућности +Comment[sr@Latn]=Izuzetno prilagodljiv menadžer prozora sa puno mogućnosti +Comment[sv]=En ytterst anpassningsbar mycket funktionsrik fönsterhanterare +Comment[ta]=நிறைய தன்மைகள் அடங்கிய சாளர மேளாளர் +Comment[tg]=Мудири равзанаҳо дорои мавзӯъҳои гуногун ва дигар бисёр функсияҳо +Comment[th]=ระบบจัดการหน้าต่างที่เต็มไปด้วยความสามารถ และใช้ชุดตกแต่งมากมาย +Comment[tr]=İleri derecede temalanabilir, özellik zengini bir masaüstü yöneticisi +Comment[tt]=Küpçaralı tışlanulı täräzä-idäräçe +Comment[uk]=Менеджер вікон з потужною підтримкою тем та різноманітних функцій +Comment[uz]=Mavzu va imkoniyatlarga juda boy oyna boshqaruvchi +Comment[uz@cyrillic]=Мавзу ва имкониятларга жуда бой ойна бошқарувчи +Comment[vi]=Một trình quản lý cửa sổ cực kỳ dễ thay đổi sắc thái với nhiều tích năng +Comment[wa]=On manaedjeu di purneas foirt bén po-z eployî des tinmes et foirt ritche en usteyes +Comment[zh_CN]=强主题化的多功能窗口管理器 +Comment[zh_TW]=一個功能相當豐富的視窗管理程式 diff --git a/kdm/kfrontend/sessions/evilwm.desktop b/kdm/kfrontend/sessions/evilwm.desktop new file mode 100644 index 000000000..3c8c4f657 --- /dev/null +++ b/kdm/kfrontend/sessions/evilwm.desktop @@ -0,0 +1,77 @@ +[Desktop Entry] +Type=XSession +Exec=evilwm +TryExec=evilwm +Name=EvilWM +Name[eo]=MalbonaFA +Name[hi]=एविलडबल्यूएम +Name[sv]=Ond WM +Name[te]=ఈవిల్ డబ్ల్యు ఎం +Comment=A minimalist window manager based on AEWM +Comment[af]='n Minimalistiese venster bestuurder wat op AEWM gebaseer is +Comment[ar]=مدير نوافذ مصغّر مبني على AEWM +Comment[be]=Кіраўнік вокнаў для мінімаліста, заснаваны на AEWM +Comment[bn]=AEWM ভিত্তিক একটি পরিমিত উইণ্ডো ম্যানেজার +Comment[bs]=Minimalistički window manager baziran na AEWM +Comment[ca]=Un gestor de finestres minimalista basat en AEWM +Comment[cs]=Minimalistický správce oken založený na AEWM +Comment[csb]=Prosti menedżer òknów ùsôdzony na spòdlém AEWM +Comment[cy]=Trefnydd ffenestri lleiafol wedi'i seilio ar AEM +Comment[da]=En minimalistisk vindueshåndtering baseret på AEWM +Comment[de]=Minimalistischer Fenstermanager, der auf AEWM basiert +Comment[el]=Ένας μινιμαλιστικός διαχειριστής παραθύρων βασισμένος στον AEWM +Comment[eo]=Minimumema fenestroadministrilo +Comment[es]=Un administrado de ventanas minimalista basado en AEWM +Comment[et]=Vähenõudlik aknahaldur, mille aluseks on AEWM +Comment[eu]=AEWMn oinarritutako leiho kudeatzaile minimalista +Comment[fa]=یک مدیر پنجرۀ کمینه براساس AEWM +Comment[fi]=Minimalistinen, AEWM:ään pohjautuva ikkunaohjelma +Comment[fr]=Un gestionnaire de fenêtres fondé sur AEWM +Comment[fy]=In minimalistyske finstersmanager basearre op AEWM +Comment[gl]=Un xestor de fiestras minimalista baseado en AEWM +Comment[he]=מנהל חלונות מינימליסטי המבוסס על AEWM +Comment[hi]= एक अल्पतम विंडो प्रबंधक एईडबल्यूएम पर आधारित +Comment[hr]=Minimalistički upravitelj prozora zasnovan na AEWM-u +Comment[hu]=Egy nagyon egyszerű ablakkezelő az AEWM alapján +Comment[is]=Lágmarks gluggastjóri sem er byggður á AEWM +Comment[it]=Un window manager minimalista basato su AEWM +Comment[ja]=AEWM ベースの小さなウィンドウマネージャ +Comment[ka]=მინიმალისტური ფანჯრის მენეჯერი AEWM-ის ბაზაზე +Comment[kk]=AEWM-негіздеген шағын терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចលក្ខណៈពិសេសតិច ដែលផ្អែកលើ AEWM +Comment[ko]=AEWM 기반 창 관리자 +Comment[lt]=Minimalistinė langų tvarkyklė, paremta AEWM +Comment[lv]=Minimālistisks logu menedžeris bāzēts uz AEWM +Comment[mk]=Минималистички менаџер на прозорци базиран на AEWM +Comment[mn]=AEWM дээр суурилсан цонхны удирдагч +Comment[mt]=Window manager minimu ibbażat fuq AEWM +Comment[nb]=En minimalistisk vindusbehandler basert på AEWM +Comment[nds]=En minimalistischen Finsterpleger, de op AEWM opbuut +Comment[ne]=AEWM आधारित मिनिमलिस्ट सञ्झ्याल प्रबन्धक +Comment[nl]=Een minimalistische windowmanager gebaseerd op AEWM +Comment[nn]=Ein minimalistisk vindaugssjef som byggjer på AEWM +Comment[pa]=AEWM ਤੇ ਆਧਾਰਿਤ ਨਿਊਨਤਮ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Prosty menedżer okien stworzony na podstawie AEWM +Comment[pt]=Um gestor de janelas minimalista baseado no AEWM +Comment[pt_BR]=Um gerenciador de janelas minimalista baseado no AEWM +Comment[ro]=Un manager de ferestre minimalistic bazat pe AEWM +Comment[ru]=Минимальный оконный менеджер, основанный на AEWM +Comment[rw]=Mugenga dirishya igira-nto ishingiye kuri AEWM +Comment[se]=Minimalisttalaš lásegieđahalli, ráhkaduvvon AEWM vuođul +Comment[sk]=Minimálny správca okien založený na AEWM +Comment[sl]=Minimalističen okenski upravitelj na osnovi AEWM +Comment[sr]=Минималистички менаџер прозора заснован на AEWM-у +Comment[sr@Latn]=Minimalistički menadžer prozora zasnovan na AEWM-u +Comment[sv]=Minimalistisk fönsterhanterare baserad på AEWM +Comment[ta]=AEWM அடிப்படையிலான குறைதபட்ச சாளர மேளாளர் +Comment[tg]=Мудири равзанаҳои хурд дар асоси AEWM +Comment[th]=ระบบจัดการหน้าต่างขนาดเล็ก สร้างจากพื้นฐานของ AEWM +Comment[tr]=AEWM tabanlı küçük bir pencere yöneticisi +Comment[tt]=AEWM asılında ciñel täräcä-idäräçe +Comment[uk]=Мінімалістичний менеджер вікон, заснований на AEWM +Comment[uz]=AEWM asosida yaratilgan juda oddiy oyna boshqaruvchi +Comment[uz@cyrillic]=AEWM асосида яратилган жуда оддий ойна бошқарувчи +Comment[vi]=Một trình quản lý cửa sổ đơn giản dựa trên AEWM +Comment[wa]=On manaedjeu di purneas minimå båzé so AEWM +Comment[zh_CN]=基于 AEWM 的最小化窗口管理器 +Comment[zh_TW]=一個基於 AEWM 的小型化視窗管理程式 diff --git a/kdm/kfrontend/sessions/fluxbox.desktop b/kdm/kfrontend/sessions/fluxbox.desktop new file mode 100644 index 000000000..df6a0813c --- /dev/null +++ b/kdm/kfrontend/sessions/fluxbox.desktop @@ -0,0 +1,81 @@ +[Desktop Entry] +Type=XSession +Exec=startfluxbox +TryExec=startfluxbox +Name=Fluxbox +Name[bn]=ফ্লাক্সবক্স +Name[cy]=Llif-flwch (Fluxbox) +Name[eo]=Fluujo +Name[hi]=फ्ल्क्स-बाक्स +Name[ne]=फ्लक्सबक्स +Name[pa]=ਫਕਸਬਕਸ +Name[rw]=AgasandukuUmujyoumwe +Name[ta]=ப்ளக்ஸ் பெட்டி +Name[te]=ఫ్లక్స్ బాక్స్ +Name[tg]=Қуттии Flux +Comment=A highly configurable and low resource window manager based on Blackbox +Comment[af]='n Baie konfigureerbare en lae hulpbron intensiewe venster bestuurder, wat op Blackbox gebaseer is. +Comment[ar]=مدير نوافذ قابل للإعداد بشكل كبير ويستخدم القليل من موارد الجهاز وهو مبني Blackbox +Comment[be]=Кіраўнік вокнаў, заснаваны на Blackbox, з вялікімі здольнасцямі змяняцца і з нізкай патрабавальнасцю да рэсурсаў +Comment[bn]=ব্ল্যাকবক্স ভিত্তিক একটি উইণ্ডো ম্যানেজার, যা নানাভাবে কনফিগার করা যায়, কিন্তু খুবই কম রিসোর্স ব্যবহার করে +Comment[bs]=Visoko prilagodljiv window manager sa malim zauzećem resursa baziran na Blackbox-u +Comment[ca]=Un gestor de finestres altament configurable i que necessita pocs recursos basat en Blackbox +Comment[cs]=Vysoce přizpůsobitelný a nízkoúrovňový správce oken založený na Blackboxu +Comment[csb]=Menedżer òknów òpiarti na Blackbox ò wiôldżich mòżnotach kònfigùracëji ë nisczich żądaniach +Comment[cy]=Trefnydd ffenestri hawdd ei ffurfweddu ac ysgafn ei ddefnydd adnoddau, wedi'i seilio ar Ddu-flwch (Blackbox) +Comment[da]=En meget indstillelig vindueshåndtering med lavt ressourceforbrug baseret på Blackbox +Comment[de]=Sehr flexibler, aber ressourcenschonender Fenstermanager, der auf Blackbox basiert +Comment[el]=Ένας ιδιαίτερα παραμετροποιήσιμος και με μικρή κατανάλωση πόρων διαχειριστής παραθύρων βασισμένος στον Blackbox +Comment[eo]=Fenestroadministrilo devenigita de Negrujo +Comment[es]=Un gestor de ventanas basado en Blackbox muy configurable y con poco consumo de recursos +Comment[et]=Väga hästi kohandatav ja vähenõudlik aknahaldur, mille aluseks on Blackbox +Comment[eu]=Blackbox-en oinarritutako leiho kudeatzaile zeharo konfiguragarria, eta errekurtso gutxi kontsumitzen dituena +Comment[fa]=یک مدیر پنجره با قابلیت پیکربندی بالا و منبع پایین براساس Blackbox +Comment[fi]=Blackboxiin pohjautuva erittäin muokattava ja vähäisen resurssitarpeen omaava ikkunaohjelma +Comment[fr]=Un gestionnaire de fenêtres très configurable et utilisant peu de ressources, fondé sur Blackbox +Comment[fy]=In tige ynstelbere lichtgewicht finstersmanager basearre op Blackbox +Comment[gl]=Un xestor de fiestras moi configurábel e lixeiro baseado en Blackbox +Comment[he]=מנהל חלונות בעל הגדרות רבות הצורך משאבים מעטים ומבוסס עלBlackbox +Comment[hi]=ब्लेक-बाक्स आधारित, अत्यंत कॉन्फ़िगरेबल तथा कम साधन चाहने वाला विंडो प्रबंधक +Comment[hr]=Visoko konfigurabilan upravitelj prozora, male potrošnje resursa, zasnovan na Blackbox-u +Comment[hu]=Egy Blackbox-alapú ablakkezelő, alacsony erőforrásigénnyel, sokféle beállítási lehetőséggel +Comment[is]=Öflugur gluggastjóri sem notar lítið af auðlindum vélarinnar sem er byggður á Blackbox +Comment[it]= Un window manager molto configurabile e che utilizza poche risorse basato su Blackbox +Comment[ja]=Blackbox ベースの高度な設定が可能でリソースにやさしいウィンドウマネージャ +Comment[ka]=კონფიგურირებადი ფანჯრის მენეჯერი Blackbox -ის ბაზაზე +Comment[kk]=Blackbox-негіздеген, баптаулары көп, үнемді терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចដែលអាចកំណត់រចនាសម្ព័ន្ធបានខ្ពស់ និងប្រើធនធានតិច ដែលផ្អែកលើ Blackbox +Comment[ko]=Blackbox 기반의 다양하게 설정 가능하고 자원을 적게 사용하는 창 관리자 +Comment[lt]=Daug konfigūravimo parinkčių turinti ir mažai resursų naudojanti langų tvarkyklė, paremta Blackbox +Comment[lv]=Plaši konfigurējams un resursus taupošs logu menedžeris bāzēts uz Blackbox +Comment[mk]=Високо конфигурабилен менаџер на прозорци со мали побарувања на ресурси базиран на Blackbox +Comment[mn]=Хар хайрцаг дээр суурилсан маш бага нөөц хэрэглэдэг тохируулах чадвар өндөр цонхны удирдагч +Comment[mt]=Window manager ħafif u konfigurabbli ibbażat fuq BlackBox +Comment[nb]=En lite ressurskrevende og svært fleksibel vindusbehandler basert på Blackbox +Comment[nds]=En Finsterpleger, de wenig Ressourcen bruukt, man bannig vele Instellen hett. Opbuut op Blackbox +Comment[ne]=कालो बाकसमा आधारित उच्च कन्फिगरयोग्य र न्यून संसाधन सञ्झ्याल प्रबन्धक +Comment[nl]=Een zeer configureerbare lichtgewicht windowmanager gebaseerd op Blackbox. +Comment[nn]=Ein lite ressurskrevjande og svært fleksibel vindaugssjef basert på Blackbox +Comment[pa]=ਇੱਕ ਬਿਲਕੁੱਲ ਹਲਕਾ ਤੇ ਜਿਆਦਾ ਸੋਧਯੋਗ ਫਾਇਲ਼ ਮੈਨੇਜਰ +Comment[pl]=Menedżer okien oparty na Blackbox o dużych możliwościach konfiguracji i niskich wymaganiach +Comment[pt]=Um gestor de janelas bastante configurável e que usa poucos recursos, baseado no Blackbox +Comment[pt_BR]=Um gerenciador de janelas altamente configurável e com baixo uso de recursos, baseado no Blackbox +Comment[ro]=Un manager de ferestre foarte configurabil și mic, bazat pe Blackbox +Comment[ru]=Настраиваемый оконный менеджер, основанный на Blackbox +Comment[rw]=Mugenga dirishya mbonezwa mu buryo buhanitse kandi bikorana bike ishingiye ku Gasandukumukara +Comment[se]=Lásegieđahalli vuođđoduvvon Blackbox:as, hui heivehahtti ja geavaha unnán resurssaid +Comment[sk]=Veľmi konfigurovateľný a nenáročný správca okien založený na Blackbox +Comment[sl]=Visoko nastavljiv in učinkovit okenski upravitelj na osnovi Blackboxa +Comment[sr]=Врло подесив и незахтеван менаџер прозора заснован на Blackbox-у +Comment[sr@Latn]=Vrlo podesiv i nezahtevan menadžer prozora zasnovan na Blackbox-u +Comment[sv]=Mycket anpassningsbar fönsterhanterare med litet resursbehov baserad på Blackbox +Comment[ta]=கறுப்புபெட்டி சார்ந்த அமைக்கக்கூடிய குறைந்த வள சாளர மேளாளர் +Comment[tg]=Мудири равзанаҳои танзимшаванда дар асоси Blackbox +Comment[th]=ระบบจัดการหน้าต่างที่สามารถปรับแต่งได้อย่างละ เอียด และใช้ทรัพยากรน้อย สร้างบนพื้นฐานของ Blackbox +Comment[tr]=Blackbox temelli, kolayca özelleştirilebilir düşük kaynaklı bir pencere yöneticisi +Comment[tt]=Blackbox asılında qorılğan, yaqşı caylana torğan täräcä-idäräçe +Comment[uk]=Дуже гнучкий та невибагливий для ресурсів менеджер вікон, заснований на Blackbox +Comment[vi]=Một trình quản lý cửa sổ rất dễ cấu hình và đòi hỏi ít tài nguyên dựa trên Blackbox +Comment[wa]=On ledjir et foirt apontiåve manaedjeu di purneas båzé so Blackbox +Comment[zh_CN]=基于 BlackBox 可深入配置且占用资源较少的窗口管理器 +Comment[zh_TW]=一個基於 Blackbox 且高可組態及低資源的視窗管理程式 diff --git a/kdm/kfrontend/sessions/flwm.desktop b/kdm/kfrontend/sessions/flwm.desktop new file mode 100644 index 000000000..d6dc24010 --- /dev/null +++ b/kdm/kfrontend/sessions/flwm.desktop @@ -0,0 +1,77 @@ +[Desktop Entry] +Type=XSession +Exec=flwm +TryExec=flwm +Name=FLWM +Name[eo]=RMFA +Name[hi]=एफएलडबल्यूएम +Name[te]=ఎఫ్ ఎల్ డబ్ల్యు ఎం +Name[th]=ตัวจัดการหน้าต่าง FLWM +Comment=The Fast Light Window Manager, based primarily on WM2 +Comment[af]=Die Vinnige, ligte venster bestuurder, wat op WM2 gebaseer is +Comment[ar]=مدير النوافذ السريع الخفيف، مبني بشكل أساسي على WM2 +Comment[be]=The Fast Light Window Manager, заснаваны на WM2 +Comment[bn]=ফাস্ট লাইট উইণ্ডো ম্যানেজার, WM2 ভিত্তি করে তৈরি +Comment[bs]=Fast Light Window Manager, baziran uglavnom na WM2 +Comment[ca]=El gestor de finestres Fast Light, primerament basat en WM2 +Comment[cs]=Fast Light Window Manager založený původně na WM2 +Comment[csb]=Fast Light Window Manager, menedżer òknów ùsôdzony przédno na WM2 +Comment[cy]=Y Trefnydd Ffenestri Chwim ac Ysgafn, wedi'i seilio ar WM2 am y rhan fwyaf +Comment[da]=Fast Light vindueshåndtering, baseret først og fremmest på WM2 +Comment[de]=Der Fast Light Window Manager, der vor allem auf WM2 basiert +Comment[el]=Ο Fast Light διαχειριστής παραθύρων, βασισμένος κυρίως στον WM2 +Comment[eo]=Rapida malpeza fenestroadministrilo +Comment[es]=El Fast Light Window Manager, basado principalmente en WM2 +Comment[et]=Kiire ja vähenõudlik aknahaldur, aluseks peamiselt WM2 +Comment[eu]=Batez ere WM2-en oinarritutako leiho kudeatzaile arin eta bizkorra +Comment[fa]=مدیر پنجرۀ سریع و سبک، در اصل بر اساس WM2 +Comment[fi]=Fast Light Window Manager. Pohjautuu suurimmaksi osin WM2:een +Comment[fr]=The Fast Light Window Manager, fondé au départ sur WM2 +Comment[fy]=De Fast Light Window Manager, primêr basearre op WM2 +Comment[gl]=O Fast Light Window Manager, un xestor de fiestras lixeiro baseado en WM2 +Comment[he]=The Fast Light Window Manager, מבוסס בעיקר על WM2 +Comment[hi]=मुख्यतः डबल्यूएम2 पर आधारित तेज और हल्का विंडो प्रबंधक +Comment[hr]=Brzi i lagani upravitelj prozora (FLWM), zasnovan na WM2 +Comment[hu]=Fast Light Window Manager ablakkezelő, elsősorban a WM2 alapján +Comment[is]=Léttur og hraðvirkur gluggastjóri sem er aðallega byggður á WM2 +Comment[it]=Il Fast Light Window Manager, basato principalmente su WM2 +Comment[ja]="高速で軽い"WM2 ベースのウィンドウマネージャ +Comment[ka]=სწრაფი და მსუბუქი ფანჯრების მენეჯერი WM2 -ის ბაზაზე +Comment[kk]=WM2-негіздеген жеңіл және жедел терезе менеджері +Comment[km]=Fast Light Window Manager ផ្អែកចម្បងលើ WM2 +Comment[ko]=WM2에 기반을 둔 빠르고 가벼운 창 관리자 +Comment[lt]=Greita ir lengva langų tvarkyklė, paremta daugiausiai WM2 +Comment[lv]=Fast Light logu menedžeris, bāzēts galvenokārt uz WM2 +Comment[mk]=Fast Light Window Manager, менаџер примарно базиран на WM2 +Comment[mn]=Ерөнхийдөө WM2 дээр суурилсан хурдан хөнгөн цонхны удирдагч +Comment[mt]=Fast Light Window Manager, ibbażat fuq WM2 +Comment[nb]=En rask, lett vindusbehandler (Fast Light Window Manager), mest basert på WM2 +Comment[nds]=De "Fast Light"-Finsterpleger, to'n gröttsten Deel opbuut op WM2 +Comment[ne]=साधारणतया WM2 मा आधारित छिटो हल्का सञ्झ्याल प्रबन्धक +Comment[nl]=De Fast Light Window Manager, primair gebaseerd op WM2. +Comment[nn]=Ein rask, lett vindaugssjef (Fast Light Window Manager), mest basert på WM2 +Comment[pa]=ਇੱਕ ਹਲਕਾ ਤੇਜ਼ ਫਾਇਲ਼ ਮੈਨੇਜਰ, ਜੋ ਕਿ WM2 ਤੇ ਆਧਾਰਿਤ ਹੈ +Comment[pl]=Fast Light Window Manager, menedżer okien stworzony głownie na podstawie WM2 +Comment[pt]=O Fast Light Window Manager, baseado em primeiro lugar no WM2 +Comment[pt_BR]=Acrônimo para Fast Light Window Manager (gerenciador rápido e leve), baseado primeiramente no WM2 +Comment[ro]=Fast Light Window Manager, bazat în principal pe WM2 +Comment[ru]=Быстрый и лёгкий оконный менеджер, основанный на WM2 +Comment[rw]=Mugenga Dirishya Yihuta Yoroshye, ishingiye mbere na mbere kuri WM2 +Comment[se]=Jođánis, geahpes lásegieđahalli, ráhkaduvvon WM2 vuođul +Comment[sk]=The Fast Light Window Manager založený na WM2 +Comment[sl]=Fast Light Window Manager, v glavnem na osnovi WM2 +Comment[sr]=Брзи лаки менаџер прозора (FLWM), заснован на WM2 +Comment[sr@Latn]=Brzi laki menadžer prozora (FLWM), zasnovan na WM2 +Comment[sv]=Fönsterhanteraren Fast Light Window Manager, i huvudsak baserad på WM2 +Comment[ta]=WM2 அடிப்படையிலான வேக ஒளி வேக சாளர மேளாளர் +Comment[tg]=мудири равзанаҳои тез ва осон дар асоси WM2 +Comment[th]=ระบบจัดการหน้าต่างที่เร็วและเบา สร้างบนพื้นฐานของ WM2 +Comment[tr]=Fast Light Pencere Yöneticisi +Comment[tt]=WM2 asılında qorılğan, citez ciñel täräcä-idäräçe +Comment[uk]=Швидкий легкий менеджер вікон, заснований здебільшого на WM2 +Comment[uz]=Asosan WM2 asosida yaratilgan tez va oddiy oyna boshqaruvchi +Comment[uz@cyrillic]=Асосан WM2 асосида яратилган тез ва оддий ойна бошқарувчи +Comment[vi]=Trình Quản lý Cửa sổ Nhanh và Nhẹ, dựa chủ yếu vào WM2 +Comment[wa]=Li Roed et Ledjir Manaedjeu di Purneas (ast Light Window Manager) båzé so WM2 +Comment[zh_CN]=又快又轻巧的窗口管理器,主要基于 WM2 +Comment[zh_TW]=一個主要基於 WM2 且快速及輕量化的視窗管理程式 diff --git a/kdm/kfrontend/sessions/fvwm.desktop b/kdm/kfrontend/sessions/fvwm.desktop new file mode 100644 index 000000000..20ae57d4c --- /dev/null +++ b/kdm/kfrontend/sessions/fvwm.desktop @@ -0,0 +1,71 @@ +[Desktop Entry] +Type=XSession +Exec=fvwm +TryExec=fvwm +Name=FVWM +Name[hi]=एफ़वीडबल्यूएम +Name[te]=ఎఫ్ వి డబ్ల్యు ఎం +Comment=A powerful ICCCM-compliant multiple virtual desktop window manager +Comment[af]='n Kragtige, ICCCM-aanpasbare veelvuldige virtuele werkskermvenster bestuurder. +Comment[ar]=مدير نوافذ قوي ومتوافق مع ICCCM ذي أسطح مكتب وهمية متعددة +Comment[be]=Магутны ICCCM-сумяшчальны кіраўнік вокнаў з падтрымкай віртуальных працоўных сталоў +Comment[bn]= একটি শক্তিশালী ICCCM-compliant উইণ্ডো ম্যানেজার, যাতে একাধিক ভার্চুয়াল ডেস্কটপ সম্ভব +Comment[bs]=Moćan ICCCM-sukladan window manager sa podrškom za više virtuelnih desktopa +Comment[ca]=Un poderós gestor de finestres per a múltiples escriptoris virtuals que compleix amb ICCCM +Comment[csb]=Stolemny menedżer òknów zgòdny z ICCCM òbsłëgòwujący wirtualné pùltë +Comment[cy]=Trefnydd ffenestri pwerus efo penbyrddau rhith lluosol, sy'n cydymffurfio â ICCCM +Comment[da]=En kraftig ICCCM-kompliant vindueshåndtering med flere virtuelle desktoppe +Comment[de]=Ein leistungsfähiger ICCCM-kompatibler Fenstermanager mit virtuellen Arbeitsflächen +Comment[el]=Ένας πολύ δυνατός, συμβατός με το ICCCM, διαχειριστής παραθύρων με πολλαπλές εικονικές επιφάνειες εργασίας +Comment[eo]=Fenestroadministrilo +Comment[es]=Un potente gestor de ventanas, compatible con ICCCM y que soporta varios escritorios virtuales +Comment[et]=Võimas ICCCM nõuetele vastav mitme virtuaalse töölauaga aknahaldur +Comment[eu]=ICCCM konpatiblea den, eta mahaigain birtual ugari dituen leiho kudeatzaile bortitza +Comment[fa]=یک مدیر پنجرۀ رومیزی مجازی چندگانه ICCCM-compliant نیرومند +Comment[fi]=Tehokas ICCCM-mukautuva virtuaalityöpöytiä tukeva ikkunaohjelma +Comment[fr]=Un gestionnaire de fenêtres puissant compatible ICCCM avec gestion de bureaux virtuels multiples +Comment[fy]=In krêftige ICCCM-compliant finstersmanager mei meardere buroblêden +Comment[gl]=Un xestor de fiestras potente acorde coa ICCCM con múltiplos escritórios virtuais +Comment[he]=מנהל חלונות עצמתי עם תאימות ל־ICCCM בעל שולחנות עבודה וירטואליים רבים +Comment[hi]=शक्तिशाली आईसीसीसीएम-कम्पलाएंट अनेक आभासी डेस्कटॉप विंडो प्रबंधक +Comment[hr]=Comment=Moćni, ICCCM kompatibilan, upravitelj prozora s više virtualnih radnih površina +Comment[hu]=Egy sokoldalú, ICCCM-kompatibilis ablakkezelő, virtuális munkaasztal-kezeléssel +Comment[is]=Öflugur ICCCM samhæfður gluggastjóri með sýndarskjáborðum +Comment[it]=Un window manager molto potente e ICCCM-compatibile che supporta i desktop virtuali +Comment[ja]=複数の仮想デスクトップをサポートした ICCCM 準拠のパワフルなウィンドウマネージャ +Comment[ka]=ძლიერი ICCCM-თავსებადი ვირტულური სამუშაო დაფების მხარდამჭერი ფანჯრის მენეჯერი +Comment[kk]=Қуатты ICCCM-үйлесімді, көп виртуалды үстел қолдайтын терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចផ្ទៃតុនិមិត្តច្រើនដ៏មានអានុភាព ដែលអនុវត្តតាម ICCCM +Comment[ko]=다중 가상 데스크톱을 사용하는 ICCCM 호환 창 관리자 +Comment[lt]=galinga, su ICCCM suderinama daugelio virtualių darbastalių langų tvarkyklė +Comment[lv]=Spēcīgs ICCCM-savietojams logu menedžeris ar vairāku darbvirsmu atbalstu +Comment[mk]=Моќен менаџер на прозорци со повеќе виртуелни површини во согласност со ICCCM +Comment[mt]=Window manager b'saħħtu, konformi ma' ICCCM, b'desktops virtwali. +Comment[nb]=En slagkraftig vindusbehandler med flere virtuelle skrivebord, som støtter ICCCM +Comment[nds]=En kraftvulle, ICCCM-kompatible Finsterpleger mit vele virtuelle Schriefdischen +Comment[ne]=शक्तिशाली ICCCM-मान्ने बहुविध अवास्तविक डेस्कटप सञ्झ्याल प्रबन्धक +Comment[nl]=Een krachtige ICCCM-compliant windowmanager met meerdere bureaubladen +Comment[nn]=Ein slagkraftig vindaugssjef med fleire virtuelle skrivebord, som støttar ICCCM +Comment[pa]=ਇੱਕ ਸ਼ਕਤੀਸ਼ਾਲੀ ICCCM-ਅਨੁਕੂਲ ਬਹੁ-ਫਰਜ਼ੀ ਵੇਹੜਿਆਂ ਵਾਲਾ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Potężny menedżer okien zgodny z ICCCM obsługujący wirtualne pulpity +Comment[pt]=Um gestor de janelas poderoso em conformidade com o ICCCM e que suporta vários ecrãs virtuais +Comment[pt_BR]=Um poderoso gerenciador de janelas compatível com o ICCM, com suporte a múltiplas áreas de trabalho virtuais +Comment[ro]=Un manager de ferestre puternic compliant ICCCM ce suportă ecrane virtuale +Comment[ru]=Мощный ICCCM-совместимый оконный менеджер, поддерживающий виртуальные рабочие столы +Comment[rw]=igikoranisha-ICCCM nyembaraga mugenga dirishya y'ibiro bitandukanye bitagaragara +Comment[se]=Fápmolaš ICCCM-heivvolaš lásegieđahalli mas lea virtuealla čállinbeavddit +Comment[sk]=Výkonný správca okien kompatibilný s ICCCM s podporou virtuálnych plôch +Comment[sl]=Močan okenski upravitelj z večimi navideznimi namizji in popolnoma v skladu z ICCCM +Comment[sr]=Моћни, ICCCM-сагласни, менаџер прозора са више виртуелних радних површина +Comment[sr@Latn]=Moćni, ICCCM-saglasni, menadžer prozora sa više virtuelnih radnih površina +Comment[sv]=Kraftfull fönsterhanterare med flera virtuella skrivbord som följer ICCCM +Comment[ta]=ICCCM-தரத்தில் பலதரப்பட்ட மெய்நிகர் மேல் மேசை சாளர மேளாளர் +Comment[tg]=Мудири равзанаҳои бузург дар асоси ICCCM дорои мудири равзанаҳо бо мизикориҳои виртуалӣ +Comment[th]=ระบบจัดการหน้าต่างที่มีหลายพื้นที่ทำงานเสมือน สอดรับกับ ICCCM ประสิทธิภาพสูง +Comment[tr]=Güçlü ICCCM-uyumlu çoklu sanal masaüstü yöneticisi +Comment[tt]=ICCCM-ütkän, küp öställär totqan köçle täräzä-idäräçe +Comment[uk]=Потужний, сумісний з ICCCM менеджер вікон, з підтримкою віртуальних стільниць +Comment[vi]=Trình quản lý cửa sổ tương thích với ICCCM nhiều chức năng, quản lý nhiều màn hình nền ảo +Comment[wa]=On manaedjeu di purneas avou multi-forveyowès sicribannes ki rote avou ICCM +Comment[zh_CN]=强大的多虚拟桌面窗口管理器,与 ICCCM 兼容 +Comment[zh_TW]=一個強大的 ICCCM 相容的多重虛擬桌面視窗管理程式 diff --git a/kdm/kfrontend/sessions/fvwm2.desktop b/kdm/kfrontend/sessions/fvwm2.desktop new file mode 100644 index 000000000..1a25a91e4 --- /dev/null +++ b/kdm/kfrontend/sessions/fvwm2.desktop @@ -0,0 +1,70 @@ +[Desktop Entry] +Type=XSession +Exec=fvwm2 +TryExec=fvwm2 +Name=FVWM2 +Name[te]=ఎఫ్ వి డబ్ల్యు ఎం 2 +Comment=A powerful ICCCM-compliant multiple virtual desktop window manager +Comment[af]='n Kragtige, ICCCM-aanpasbare veelvuldige virtuele werkskermvenster bestuurder. +Comment[ar]=مدير نوافذ قوي ومتوافق مع ICCCM ذي أسطح مكتب وهمية متعددة +Comment[be]=Магутны ICCCM-сумяшчальны кіраўнік вокнаў з падтрымкай віртуальных працоўных сталоў +Comment[bn]= একটি শক্তিশালী ICCCM-compliant উইণ্ডো ম্যানেজার, যাতে একাধিক ভার্চুয়াল ডেস্কটপ সম্ভব +Comment[bs]=Moćan ICCCM-sukladan window manager sa podrškom za više virtuelnih desktopa +Comment[ca]=Un poderós gestor de finestres per a múltiples escriptoris virtuals que compleix amb ICCCM +Comment[csb]=Stolemny menedżer òknów zgòdny z ICCCM òbsłëgòwujący wirtualné pùltë +Comment[cy]=Trefnydd ffenestri pwerus efo penbyrddau rhith lluosol, sy'n cydymffurfio â ICCCM +Comment[da]=En kraftig ICCCM-kompliant vindueshåndtering med flere virtuelle desktoppe +Comment[de]=Ein leistungsfähiger ICCCM-kompatibler Fenstermanager mit virtuellen Arbeitsflächen +Comment[el]=Ένας πολύ δυνατός, συμβατός με το ICCCM, διαχειριστής παραθύρων με πολλαπλές εικονικές επιφάνειες εργασίας +Comment[eo]=Fenestroadministrilo +Comment[es]=Un potente gestor de ventanas, compatible con ICCCM y que soporta varios escritorios virtuales +Comment[et]=Võimas ICCCM nõuetele vastav mitme virtuaalse töölauaga aknahaldur +Comment[eu]=ICCCM konpatiblea den, eta mahaigain birtual ugari dituen leiho kudeatzaile bortitza +Comment[fa]=یک مدیر پنجرۀ رومیزی مجازی چندگانه ICCCM-compliant نیرومند +Comment[fi]=Tehokas ICCCM-mukautuva virtuaalityöpöytiä tukeva ikkunaohjelma +Comment[fr]=Un gestionnaire de fenêtres puissant compatible ICCCM avec gestion de bureaux virtuels multiples +Comment[fy]=In krêftige ICCCM-compliant finstersmanager mei meardere buroblêden +Comment[gl]=Un xestor de fiestras potente acorde coa ICCCM con múltiplos escritórios virtuais +Comment[he]=מנהל חלונות עצמתי עם תאימות ל־ICCCM בעל שולחנות עבודה וירטואליים רבים +Comment[hi]=शक्तिशाली आईसीसीसीएम-कम्पलाएंट अनेक आभासी डेस्कटॉप विंडो प्रबंधक +Comment[hr]=Comment=Moćni, ICCCM kompatibilan, upravitelj prozora s više virtualnih radnih površina +Comment[hu]=Egy sokoldalú, ICCCM-kompatibilis ablakkezelő, virtuális munkaasztal-kezeléssel +Comment[is]=Öflugur ICCCM samhæfður gluggastjóri með sýndarskjáborðum +Comment[it]=Un window manager molto potente e ICCCM-compatibile che supporta i desktop virtuali +Comment[ja]=複数の仮想デスクトップをサポートした ICCCM 準拠のパワフルなウィンドウマネージャ +Comment[ka]=ძლიერი ICCCM-თავსებადი ვირტულური სამუშაო დაფების მხარდამჭერი ფანჯრის მენეჯერი +Comment[kk]=Қуатты ICCCM-үйлесімді, көп виртуалды үстел қолдайтын терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចផ្ទៃតុនិមិត្តច្រើនដ៏មានអានុភាព ដែលអនុវត្តតាម ICCCM +Comment[ko]=다중 가상 데스크톱을 사용하는 ICCCM 호환 창 관리자 +Comment[lt]=galinga, su ICCCM suderinama daugelio virtualių darbastalių langų tvarkyklė +Comment[lv]=Spēcīgs ICCCM-savietojams logu menedžeris ar vairāku darbvirsmu atbalstu +Comment[mk]=Моќен менаџер на прозорци со повеќе виртуелни површини во согласност со ICCCM +Comment[mt]=Window manager b'saħħtu, konformi ma' ICCCM, b'desktops virtwali. +Comment[nb]=En slagkraftig vindusbehandler med flere virtuelle skrivebord, som støtter ICCCM +Comment[nds]=En kraftvulle, ICCCM-kompatible Finsterpleger mit vele virtuelle Schriefdischen +Comment[ne]=शक्तिशाली ICCCM-मान्ने बहुविध अवास्तविक डेस्कटप सञ्झ्याल प्रबन्धक +Comment[nl]=Een krachtige ICCCM-compliant windowmanager met meerdere bureaubladen +Comment[nn]=Ein slagkraftig vindaugssjef med fleire virtuelle skrivebord, som støttar ICCCM +Comment[pa]=ਇੱਕ ਸ਼ਕਤੀਸ਼ਾਲੀ ICCCM-ਅਨੁਕੂਲ ਬਹੁ-ਫਰਜ਼ੀ ਵੇਹੜਿਆਂ ਵਾਲਾ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Potężny menedżer okien zgodny z ICCCM obsługujący wirtualne pulpity +Comment[pt]=Um gestor de janelas poderoso em conformidade com o ICCCM e que suporta vários ecrãs virtuais +Comment[pt_BR]=Um poderoso gerenciador de janelas compatível com o ICCM, com suporte a múltiplas áreas de trabalho virtuais +Comment[ro]=Un manager de ferestre puternic compliant ICCCM ce suportă ecrane virtuale +Comment[ru]=Мощный ICCCM-совместимый оконный менеджер, поддерживающий виртуальные рабочие столы +Comment[rw]=igikoranisha-ICCCM nyembaraga mugenga dirishya y'ibiro bitandukanye bitagaragara +Comment[se]=Fápmolaš ICCCM-heivvolaš lásegieđahalli mas lea virtuealla čállinbeavddit +Comment[sk]=Výkonný správca okien kompatibilný s ICCCM s podporou virtuálnych plôch +Comment[sl]=Močan okenski upravitelj z večimi navideznimi namizji in popolnoma v skladu z ICCCM +Comment[sr]=Моћни, ICCCM-сагласни, менаџер прозора са више виртуелних радних површина +Comment[sr@Latn]=Moćni, ICCCM-saglasni, menadžer prozora sa više virtuelnih radnih površina +Comment[sv]=Kraftfull fönsterhanterare med flera virtuella skrivbord som följer ICCCM +Comment[ta]=ICCCM-தரத்தில் பலதரப்பட்ட மெய்நிகர் மேல் மேசை சாளர மேளாளர் +Comment[tg]=Мудири равзанаҳои бузург дар асоси ICCCM дорои мудири равзанаҳо бо мизикориҳои виртуалӣ +Comment[th]=ระบบจัดการหน้าต่างที่มีหลายพื้นที่ทำงานเสมือน สอดรับกับ ICCCM ประสิทธิภาพสูง +Comment[tr]=Güçlü ICCCM-uyumlu çoklu sanal masaüstü yöneticisi +Comment[tt]=ICCCM-ütkän, küp öställär totqan köçle täräzä-idäräçe +Comment[uk]=Потужний, сумісний з ICCCM менеджер вікон, з підтримкою віртуальних стільниць +Comment[vi]=Trình quản lý cửa sổ tương thích với ICCCM nhiều chức năng, quản lý nhiều màn hình nền ảo +Comment[wa]=On manaedjeu di purneas avou multi-forveyowès sicribannes ki rote avou ICCM +Comment[zh_CN]=强大的多虚拟桌面窗口管理器,与 ICCCM 兼容 +Comment[zh_TW]=一個強大的 ICCCM 相容的多重虛擬桌面視窗管理程式 diff --git a/kdm/kfrontend/sessions/fvwm95.desktop b/kdm/kfrontend/sessions/fvwm95.desktop new file mode 100644 index 000000000..2bbd1c403 --- /dev/null +++ b/kdm/kfrontend/sessions/fvwm95.desktop @@ -0,0 +1,76 @@ +[Desktop Entry] +Type=XSession +Exec=fvwm95 +TryExec=fvwm95 +Name=FVWM95 +Name[hi]=एफ़वीडबल्यूएम95 +Name[te]=ఎఫ్ వి డబ్ల్యు ఎం 95 +Comment=A Windows 95 look-alike derivative of FVWM +Comment[af]='n FVWM venster bestuurder wat soos Windows 95 lyk +Comment[ar]=شبيه بـWin95 ومشتق من FVWM +Comment[be]=Вытворная ад FVWM, падобная на Windows 95 +Comment[bn]=FVWM-ভিত্তিক Windows 95-এর মত দেখতে একটি উইণ্ডো ম্যানেজার +Comment[bs]=Derivacija FVWM nalik na Windows 95 +Comment[ca]=Un semblant a Win95 derivat de FVWM +Comment[cs]=Správce oken se vzhledem Windows 95 odvozený od FVWM +Comment[csb]=Pòchòdzący z FVWM menedżer òknów ò wëzdrzatkù szlachùjącym za Windows 95 +Comment[cy]=Deilliant o FVWM sy'n edrych yn debyg i Windows95 +Comment[da]=En Windows 95-lignende vindueshåndtering afledt af FVWM +Comment[de]=Ein Abkömmling von FVWM im Stil von Windows 95 +Comment[el]=Ένας παρόμοιος με τα Windows 95 διαχειριστής παραθύρων προερχόμενος από τον FVWM +Comment[eo]=Fenestroadministrilo kiel tiu de Vindozo 95 +Comment[es]=Un derivado de FVWM de aspecto similar a Win95 +Comment[et]=FVWM derivaat, mis näeb välja nagu Windows 95 +Comment[eu]=Windows 95en itxura duen FVWMren ondorengo bat +Comment[fa]=ویندوز ۹۵ شبیه مشتق FVWM +Comment[fi]=Windows 95:lta näyttävä FVWM-johdannainen +Comment[fr]=Un gestionnaire de fenêtres ressemblant à Windows 95 dérivant de FVWM +Comment[fy]=In op Windows 95 lykjende fariant fan FVWM +Comment[gl]=Un xestor de fiestras coma o de Windows 95 derivado de FVWM +Comment[he]=נגזרת של FVWM הנראה כמו חלונות 95 +Comment[hi]=विंडोज़ 95 जैसा दिखने वाला एफ़वीडबल्यूएम का प्रतिरूप +Comment[hr]=Derivat FVWM-a nalik na Windows 95 +Comment[hu]=Win95-szerű FVWM-változat +Comment[is]=Útgáfa af FVWM sem líkist Windows 95 +Comment[it]=Una variante di FVWM che assomiglia a Windows 95 +Comment[ja]=Windows95 に似た外見の FVWM 派生ウィンドウマネージャ +Comment[ka]=FVWM კლონი Windows 95-ის სტილში +Comment[kk]=Windows 95 секілді FVWM-негіздеген терезе менеджері +Comment[km]=ស្រដៀង Windows 95 ដែលក្លាយមកពី FVWM +Comment[ko]=FVWM의 윈도 95를 닮은 변종 +Comment[lt]=Windows 95 išvaizdą primenanti FVWM atmaina +Comment[lv]=Windows 95 līdzīgs FVWM atvasinājums +Comment[mk]=Деривација на FVWM менаџерот со изглед на Windows 95 +Comment[mn]=FVWM -ээс уламжилсан Виндовс 95 шиг харагдалттай +Comment[ms]=Terbitan FVWM yang menyerupai Windows 95 +Comment[mt]=Derivattiv ta' FVWM jixbaħ lil Windows 95 +Comment[nb]=En variant av FVWM som ser ut som Windows 95 +Comment[nds]=Süht ut as A Windows 95 un is vun FVWM afleddt +Comment[ne]=विण्डोज ९५ FVWM को डेरिभेटिभ जस्तो छ +Comment[nl]=Een op Windows 95 lijkende variant van FVWM +Comment[nn]=Ein variant av FVWM som ser ut som Windows 95 +Comment[pa]=ਵਿੰਡੋ 95 ਵਰਗਾ FVWM ਦਾ ਇੱਕ ਰੂਪ +Comment[pl]=Wywodzący się z FVWM menedżer okien o wyglądzie podobnym do Windows 95 +Comment[pt]=Uma derivação do FVWM parecida com o Windows 95 +Comment[pt_BR]=Um derivado do FVWM com a aparência do Windows 95 +Comment[ro]=O versiune de FVWM cu aspect de Windows 95 +Comment[ru]=Клон FVWM в стиле Windows 95 +Comment[rw]=Windows 95 isa nk'ikomoka kuri FVWM +Comment[se]=FVWM-vuođđoduvvon lásegieđahalli mii lea Windows 95-lágan +Comment[sk]=Správca okien podobný Windows 95 založený na FVWM +Comment[sl]=Izpeljanka FVWM v izgledu Windows 95 +Comment[sr]=Дериват FVWM-а налик на Windows 95 +Comment[sr@Latn]=Derivat FVWM-a nalik na Windows 95 +Comment[sv]=Fönsterhanterare som liknar Windows 95 med ursprung i FVWM +Comment[ta]=FVWM லிருந்து தோன்றிய விண்டோஸ் 95 ஐப் போன்ற +Comment[tg]=Windows 95 look-монанди маҳсулии FVWM +Comment[th]=FVWM ที่ถูกทำให้ดูเหมือนวินโดวส์ 95 +Comment[tr]=Windows 95'e benzeyen bir pencere yönetimi +Comment[tt]=Windows 95 küreneşendä FVWM qabatlama +Comment[uk]=Похідний від FVWM з виглядом Windows 95 +Comment[uz]=Win95'ga oʻxshagan FVWM'ning turi +Comment[uz@cyrillic]=Win95'га ўхшаган FVWM'нинг тури +Comment[vi]=Trình quản lý cửa sổ giống Windows 95, hậu duệ của FVWM +Comment[wa]=On manaedjeu di purneas rishonnant a Windows 95 et k' est båzé so FVWM +Comment[zh_CN]=一个与 Windows 95 外观类似的 FVWM 变种 +Comment[zh_TW]=一個由 FVWM 演化出來且外觀像 Win95 的視窗管理程式 diff --git a/kdm/kfrontend/sessions/gnome.desktop b/kdm/kfrontend/sessions/gnome.desktop new file mode 100644 index 000000000..ed89391ec --- /dev/null +++ b/kdm/kfrontend/sessions/gnome.desktop @@ -0,0 +1,81 @@ +[Desktop Entry] +Type=XSession +Exec=gnome-session +TryExec=gnome-session +Name=GNOME +Name[bn]=গনোম +Name[eo]=Gnomikuo +Name[hi]=ग्नोम +Name[ko]=세임 그놈 +Name[mn]=ГНОМЕ +Name[ne]=जिनोम +Name[sv]=Gnome +Name[ta]=க்னோம் +Name[te]=గ్నొమ్ +Comment=The GNU Network Object Model Environment. A complete, free and easy-to-use desktop environment +Comment[af]=Die GNU 'Network Object Model Environment'. 'n Volledige, gratis en maklik om te gebruik werkskerm omgewing. +Comment[ar]=بيئة نموذج الكائن الشبكي من GNU، بيئة سطح مكتبي حرّة وسهلة الاستخدام +Comment[be]=The GNU Network Object Model Environment. Поўнае, свабоднае і простае ў выкарыстанні працоўнае асяроддзе +Comment[bn]=দি গনিউ নেটওয়ার্ক অবজেক্টমডেল এনভায়রনমেন্ট। একটি পূর্ণ, মুক্ত এবং সহজেই ব্যবহারযোগ্য ডেস্কটপ এনভায়রনমেন্ট +Comment[bs]=GNU Network Object Model Environment. Kompletna, slobodna i jednostavna za upotrebu desktop okolina +Comment[ca]=El GNU Network Object Model Environment. Un complet, lliure i fàcil d'usar entorn d'escriptori +Comment[cs]=GNOME, GNU Network Object Model Environment. Kompletní, svobodné a uživatelsky přívětivé garfické prostředí. +Comment[csb]=GNU Network Object Model Environment (GNOME). Fùlwôrtné, wòlné ë prosté w brëkòwaniô òkrãżé pùltu +Comment[cy]=Yr Amgylchedd Model Gwrthrych Rhwydwaith GNU (GNU Network Object Model Environment). Amgylchedd penbwrdd cyflawn, rhydd, a hawdd ei ddefnyddio. +Comment[da]=GNU Network Object Model Environment. Et fuldstænding, frit og nemt at bruge desktopmiljø +Comment[de]=Das GNU Network Object Model Environment. Eine vollständige, freie und leicht bedienbare Arbeitsumgebung +Comment[el]=Το GNU Network Object Model Environment. Ένα πλήρης, ελεύθερο και εύκολο στη χρήση περιβάλλον επιφάνειας εργασίας +Comment[eo]=Plena labortabla ĉirkaŭaĵo +Comment[es]=El GNU Network Object Model Environment, un entorno de escritorio completo, libre y fácil de usar +Comment[et]=GNU Network Object Model Environment on täielik, vaba ja väga hõlpsasti kasutatav töölaua keskkond +Comment[eu]=GNU Network Object Model Environment. mahaigain-ingurune oso, libre eta erabilterraza +Comment[fa]=محیط مدل شئ شبکه گنو. محیط رومیزی کامل، آزاد و آسان برای استفاده. +Comment[fi]=GNU Network Object Model Environment. Valmis, vapaa ja helppokäyttöinen työpöytäympäristö +Comment[fr]=The GNU Network Object Model Environment. Un environnement de bureau complet, gratuit et facile à utiliser +Comment[fy]=De GNU Network Object Model Environment, In komplete, frije en ienfâldige te brûken buroblêd omwrâld +Comment[gl]=O GNU Network Object Model Environment. Un entorno de escritório completo, libre e de uso doado +Comment[he]=The GNU Network Object Model Environment. סביבת עבודה מלאה, חופשית וקלה לשימוש +Comment[hi]=जीएनयू नेटवर्क ऑब्जेक्ट मॉडल एनवायरनमेंट. एक संपूर्ण, उपयोग में आसान डेस्कटॉप वातावरण +Comment[hr]=GNOME - GNU Network Object Model Environment - Cjelokupno, besplatno i jednostavno okruženje radne površine +Comment[hu]=GNU Network Object Model Environment (GNOME), egy teljes, ingyenes, könnyen kezelhető grafikus környezet +Comment[is]=GNU Network Object Model Environment er fullkomið og fjrálst skjáborðsumhverfi sem er auðvelt að nota +Comment[it]=Il GNU Network Object Model Environment. Un ambiente desktop completo, libero e facile da usare +Comment[ja]=GNU オブジェクトモデル環境, 完全にフリーで使いやすいデスクトップ環境 +Comment[ka]=GNU Network Object Model Environment - სრული, თავისუფალი და ადვილად გამოყენებადი სამუშაო გარემო +Comment[kk]=GNU Network Object Model Environment. Толық, еркін таратылатын, ыңғайлы графикалық орта +Comment[km]=GNU Network Object Model Environment ។ បរិស្ថានផ្ទៃតុពេញលេញ, ឥតគិតថ្លៃ និងងាយស្រួលប្រើ +Comment[ko]=GNU Network Object Model Environment(그놈). 완전한 자유 소프트웨어 데스크톱 환경입니다. +Comment[lt]=GNU tinklo objektų modeliavimo aplinka. Savarankiška, laisva ir lengvai naudojama darbastalio aplinka +Comment[lv]=GNU Network Object Model Environment. Pilnvērtīga bezmaksas viegli lietojama darba vide. +Comment[mk]=GNU Network Object Model Environment. Работна околина која е комплетна, слободна и едноставна за користење +Comment[mn]=GNU Network Object Model Environment. Бүрэн, үнэгүй хэрэглэхэд хялбар дэлгэцийн системийн орчин +Comment[mt]=GNU Network Object Model Environment. Ambjent grafiku komplet, ħieles u faċli tużah. +Comment[nb]=GNU Network Object Model Environment. Et skrivebordsmiljø som er komplett, fritt og lett å bruke. +Comment[nds]=De "GNU Network Object Model Environment". En komplette Schriefdisch-Ümgeven, ümsunst un eenfach to bruken +Comment[ne]=GNU सञ्जाल बस्तु नमूना वातावरण । पूर्ण, स्वतन्त्र र प्रयोग गर्न सजिलो डेस्कटप वातावरण +Comment[nl]=De GNU Network Object Model Environment, een complete, vrije en eenvoudig te gebruiken desktop environment. +Comment[nn]=GNU Network Object Model Environment. Eit skrivebordsmiljø som er komplett, fritt og lett å bruka. +Comment[pa]=GNU Network Object Model Environment, ਇੱਕ ਸੰਪੂਰਨ, ਮੁਫਤ ਅਤ ਵਰਤਣ ਵਿੱਚ ਅਤਿ ਆਸਾਨ ਵੇਹੜਾ ਵਾਤਾਵਰਣ +Comment[pl]=GNU Network Object Model Environment (GNOME). Pełne, wolne i łatwe w użyciu środowisko pulpitu +Comment[pt]=O GNU Network Object Model Environment. Um ambiente de trabalho completo, livre e fácil de usar +Comment[pt_BR]=Acrônimo para GNU Network Object Model Environment ou Ambiente de Modelo de Objetos de Rede GNU; um ambiente de trabalho completo, livre e fácil de usar +Comment[ro]=GNU Network Object Model Environment. Un mediu grafic complet, gratuit și ușor de utilizat +Comment[ru]=GNU Network Object Model Environment - полная, свободная и лёгкая в использовании графическая среда +Comment[rw]=Ibikikije Moderi Igikoresho Urusobemiyoboro GNU. Ibikikije by'ibiro byuzuye, by'ubuntu kandi byoroshye-gukoresha. +Comment[se]=GNU Netword Object Model Environment: Čállinbeavdebiras mii lea aibbas fridja ja álki geavahit. +Comment[sk]=The GNU Network Object Model Environment. Úplné, voľne šíriteľné a ľahko použiteľné pracovné prostredie +Comment[sl]=GNU Network Object Model Environment. Popolno, prosto in preposto namizno okolje +Comment[sr]=„GNU Network Object Model Environment“(Gnome, Гном). Потпуно, бесплатно и лако за коришћење радно окружење +Comment[sr@Latn]=„GNU Network Object Model Environment“(Gnome, Gnom). Potpuno, besplatno i lako za korišćenje radno okruženje +Comment[sv]=GNU Network Object Model Environment. En fullständig, fri och lättanvänd skrivbordsmiljö +Comment[ta]=GNU மாதிரி வலை பொருள் சூழல்.முழுமையான , இலவச சுலபமாக பயன்படுத்தக்கூடிய மேல்மேசை சூழல் +Comment[th]=GNU Network Object Model Environment สภาพแวดล้อมสำหรับเดสก์ทอปที่ครบถ้วน, ฟรี และใช้งานง่าย +Comment[tr]=GNU Network Object Model Environment (GNOME) +Comment[tt]=The GNU Network Object Model Environment. Qullanu öçen ciñel, buşlay, tulıçaralı östäl möxite +Comment[uk]=The GNU Network Object Model Environment. Повнофункціональне, вільне та зручне графічне середовище +Comment[uz]=GNOME (GNU Network Object Model Environment) - mukammal, erkin va foydalanish uchun juda qulay ish stoli muhiti +Comment[uz@cyrillic]=GNOME (GNU Network Object Model Environment) - мукаммал, эркин ва фойдаланиш учун жуда қулай иш столи муҳити +Comment[vi]=Môi trường Mạng Mô hình Đối tượng của GNU. Một môi trường màn hình nền đầy đủ, tự do và dễ sử dụng +Comment[wa]=L' evironmint di modele di cayets d' rantoele GNU (GNU Network Object Model Environment). On complet evironmint d' sicribanne, libe et åjhey a -z eployî. +Comment[zh_CN]=GNU 网络对象模型环境。完整、自由、易用的桌面环境 +Comment[zh_TW]=GNU 網路物件模型環境。一個完整,免費且容易使用的桌面環境 diff --git a/kdm/kfrontend/sessions/golem.desktop b/kdm/kfrontend/sessions/golem.desktop new file mode 100644 index 000000000..3ba7f2b41 --- /dev/null +++ b/kdm/kfrontend/sessions/golem.desktop @@ -0,0 +1,81 @@ +[Desktop Entry] +Type=XSession +Exec=golem +TryExec=golem +Name=Golem +Name[bn]=গোলেম +Name[eo]=Golemo +Name[hi]=गोलेम +Name[ne]=गोलेम +Name[pa]=ਗੋਲੀਮ +Name[te]=గొలెం +Comment=A lightweight window manager +Comment[af]='n Lig gewig venster bestuurder +Comment[ar]=مدير نوافر خفيف العبء +Comment[be]=Лёгкі кіраўнік вокнаў +Comment[bn]=একটি হাল্কা উইণ্ডো ম্যানেজার +Comment[bs]=Lagani window manager +Comment[ca]=Un lleuger gestor de finestres KDE +Comment[cs]=Malý správce oken +Comment[csb]=Menedżer òknół ò môłëch żądaniach +Comment[cy]=Trefnydd ffenestri ysgafn +Comment[da]=En letvægtsvindueshåndtering +Comment[de]=Eine schlanker Fenstermanager +Comment[el]=Ένας ελαφρύς διαχειριστής παραθύρων +Comment[eo]=Malpeza fenestroadministrilo +Comment[es]=Un gestor de ventanas ligero +Comment[et]=Imeväike aknahaldur +Comment[eu]=Leiho kudeatzaile arina +Comment[fa]=یک مدیر پنجرۀ سبک +Comment[fi]=Kevyt ikkunaohjelma +Comment[fr]=Un gestionnaire de fenêtres très léger +Comment[fy]=In lichtgewicht finstersmanager +Comment[gl]=Un xestor de fiestras lixeiro +Comment[he]=מנהל החלונות קל משקל +Comment[hi]=एक हल्का विंडो प्रबंधक +Comment[hr]=Lagani upravitelj prozora +Comment[hu]=Egy nagyon egyszerű ablakkezelő +Comment[is]=Léttur gluggastjóri +Comment[it]=Un window manager leggero +Comment[ja]=軽量なウィンドウマネージャ +Comment[ka]=მსუბუქი ფანჯრი მენეჯერი +Comment[kk]=Жеңіл терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចសមត្ថភាពខ្សោយ +Comment[ko]=가벼운 창 관리자 +Comment[lt]=Nedaug resursų eikvojanti langų tvarkyklė +Comment[lv]=Viegls logu menedžeris +Comment[mk]=Лесен менаџер на прозорци +Comment[mn]=Хөнгөн цонхны удирдагч +Comment[ms]=Pengurus tetingkap ringan +Comment[mt]=Window manager ħafif +Comment[nb]=En lettvekts vindusbehandler +Comment[nds]=En ranke Finsterpleger +Comment[ne]=हल्का वजन सञ्झ्याल प्रबन्धक +Comment[nl]=Een lichtgewicht windowmanager +Comment[nn]=Ein lett vindaugssjef +Comment[pa]=ਇੱਕ ਹਲਕਾ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Menedżer okien o małych wymaganiach +Comment[pt]=Um gestor de janelas leve +Comment[pt_BR]=Um gerenciador de janelas leve +Comment[ro]=Un manager de ferestre foarte mic +Comment[ru]=Лёгкий оконный менеджер +Comment[rw]=Mugenga dirishya yoroshye +Comment[se]=Geahpes lásegieđahalli +Comment[sk]=Nenáročný správca okien +Comment[sl]=Lahek okenski upravitelj +Comment[sr]=Лагани менаџер прозора +Comment[sr@Latn]=Lagani menadžer prozora +Comment[sv]=Lättviktig fönsterhanterare +Comment[ta]=குறைவான எடை உடைய சாளர மேலாளர் +Comment[te]=తెలికైన విండొ అభికర్త +Comment[tg]=Мудири тирезаи сабук +Comment[th]=ระบบจัดการหน้าต่างขนาดเบา +Comment[tr]=Hızlı çalışan bir pencere yöneticisi +Comment[tt]=Ciñel täräzä-idäräçe +Comment[uk]=Швидкий менеджер вікон +Comment[uz]=Oddiy oyna boshqaruvchi +Comment[uz@cyrillic]=Оддий ойна бошқарувчи +Comment[vi]=Trình quản lý cửa sổ nhẹ ký +Comment[wa]=On ledjir manaedjeu di purneas +Comment[zh_CN]=轻量级窗口管理器 +Comment[zh_TW]=一個輕量化的視窗管理程式 diff --git a/kdm/kfrontend/sessions/icewm.desktop b/kdm/kfrontend/sessions/icewm.desktop new file mode 100644 index 000000000..8b70361fa --- /dev/null +++ b/kdm/kfrontend/sessions/icewm.desktop @@ -0,0 +1,81 @@ +[Desktop Entry] +Type=XSession +Exec=icewm-session +TryExec=icewm-session +Name=IceWM +Name[eo]=GlaciFA +Name[hi]=आइस-डबल्यूएम +Name[lo]=ຕົວຈັດການຫນ້າຕ່າງ IceWM +Name[sv]=Ice WM +Name[te]=ఐస్ డబ్ల్యు ఎం +Name[th]=ตัวจัดการหน้าต่าง IceWM +Comment=A Windows 95-OS/2-Motif-like window manager +Comment[af]='n Windows 95-OS/2-Motif tipe venster bestuurder +Comment[ar]=مدير نوافذ مشابه لـ Win95-OS/2-Motif +Comment[be]=Кіраўнік вокнаў, падобны на Windows 95-OS/2-Motif +Comment[bn]=Windows 95-OS/2-Motif-এর অনুরূপ একটি উইণ্ডো ম্যানেজার +Comment[bs]=Window manager nalik na Windows 95-OS/2-Motif +Comment[ca]=Un gestor de finestres com els de Windows 95-OS/2-Motif +Comment[cs]=Správce oken podobný Windows 95-OS/2-Motif +Comment[csb]=Menedżer òknół szlachùjący za Windows 95-OS/2-Motif +Comment[cy]=Trefnydd ffenestri sy'n debyg i Windows95-OS/2-Motif +Comment[da]=En Windows 95-OS/2-Motif-lignende vindueshåndtering +Comment[de]=Fenstermanager im Stil von Windows 95, OS/2 und Motif +Comment[el]=Ένας διαχειριστής παραθύρων παρόμοιος με τα Windows 95-OS/2-Motif +Comment[eo]=Fenestroadministrilo kiel Vindozo 95, OS/2 kaj Motifo +Comment[es]=Un gestor de ventanas similar a Win95-OS/2-Motif +Comment[et]=Aknahaldur, mis näeb välja nagu Windows 95-OS/2-Motif +Comment[eu]=Windows 95 OS/2 Motif-en itxura duen leiho kudeatzailea +Comment[fa]=یک مدیر پنجره شبیه Windows 95-OS/2-Motif +Comment[fi]=Windows 95:n ja OS/2-Motifin tyylinen ikkunaohjelma +Comment[fr]=Un gestionnaire de fenêtres ressemblant à Windows 95-OS/2-Motif +Comment[fy]=In Win95-OS/2-Motif-likens finstersmanager +Comment[gl]=Un xestor de fiestras coma o de Windows 95-OS/2-Motif +Comment[he]=מנהל חלונות מבוסס Motif הדומה במראהו לחלונות 95/OS-2 +Comment[hi]=विंडोज़ 95-ओएस/2-मोटिफ जैसा विंडो प्रबंधक +Comment[hr]=Upravitelj prozora nalik na Windows 95/OS/2/Motif +Comment[hu]=Win95-OS/2-Motif-szerű ablakkezelő +Comment[is]=Gluggastjóri sem líkist 95-OS/2-Motif +Comment[it]=Un window manager in stile Windows 95-OS/2-Motif +Comment[ja]=Windows95, OS/2, Motif に似たウィンドウマネージャ +Comment[ka]=ფანჯრების მენეჯერი Windows95-OS/2-Motif სტილში +Comment[kk]=Windows 95-OS/2-Motif-секілді терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចដែលដូច Windows 95-OS/2-Motif +Comment[ko]=윈도 95, OS/2, Motif를 닮은 창 관리자 +Comment[lt]=A Windows 95-OS/2-Motif-primenanti langų tvarkyklė +Comment[lv]=Windows 95 - OS/2 - Motif līdzīgs logu menedžeris +Comment[mk]=Менаџер на прозорци со изглед на Windows 95, OS/2 и Motif +Comment[mn]=Виндовс 95-OS/2-Motif-шиг цонхны удирдагч +Comment[ms]=Pengurus tetingkap seperti Motif Windows 95-OS/2 +Comment[mt]=Window manager jixbaħ lill-Windows 95-OS/2-Motif +Comment[nb]=En vindusbehandler som likner Windows 95-OS/2-Motif +Comment[nds]=Finsterpleger, de utsüht as Windows 95-OS/2-Motif +Comment[ne]=विण्डोज ९५-OS/2-मोटिफ जस्तो सञ्झ्याल प्रबन्धक +Comment[nl]=Een Win95-OS/2-Motif-achtige windowmanager +Comment[nn]=Ein vindaugssjef som liknar Windows 95-OS/2-Motif +Comment[pa]=ਇੱਕ ਵਿੰਡੋ 95-OS/2-Motif-ਵਰਗਾ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Menedżer okien podobny do Windows 95-OS/2-Motif +Comment[pt]=Um gestor de janelas parecido com o Windows 95, o OS/2 e o Motif +Comment[pt_BR]=Um gerenciador de janelas parecido com Windows 95-OS/2-Motif +Comment[ro]=Un manager de ferestre cu aspect de Windows 95, OS/2 sau Motif +Comment[ru]=Оконный менеджер в стиле Windows95-OS/2-Motif +Comment[rw]=Windows 95-OS/2-umutako-nka mugenga dirishya +Comment[se]=Windows 95-OS/2-Motif-lágan lásegieđahalli +Comment[sk]=Správca okien podobný Windows 95-OS/2-Motif +Comment[sl]=Okenski upravitelj, podoben Windows 95, OS/2 in Motifu +Comment[sr]=Менаџер прозора налик на Windows 95/OS/2/Motif +Comment[sr@Latn]=Menadžer prozora nalik na Windows 95/OS/2/Motif +Comment[sv]=Fönsterhanterare som liknar Windows 95-OS/2-Motif +Comment[ta]=சாளரங்கள் 95-OS/2-மாடிஃப்-லைக் சாளர மேலாளர் +Comment[te]=విండొస్ 95-ఒఎస్/2 -మొటిఫ్ లాంటి విండొ అభికర్త +Comment[tg]=Windows 95-OS/2-Motif-монанди мудири тиреза +Comment[th]=ระบบจัดการหน้าต่างของที่ดูคล้าย วินโดวส์ 95-โอเอสทู-โมทิฟ +Comment[tr]=Windows 95-OS/2-Motif benzeri bir pencere yöneticisi +Comment[tt]=Windows 95-OS/2-Motif küreneşendä täräzä-idäräçe +Comment[uk]=Менеджер вікон на кшталт Windows 95-OS/2-Motif +Comment[uz]=Win95-OS/2-Motif'ga oʻxshash oyna boshqaruvchi +Comment[uz@cyrillic]=Win95-OS/2-Motif'га ўхшаш ойна бошқарувчи +Comment[vi]=Trình quản lý cửa sổ với mô típ kiểu Windows 95 +Comment[wa]=On manaedjeu di purneas rishonnant Windows95-OS/2-Motif +Comment[zh_CN]=类似 Windows-OS/2-Motif 的窗口管理器 +Comment[zh_TW]=一個像 Win95-OS/2-Motif 的視窗管理程式 diff --git a/kdm/kfrontend/sessions/ion.desktop b/kdm/kfrontend/sessions/ion.desktop new file mode 100644 index 000000000..c4ae9ca04 --- /dev/null +++ b/kdm/kfrontend/sessions/ion.desktop @@ -0,0 +1,77 @@ +[Desktop Entry] +Type=XSession +Exec=ion +TryExec=ion +Name=Ion +Name[bn]=আয়ন (Ion) +Name[eo]=Iono +Name[hi]=आयन +Name[ja]=ION +Name[ko]=카메룬 +Name[pa]=ਲੋਨ +Name[te]=ఐయాన్ +Name[th]=ไอออน +Comment=A keyboard-friendly window manager with tiled windows, based on PWM +Comment[af]='n Sleutelbord vriendelike venster bestuurder, met geteëlde vensters. Dis op PWM gebaseer. +Comment[ar]=مدير نوافذ سهل استخدام لوحة المفاتيح ذي نوافذ معنونة، مبني على PWM +Comment[be]=Кіраўнік вокнаў для працы з клавіятурай, заснаваны на PWM +Comment[bn]=PWM ভিত্তিক উইণ্ডো ম্যানেজার, যা কীবোর্ড দিয়ে নিয়ন্ত্রণ করা সহজ +Comment[bs]=Window manager za tastaturu sa popločanim prozorima, baziran na PWM +Comment[ca]=Un gestor de finestres amigable amb el teclat i amb finestres alicatades, basat en PWM +Comment[csb]=Menedżer òknów z dobrą òbsłëgą klawiaturë ë kachelkòwaniém òknów, ùsôdzony na spòdlém PWM +Comment[cy]=Trefnydd ffenestri sy'n hawdd ei ddefnyddio o'r alweddell, efo ffenestri wedi'u teilio, wedi'i seilio ar PVM +Comment[da]=En tastaturvenlig vindueshåndtering med fliselagte vinduer, baseret på PWM +Comment[de]=Tastaturfreundlicher Fenstermanager mit gekachelten Fenstern, basiert auf PWM +Comment[el]=Ένας φιλικός με το πληκτρολόγιο διαχειριστής παραθύρων με υποστήριξη παραθύρων σε παράθεση, βασισμένος στον PWM +Comment[eo]=Fenestroadministrilo por la uzo klaviara +Comment[es]=Un gestor de ventanas utilizable desde el teclado con mosaico de ventanas, basado en PWM +Comment[et]=Klaviatuurisõprade aknahaldur paanitud akendega, aluseks PWM +Comment[eu]=PWMn oinarritutako leiho-mosaikoa duen leiho kudeatzailea, teklatutik erabil daitekeena +Comment[fa]=یک مدیر پنجرۀ صفحه کلید پسند با پنجرههای کاشیشده، براساس PWM +Comment[fi]=Näppäimistöystävällinen ikkunaohjelma +Comment[fr]=Un gestionnaire de fenêtres utilisable au clavier avec des fenêtres en mosaïque, fondé sur PWM +Comment[fy]=In toetseboerdfreonlike finstersmanager mei tegele finsters. basearre op PWM +Comment[gl]=Un xestor de fiestras de manexo co teclado e fiestras en mosaico baseado en PWM +Comment[he]=מנהל חלונות ידידותי למקלדת עם חלונות פרושים המבוסס על PWM +Comment[hi]=पीडबल्यूएम आधारित चटाई विंडो युक्त विंडो प्रबंधक जो कुंजीपट फ्रेडली है +Comment[hr]=Comment=Upravitelj prozora s popločenim prozorima, namijenjen tipkovnici i zasnovan na PWM-u +Comment[hu]=Egy billentyűzetről könnyen kezelhető ablakkezelő, mozaikszerű ablakelrendezéssel, a PWM alapján +Comment[is]=Gluggastjóri sem er gott að vinna í með lyklaborðinu einu, byggður á PWM +Comment[it]=Un window manager "amico della tastiera" con finestre affiancate, basato su PWM +Comment[ja]=PWM ベースのキーボード操作に適したタイル表示式のウィンドウマネージャ +Comment[ka]=კლავიატურით მართვადი ფანჯრების მენეჯერი, PWM-ის ბაზაზე +Comment[kk]=PWM-негіздеген, пернетақтадан басқарылатын, терезелері кезектескен, терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចដែលងាយប្រើជាមួយក្ដារចុច ហើយអាចរៀបបង្អួចជាក្បឿង និងផ្អែកលើ PWM +Comment[lt]=Patogi dirbti su klaviatūra langų tvarkyklė su išklotais langais, paremta PWM +Comment[lv]=Tastatūrai draudzīgs logu menedžeris ar tiled logiem, bāzēts uz PWM +Comment[mk]=Менаџер на прозорци со поплочени прозорци базиран на PWM погоден за работа со тастатура +Comment[mn]=PWM дээр суурилсан гараар ажиллахад тохиромжтой цонх удирдагч +Comment[ms]=Pengurus tetingkap mesra papan kekunci dengan tetingkap berjubin, berdasarkan PWM +Comment[mt]=Window manager ibbażat fuq PWM, b'sapport għal tastieri u windows imqassma fuiq madum +Comment[nb]=En tastaturvennlig vindusbehandler med flislagte vinduer, basert på PWM +Comment[nds]=En tastatuurfründliche Finsterpleger mit kachelte Finstern, opbuut op PWM +Comment[ne]=शीर्षक सञ्झ्यालसँग सोझो सञ्झ्याल प्रबन्धक कुञ्जीपाटी +Comment[nl]=Een toetsenbordvriendelijke windowmanager met getegelde vensters. Gebaseerd op PWM +Comment[nn]=Ein tastaturvennleg vindaugssjef med flislagde vindauge, basert på PWM +Comment[pa]=ਇੱਕ ਕੀ-ਬੋਰਡ ਸਹਾਇਕ ਝਰੋਖਾ ਮੈਨੇਜਰ, ਜੋ ਕਿ PWM ਤੇ ਆਧਾਰਿਤ ਹੈ +Comment[pl]=Menedżer okien z dobrą obsługą klawiatury i kafelkowaniem okiem, stworzony na podstawie PWM +Comment[pt]=Um gestor de janelas amigável para o teclado, com janelas lado-a-lado, baseado no PWM +Comment[pt_BR]=Um gerenciador de janelas amigável com o teclado, com janelas ladrilhadas, baseado no PWM +Comment[ro]=Un manager de ferestre ușor de utilizat din tastatură, cu ferestre ce pot fi așezate în mozaic, bazat pe PWM +Comment[ru]=Управляемый с клавиатуры оконный менежер, основанный на PWM +Comment[rw]=mugenga dirishya ya mwandikisho-yoroshyeikoresha ifite amadirishya agerekeranye, ishingiye kuri PWM +Comment[se]=Boallobeavdeustitlaš lašegieđahalli, mas leat bálddalas láset, ráhkaduvvon PWM vuođul +Comment[sk]=Správca okien s podporou kláves, usporiadania okien do dlaždíc, založený na PWM +Comment[sl]=Tipkovnici prijazen okenski upravitelj z razdeljenimi okni, na osnovi PWM +Comment[sr]=Пријатељски према тастатури менаџер прозора са наслаганим прозорима, заснован на PWM-у +Comment[sr@Latn]=Prijateljski prema tastaturi menadžer prozora sa naslaganim prozorima, zasnovan na PWM-u +Comment[sv]=Tangentbordsvänlig fönsterhanterare med fönster sida vid sida, baserad på PWM +Comment[ta]=PWM அடிப்படையிலான விசைப்பலகையால் கையாளமுடிந்த சாளர மேளாளர் +Comment[th]=ระบบจัดการหน้าต่างที่เป็นมิตรกับการใช้แป้นพิมพ์ พร้อมกับหน้าต่างที่ถูกปูเรียง สร้างจาก PWM +Comment[tr]=PWM tabanlı, klavye dostu uzatılmış pencereleriyle bir pencere yönetici +Comment[tt]=Täräzä bülü belän töylek-söygän täräzä-idäräçe, PWM asılında +Comment[uk]=Менеджер вікон налаштований для легкого використання з клавіатурою, засновано на PWM +Comment[vi]=Trình quản lý cửa sổ thiết kết thân thiện với việc dùng bàn phím, có các cửa sổ xếp ngói, dựa trên PWM +Comment[wa]=On manaedjeu di purneas k' inme li taprece avou des purneas å pus grand, båzé so PWM +Comment[zh_CN]=一个基于 PWM 的窗口管理器,适合键盘操作,可平铺窗口 +Comment[zh_TW]=一個基於 PWM 且方便鍵盤使用和支援棋盤化視窗的視窗管理程式 diff --git a/kdm/kfrontend/sessions/kde.desktop.in b/kdm/kfrontend/sessions/kde.desktop.in new file mode 100644 index 000000000..b032d395a --- /dev/null +++ b/kdm/kfrontend/sessions/kde.desktop.in @@ -0,0 +1,45 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=XSession +Exec=@KDE_BINDIR@/startkde +TryExec=@KDE_BINDIR@/startkde +Name=KDE +Name[hi]=केडीई +Name[mn]=КДЭ +Name[ta]=Kஏற்றக் காவலன் +Name[xh]=iKDE +Name[xx]=xxKDExx +Comment=The K Desktop Environment. A powerful Open Source graphical desktop environment +Comment[bs]=K Desktop Environment. Moćan grafički desktop otvorenog izvornog koda +Comment[ca]=L'entorn d'escriptori K. Un poderós entorn d'escriptori gràfic de Codi Font Obert +Comment[cy]=Yr Amgylchedd Penbwrdd K. Amgylchedd penbwrdd graffegol pwerus, sy'n gôd-agored. +Comment[da]=K Skrivebordsmiljøet. Et kraftigt, åbent, grafisk skrivebordsmiljø +Comment[de]=Das K Desktop Environment. Eine mächtige, graphische Arbeitsumgebung und Open Source / Freie Software +Comment[el]=Το K Desktop Environment. Ένα πανίσχυρο ελεύθερης προέλευσης γραφικό περιβάλλον επιφάνειας εργασίας +Comment[es]=El Entorno de Escritorio K, un potente entorno de escritorio gráfico realizado de código abierto +Comment[et]=K töölaua keskkond on võimas vaba tarkvara graafiline töölaua keskkond +Comment[fi]=KDE-työpöytäympäristö (K Desktop Environment) on tehokas avoimen lähdekoodin graafinen työpöytäympäristö +Comment[fr]=The K Desktop Environment. Un environnement de bureau graphique, puissant et Open Source +Comment[he]=The K Desktop Environment. סביבת עבודה גרפית, בעלת-עוצמה בקוד פתוח +Comment[hi]=के डेस्कटॉप वातावरण. एक शक्तिशाली, ओपन सोर्स चित्रमय डेस्कटॉप वातावरण +Comment[hu]=A KDE grafikus munkakörnyezet, egy szabad forráskódú grafikus ablakkezelő környezet +Comment[it]=L'ambiente desktop KDE. Un potente ambiente desktop grafico Open Source +Comment[mn]=The K Desktop Environment. Хүчирхэг нээлттэй эх код бүхий график дэлгэцийн орчин +Comment[nb]=K Desktop Environment. Et kraftig grafisk skrivebordsmiljø med åpen kildekode. +Comment[nl]=De K Desktop Environment, een krachtige open source grafische desktop environment +Comment[nn]=K Desktop Environment. Eit kraftig grafisk skrivebordsmiljø med open kjeldekode. +Comment[pl]=Środowisko KDE. Potężne środowisko graficzne Wolnego Oprogramowania. +Comment[pt]=O K Desktop Environment. Um ambiente gráfico open source poderoso +Comment[pt_BR]=Acrônimo para K Desktop Environment (ou Ambiente de Trabalho K). Um poderoso ambiente de trabalho gráfico de código aberto +Comment[ro]=K Desktop Environment. Un mediu grafic cu surse deschise, foarte puternic +Comment[sk]=The K Desktop Environment. Výkonné, voľne šíriteľné grafické pracovné prostredie +Comment[sl]=Namizno okolje K. Zmogljivo grafično namizno okolje odprte kode +Comment[sr]=K Desktop Environment (KDE). Моћно графичко радно окружење отвореног кода +Comment[sv]=K-skrivbordsmiljön. En kraftfull grafisk skrivbordsmiljö med öppen källkod +Comment[ta]= Kமேல்மேசை சூழல். சக்திவாய்ந்த திறந்த ஆணைமூல சித்திர வகை மேல்மேசை சூழல் +Comment[tr]=KDE Masaüstü Yöneticisi. Güçlü bir grafiksel masaüstü ortamı +Comment[uk]=The K Desktop Environment. Потужне графічне середовище з відкритими текстами +Comment[uz]=KDE (K Desktop Environment) - кучли Open Source график иш столи муҳити +Comment[vi]=môi trường desktop K, môi trường desktop đồ hoạ mã nguồn mở rất mạnh +Comment[xx]=xxThe K Desktop Environment. A powerful Open Source graphical desktop environmentxx +Comment[zh_CN]=K 桌面环境。强大的开放源代码图形桌面环境 diff --git a/kdm/kfrontend/sessions/larswm.desktop b/kdm/kfrontend/sessions/larswm.desktop new file mode 100644 index 000000000..072b382c5 --- /dev/null +++ b/kdm/kfrontend/sessions/larswm.desktop @@ -0,0 +1,72 @@ +[Desktop Entry] +Type=XSession +Exec=larswm +TryExec=larswm +Name=LarsWM +Name[eo]=Lazero +Name[hi]=लार्स-डबल्यूएम +Name[ko]=라이코스 +Name[sv]=Lars WM +Name[te]=లార్స్ డబ్ల్యు ఎం +Comment=The Lars Window Manager, based on 9WM, supports tiled windows +Comment[af]=Die Lars venster bestuurder, wat op 9WM gebaseer is. Dit ondersteun geteëlde vensters. +Comment[ar]=مدير نوافذ لارس، مبني على 9WM، يدعم النوافذ المعنونة +Comment[be]=Кіраўнік вокнаў Lars Window Manager, заснаваны на 9WM +Comment[bn]=লার্স উইণ্ডো ম্যানেজার, 9WM-এর ওপর ভিত্তি করে তৈরি +Comment[bs]=Lars Window Manager, baziran 9WM, podržava popločane prozore +Comment[ca]=El gestor de finestres Lars, basat en 9WM, permet finestres alicatades +Comment[csb]=Menedżer òknów Larsa, ùsôdzony na spòdlém 9WM, wspiérô kachelkòwanié òknów +Comment[cy]=Y Trefnydd Ffenestri Lars, wedi'i seilio ar 9WM, sy'n cynnal ffenestri wedi'u teilio +Comment[da]=Lars vindueshåndteringen, baseret på 9WM, understøtter fliselagte vinduer +Comment[de]=Lars-Fenstermanager, basiert auf 9WM und unterstützt gekachelte Fenstern +Comment[el]=Ο διαχειριστής παραθύρων Lars, βασισμένος στον 9WM, υποστηρίζει παράθυρα σε παράθεση +Comment[eo]=La Laza Fenestroadministrilo, devenigita de 9FA +Comment[es]=El Lars Window Manager, basado en 9WM, soporta mosaico de ventanas +Comment[et]=Larsi aknahaldur, mille aluseks on 9WM, toetab paanitud aknaid +Comment[eu]=Lars leiho kudeatzailea, 9WM-n oinarritua, leiho-mosaikorako euskarria duena +Comment[fa]=مدیر پنجرۀ Lars، بر اساس 9WM، پنجرههای کاشیشده را پشتیبانی میکند. +Comment[fi]=Lars-ikkunaohjelma. Pohjautuu 9WM:ään ja tukee järjestettyjä ikkunoita +Comment[fr]=The Lars Window Manager, fondé sur 9WM, avec une gestion des fenêtres en mosaïque +Comment[fy]=De Lars Window Manager, basearre op 9WM. Biedt stipe foar tegele finsters +Comment[gl]=O Xestor de Fiestras Lars, baseado en 9WM, atura fiestras en mosaico +Comment[he]=The Lars Window Manager, מבוסס על 9WM, תומך בחלונות פרושים +Comment[hi]=9डबल्यूएम आधारित लार्स विंडो प्रबंधक, चटाई विडो समर्थित करता है +Comment[hr]=Lars upravitelj prozora, zasnovan na 9WMu, podržava popločene prozore +Comment[hu]=Lars ablakkezelője, a 9WM alapján, mozaikszerű ablakelrendezési lehetőséggel +Comment[is]=Lars gluggastjórinn, byggður á 9WM og styður flísaða glugga +Comment[it]=Il Window Manager di Lars, basato su 9WM, supporta le finestre affiancate +Comment[ja]=Lars ウィンドウマネージャ, 9WMベース, タイル表示をサポートするウィンドウマネージャ +Comment[kk]=9WM-негіздеген терезе менеджері +Comment[km]=Lars Window Manager ផ្អែកលើ 9WM គាំទ្របង្អួចជាក្បឿង +Comment[lt]=Lars langų tvarkyklė, paremta 9WM, turi galimybę iškloti langus +Comment[mk]=Lars Window Manager, менаџер базиран на 9WM, поддржува поплочени прозорци +Comment[mn]=The Lars Window Manager, 9WM дээр суурилсан, олон цонхтой +Comment[ms]=Pengurus Tetingkap Lars, berdasarkan 9WM, menyokong tetingkap berjubin +Comment[mt]=Lars Window Manager - ibbażat fuq 9WM u jiflaħ windows imqassma f'madum +Comment[nb]=Lars vindusbehandler, basert på 9WM, støtter flislagte vinduer +Comment[nds]=De Lars-Finsterpleger, opbuut op 9WM, ünnerstütt kachelte Finstern +Comment[ne]=9WM मा आधारित लार्स सञ्झ्याल प्रबन्धक, टायल गरिएका सञ्झ्यालहरू समर्थन गर्छ +Comment[nl]=De Lars Window Manager, gebaseerd op 9WM. Biedt ondersteuning voor getegelde vensters +Comment[nn]=Vindaugssjefen Lars, basert på 9WM, støttar flislagde vindauge +Comment[pa]=ਲਾਰਸ ਫਾਇਲ਼ ਮੈਨੇਜਰ 9WM ਤੇ ਆਧਾਰਿਤ, ਝਰੋਖਿਆਂ ਦੇ ਸਮੇਟਣ ਲਈ ਸਹਾਈ +Comment[pl]=Menedżer okien Larsa, stworzony na podstawie 9WM, obsługuje kafelkowanie okien +Comment[pt]=O Lars Window Manager, baseado no 9WM, e que suporta janelas lado-a-lado +Comment[pt_BR]=O gerenciador de janelas Lars, baseado no 9WM, com suporte a janelas ladrilhadas +Comment[ro]=Managerul de ferestre al lui Lars, bazat pe 9WM. Suportă ferestre aranjate în mozaic +Comment[ru]=Оконный менеджер на основе 9wm +Comment[rw]=Mugenga Dirishya Lars, ishingiye kuri 9WM, yemera amadirishya agerekeranye +Comment[se]=Lásegieđahalli Lars, ráhkaduvvon 9WM vuođul, doarju bálddalas lásiid. +Comment[sk]=The Lars Window Manager založený na 9WM s podporou okien usporiadaných do dlaždíc +Comment[sl]=Larsov okenski upravitelj, na osnovi 9WM, podpira porazdeljena okna +Comment[sr]=„Lars Window Manager“, заснован на 9WM-у, подржава наслагане прозоре +Comment[sr@Latn]=„Lars Window Manager“, zasnovan na 9WM-u, podržava naslagane prozore +Comment[sv]=Lars fönsterhanterare, baserad på 9WM, med stöd för fönster sida vid sida +Comment[ta]=9WM அடிபடையிலான லார்ஸ் சாளர மேலாளர் சீரமைக்கப்பட்ட சாளரங்களை ஆதரிக்கிறது +Comment[th]=ระบบจัดการหน้าต่าง Lars สร้างจาก 9WM สนับสนุนการปูเรียงหน้าต่าง +Comment[tr]=Lars Pencere Yöneticisi +Comment[tt]=Lars Window Manager, 9WM asılında, bülengän täräzä tota +Comment[uk]=The Lars Window Manager, засновано на 9WM, підтримує розташування вікон плиткою +Comment[vi]= Trình Quản lý Cửa sổ Lars, dựa vào 9WM, hỗ trợ cửa sổ xếp ngói +Comment[wa]=Li manaedjeu d' purnea d' Lasrs (Lars Window Manager), båzé so 9WM, sopoite les purneas å pus grand +Comment[zh_CN]=Lars 窗口管理器,基于 9WM,支持平铺窗口 +Comment[zh_TW]=The Lars 視窗管理程式,基於 9WM,支援棋盤化視窗 diff --git a/kdm/kfrontend/sessions/lwm.desktop b/kdm/kfrontend/sessions/lwm.desktop new file mode 100644 index 000000000..2f3e0ff00 --- /dev/null +++ b/kdm/kfrontend/sessions/lwm.desktop @@ -0,0 +1,76 @@ +[Desktop Entry] +Type=XSession +Exec=lwm +TryExec=lwm +Name=LWM +Name[eo]=MFA +Name[hi]=एलडबल्यूएम +Name[te]=ఎల్ డబ్ల్యు ఎం +Name[th]=ตัวจัดการหน้าต่าง LWM +Comment=The Lightweight Window Manager. A non-configurable, bare window manager +Comment[af]=Die lig gewig venster bestuurder. +Comment[ar]=مدير النوافذ خفيف العبء، مدير نوافذ مجرّد غير قابل للإعداد +Comment[be]=The Lightweight Window Manager. Пусты кіраўнік вокнаў без падтрымкі настаўленняў +Comment[bn]=দি লাইটওয়েট উইণ্ডো ম্যানেজার +Comment[bs]=Lightweight Window Manager. Ne-podesivi, potpuno osnovni window manager +Comment[ca]=El gestor de finestres Lightweight. Un gestor de finestres no configurable i pelat +Comment[cs]=The Lightweight Window Manager. Prostý a nekonfigurovatelný správce oken +Comment[csb]=Lightweight Window Manager (Letczi menedżer òknół). Bro prosti menedżer òknów bez mòżnoté kònfigùracëji +Comment[cy]=Y Trefnydd Ffenestri Ysgafn. Trefnydd ffenestri noeth, na ellir ffurfweddu +Comment[da]=Lightweight Window Manager, en ikke-indstillelig, minimal vindueshåndtering +Comment[de]=Lightweight Window Manager -- reiner Fenstermanager ohne Einstellmöglichkeiten +Comment[el]=Ο ελαφρύς διαχειριστής παραθύρων. Ένας μη παραμετροποιήσιμος, πολύ απλός διαχειριστής παραθύρων +Comment[eo]=Malpeza Fenestroadministrilo +Comment[es]=El Lightweight Window Manager, un sencillísimo y no configurable gestor de ventanas +Comment[et]=Imeväike aknahaldur on seadistamatu, sõna otseses mõttes ainult akende haldur +Comment[eu]=Libhtweight leiho kudeatzailea. Konfiguraziorik onartzen ez duen leiho-kudeatzaile zeharo sinplea +Comment[fa]=مدیر پنجرۀ سبک، مدیر پنجرۀ غیرقابل پیکربندی و ساده +Comment[fi]=Kevyt ikkunaohjelma. Paljas, ei muokattavissa oleva ikkunaohjelma +Comment[fr]=The Lightweight Window Manager. Un gestionnaire de fenêtres non configurable et nu +Comment[fy]=De Lightweight Window Manager, in net-ynstelbere, keale finstersmanager +Comment[gl]=O Xestor de Fiestras Lixeiro. +Comment[he]=The Lightweight Window Manager. מנהל חלונות מצומצם בלי אפשרויות להגדרה. +Comment[hi]=हल्का विंडो प्रबंधक. खाली विंडो प्रबंधक जो कॉन्फ़िगर नहीं हो सकता +Comment[hr]=LWM - Lightweight Window Manager (Lagani upravitelj prozora) - Temeljni upravitelj prozora bez mogućnosti konfiguriranja +Comment[hu]=Lightweight Window Manager, egy könnyen konfigurálható, alapszintű ablakkezelő +Comment[is]=Hinn létti gluggastjóri. Ekki stillanlegur og hrár +Comment[it]=Il Window Manager Leggero. Un window manager essenziale e non configurabile +Comment[ja]=設定項目のない、単純なウィンドウマネージャ +Comment[ka]=მსუბუქი არაკონფიგურირებადი ფანჯრების მენეჯერი +Comment[kk]=Lightweight Window Manager. Баптауы жоқ, жай терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចសមត្ថភាពខ្សោយ ។ កម្មវិធីគ្រប់គ្រងបង្អួចដែលគ្មានអ្វីបិទបាំង ហើយមិនអាចកំណត់រចនាសម្ព័ន្ធបាន +Comment[lt]=„Lengva“ langų tvarkyklė. Nekonfigūruojama, „plika“ langų tvarkyklė +Comment[lv]=Vieglais logu menedžeris. Nekonfigurējams, vienkāršs logu menedžeris +Comment[mk]=Lightweight Window Manager. Неконфигурабилен, скоро празен менаџер на прозорци +Comment[mn]=The Lightweight Window Manager. Тохируулгах боломжгүй,цонхны удирдлага +Comment[ms]=Pengurus tetingkap Ringan. Tidak boleh konfigur, pengurus tetingkap terdedah +Comment[mt]=Lightweight Window Manager. Window Manager sempliċi u mhux konfigurabbli +Comment[nb]=Lettvekts vindusbehandler- Lightweight Window Manager. En enkel vindusbehandler uten innstillinger. +Comment[nds]=De "Lightweight"-Finsterpleger. En reen Finsterpleger ahn Instellen +Comment[ne]=हल्का वजन सञ्झ्याल प्रबन्धक । कन्फिगर गर्न नसकिने, बेयर सञ्झ्याल प्रबन्धक +Comment[nl]=De Lightweight Window Manager, een niet-instelbare, kale windowmanager +Comment[nn]=Lightweight Window Manager. Ein enkel vindaugssjef utan innstillingar. +Comment[pa]=ਇੱਕ ਹਲਕਾ, ਨਾ-ਸੰਰਚਿਤਯੋਗ ਪੱਟੀ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Lightweight Window Manager (Lekki menedżer okien). Surowy menedżer okien bez możliwości konfiguracji +Comment[pt]=O Lightweight Window Manager. Um gestor de janelas não-configurável e básico +Comment[pt_BR]=Um gerenciador de janelas leve, sendo básico e não muito configurável +Comment[ro]=Lightweight Window Manager. Un manager de ferestre neconfigurabil și minimal +Comment[ru]=Облегчённый, не настраиваемый простой оконный менеджер +Comment[rw]=Mugenga Dirishya Yoroshye. Mugenga dirishya idafite ikintu, itabonezwa. +Comment[se]=Lightweight Window Manager. Oktageardánis lásegieđahalli mii ii lea heivehahtti. +Comment[sk]=The Lightweight Window Manager. Nenastaviteľný, jednoduchý správca okien +Comment[sl]=Lahek okenski upravitelj. Ni nastavljiv, osnovni okenski upravitelj +Comment[sr]=„Lightweight Window Manager“. Неподесив, голи менаџер прозора +Comment[sr@Latn]=„Lightweight Window Manager“. Nepodesiv, goli menadžer prozora +Comment[sv]=Den lättviktiga fönsterhanteraren. En enkel fönsterhanterare utan anpassningsmöjligheter +Comment[ta]=குறைந்த எடையுள்ள சாளர மேலாளர். வடிவமைக்க முடியாத +Comment[th]=Lightweight Window Manager ระบบจัดการหน้าต่างเปล่าๆ ที่ไม่สามารถปรับแต่งได้เลย +Comment[tr]=Lightweight Pencere Yöneticisi. Yapılandırılamayan, kaba pencere yönetici +Comment[tt]=Lightweight Window Manager. Caylanmí torğan, ciñel täräzä-idäräçe +Comment[uk]=Невеличкий менеджер вікон без можливості налаштування +Comment[vi]=Trình Quản lý Cửa sổ Nhẹ ký. Rất khó cấu hình +Comment[wa]=Li Ledjir Manaedjeu di Purneas (Lightweight Window Manager). On manaedjeu di purneas tot simpe, nén apontiåve +Comment[zh_CN]=轻量级窗口管理器。不可配置的裸窗口管理器 +Comment[zh_TW]=一個輕量化的視窗管理程式。不可組態、只有基礎視窗管理的視窗管理程式 +# this can't be used as a session in itself +Hidden=true diff --git a/kdm/kfrontend/sessions/matchbox.desktop b/kdm/kfrontend/sessions/matchbox.desktop new file mode 100644 index 000000000..8c6e2fd52 --- /dev/null +++ b/kdm/kfrontend/sessions/matchbox.desktop @@ -0,0 +1,82 @@ +[Desktop Entry] +Type=XSession +Exec=matchbox +TryExec=matchbox +Name=Matchbox +Name[bn]=ম্যাচবক্স +Name[eo]=Alumetujo +Name[hi]=मैचबॉक्स +Name[ne]=मिल्ने बाकस +Name[pa]=ਮੈਚ-ਬਾਕਸ +Name[rw]=Agasandukugahura +Name[ta]=பொருத்தப்பெட்டி +Name[te]=అగ్గి పెట్టె +Name[tg]=Қуттии гӯгирд +Name[wa]=Boesse di brocales (Matchbox) +Comment=A window manager for handheld devices +Comment[af]='n Venster bestuurder vir draagbare toestelle +Comment[ar]=مدير نوافذ للأجهزة اليدوية +Comment[be]=Кіраўнік вокнаў для кішанёвых кампутараў +Comment[bn]=হ্যাণ্ডহেল্ড যন্ত্রাদির উপযোগী একটি উইণ্ডো ম্যানেজার +Comment[bs]=Window manager za ručne uređaje +Comment[ca]=Un gestor de finestres per a dispositius de ma +Comment[cs]=Správce oken pro PDA +Comment[csb]=Menedżer òknów dlô palmtopów +Comment[cy]=Trefnydd ffenestri ar gyfer llawiaduron +Comment[da]=En vindueshåndtering for håndholdte enheder +Comment[de]=Fenstermanager für portable Geräte +Comment[el]=Ένας διακομιστής παραθύρων για συσκευές παλάμης +Comment[eo]=Fenestroadministrilo por manaj aparatoj +Comment[es]=Un gestor de ventanas para dispositivos de mano +Comment[et]=Aknahaldur pihuarvutitele +Comment[eu]=Eskuan erabiltzeko gailuentzako leiho kudeatzailea +Comment[fa]=یک مدیر پنجره برای دستگاههای دستی +Comment[fi]=ikkunaohjelma PDA-laitteisiin +Comment[fr]=Un gestionnaire de fenêtres pour les périphériques contrôlés à la main +Comment[fy]=In finstersmanager foar hânkompjûters +Comment[gl]=Un xestor de fiestras para dispositivos manuais +Comment[he]=מנהל חלונות למכשירים נישאים +Comment[hi]=हाथ में रखने वाले औज़ारों के लिए विंडो प्रबंधक +Comment[hr]=Upravitelj prozora za ručna računala +Comment[hu]=Ez az ablakkezelő elsősorban kéziszámítógépekhez ajánlott +Comment[is]=Gluggastjóri fyrir lófatölvur +Comment[it]=Un window manager per palmari +Comment[ja]=ハンドヘルドデバイス向けのウィンドウマネージャ +Comment[ka]=ფანჯრების მენეჯერი მობილური მოწყობილობებისთვის +Comment[kk]=Қол құрылғыларға арналған терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួច សម្រាប់ឧបករណ៍យួរដៃ +Comment[ko]=핸드헬드 장치를 위한 창 관리자 +Comment[lt]=Langų tvarkyklė įvairiems įrenginiams +Comment[lv]=Logu menedžeris priekš portatīvajām iekārtām +Comment[mk]=Менаџер на прозорци за преносни уреди +Comment[mn]=Гар төхөөрөмжид зориулсан цонхны удирдлага +Comment[ms]=Pengurus tetingkap untuk peranti telapak +Comment[mt]=Window manager għal apparat "handheld" +Comment[nb]=En vindusbehandler for håndholdte enheter +Comment[nds]=Finsterpleger för Handreekner +Comment[ne]=ह्यान्डहेल्ड यन्त्रहरूका लागि सञ्झ्याल प्रबन्धक +Comment[nl]=Een windowmanager voor handcomputers +Comment[nn]=Ein vindaugssjef for handhaldne einingar +Comment[pa]=ਹੱਥਲੇ ਜੰਤਰਾਂ ਲਈ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Menedżer okien dla palmtopów +Comment[pt]=Um gestor de janelas para dispositivos móveis +Comment[pt_BR]=Um gerenciador de janelas para dispositivos handheld +Comment[ro]=Un manager de ferestre pentru PDA-uri +Comment[ru]=Оконный менеджер для мобильных устройств +Comment[rw]=Mugenga dirishya y'amapareye atwarwa-ntoki +Comment[se]=Lásegieđahalli giehtaovttadagaid várás +Comment[sk]=Správca okien pre prenosné zariadenia +Comment[sl]=Okenski upravitelj za dlančne naprave +Comment[sr]=Менаџер прозора за мале преносне уређаје +Comment[sr@Latn]=Menadžer prozora za male prenosne uređaje +Comment[sv]=Fönsterhanterare för handburna enheter +Comment[ta]=கையில் உள்ள சாதனங்களுக்கான சாளர மேலாளர் +Comment[tg]=Як мудири тиреза барои дастгоҳҳои дастӣ +Comment[th]=ระบบจัดการหน้าต่างของอุปกรณ์มือถือ +Comment[tr]=El bilgisayarları için bir pencere yöneticisi +Comment[tt]=Qul cıhazı öçen täräzä-idäräçe +Comment[uk]=Менеджер вікон для портативних пристроїв +Comment[vi]=Trình quản lý cửa sổ dành cho thiết bị cầm tay +Comment[wa]=On manaedjeu di purneas po les éndjins ebarkés +Comment[zh_CN]=手持设备的窗口管理器 +Comment[zh_TW]=一個掌上型設備所使用的視窗管理程式 diff --git a/kdm/kfrontend/sessions/metacity.desktop b/kdm/kfrontend/sessions/metacity.desktop new file mode 100644 index 000000000..d2a5a5537 --- /dev/null +++ b/kdm/kfrontend/sessions/metacity.desktop @@ -0,0 +1,83 @@ +[Desktop Entry] +Type=XSession +Exec=metacity +TryExec=metacity +Name=Metacity +Name[bn]=মেটাসিটি +Name[eo]=Metaurbo +Name[hi]=मेटासिटी +Name[mn]=Метасити +Name[ne]=मेटासिटी +Name[rw]=Metacity(Mugenga-dirishya) +Name[ta]=மெடாசிட்டி +Name[te]=మెటాసిటి +Comment=A lightweight GTK2 based window manager +Comment[af]=Die lig gewig GTK2 gebaseerde venster bestuurder +Comment[ar]=مدير نوافذ خفيف العبء مبني على GTK2 +Comment[be]=Лёгкі кіраўнік вокнаў, заснаваны на GTK2 +Comment[bn]=একটি হাল্কা GTK2 ভিত্তিক উইণ্ডো ম্যানেজার +Comment[bs]=Lagani window manager baziran na GTK2 +Comment[ca]=Un gestor de finestres lleuger basat en GTK2 +Comment[cs]=Malý správce oken založený na GTK2 +Comment[csb]=Menedżer òknół ò môłëch żądniach, òpiarti na GTK2 +Comment[cy]=Trefnydd ffenestri ysgafn, wedi'i seilio ar GTK2 +Comment[da]=En letvægts GTK2 baseret vindueshåndtering +Comment[de]=Schlanker Fenstermanager, der auf GTK2 basiert +Comment[el]=Ένας ελαφρύς διαχειριστής παραθύρων βασισμένος στο GTK2 +Comment[eo]=Malpeza fenestroadministrilo +Comment[es]=Un gestor de ventanas ligero basado en GTK2 +Comment[et]=Imeväike aknahaldur, mille aluseks on GTK2 +Comment[eu]=GTK2n oinarritutako leiho kudeatzaile arin bat +Comment[fa]=GTK2 سبک بر اساس مدیر پنجره +Comment[fi]=Kevyt, GTK2-pohjainen ikkunaohjelma +Comment[fr]=Un gestionnaire de fenêtres fondé sur GTK2 et léger +Comment[fy]=In lichtgewicht op GTK2 basearre finstersmanager +Comment[gl]=Un xestor de fiestras lixeiro baseado en GTK2 +Comment[he]=מנהל חלונות קל מבוסס GTK2 +Comment[hi]=जीटीके2 आधारित हल्का विंडो प्रबंधक +Comment[hr]=Lagani upravitelj prozora zasnovan na GTK2 +Comment[hu]=Egy egyszerű, GTK2-alapú ablakkezelő +Comment[is]=Léttur gluggastjóri byggður á GTK2 +Comment[it]=Un window manager leggero basato su GTK2 +Comment[ja]=GTK2 ベースの軽量なウィンドウマネージャ +Comment[ka]=GTK2-ს ბაზაზე მსუბუქი ფანჯრის მენეჯერი +Comment[kk]=GTK2-негіздеген, жеңіл терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចសមត្ថភាពខ្សោយ ដែលផ្អែកលើ GTK2 +Comment[ko]=가벼운 GTK2 기반 창 관리자 +Comment[lt]=Nedaug resursų reikalaujanti langų tvarkyklė, paremta GTK2 +Comment[lv]=Viegls GTK 2 bāzēts logu menedžeris +Comment[mk]=Лесен менаџер на прозорци базиран на GTK2 +Comment[mn]=Хөнгөн GTK2 суурьт цонхны удирдагч +Comment[ms]=Pengurus tetingkap berasaskan GTK2 ringan +Comment[mt]=Window manager ħafif ibbażat fuq GTK2 +Comment[nb]=En lettvekts vindusbehandler basert på GTK2 +Comment[nds]=En ranke Finsterpleger, opbuut op GTK2 +Comment[ne]=सञ्झ्याल प्रबन्धकमा आधारित हल्का वजन GTK2 +Comment[nl]=Een lichtgewicht op GTK2 gebaseerde windowmanager +Comment[nn]=En lett vindaugssjef basert på GTK2 +Comment[pa]=ਇੱਕ ਹਲਕਾ GTK2 ਤੇ ਆਧਾਰਿਤ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Menedżer okien o małych wymaganiach, oparty na GTK2 +Comment[pt]=Um gestor de janelas leve, baseado em GTK2 +Comment[pt_BR]=Um gerenciador de Janelas leve baseado em GTK2 +Comment[ro]=Un manager de ferestre mic bazat pe GTK2 +Comment[ru]=Лёгкий оконный менеджер на основе GTK2 +Comment[rw]=Mugenga dirishya ishingiye kuri GTK2 yoroshye +Comment[se]=Geahpes GTK2-vuođđoduvvon lásegieđahalli +Comment[sk]=Nenáročný správca okien založený na GTK2 +Comment[sl]=Lahek okenski upravitelj na osnovi GTK2 +Comment[sr]=Лагани менаџер прозора заснован на GTK2 +Comment[sr@Latn]=Lagani menadžer prozora zasnovan na GTK2 +Comment[sv]=Lättviktig GTK2-baserad fönsterhanterare +Comment[ta]=சாளர மேலாளர் அடிப்படையிலான ஒரு குறைந்த எடை GTK2 +Comment[te]=తెలికైన జిటికె2 ఆధారిత విండొ అభికర్త +Comment[tg]=Сабуки GTK2 ба асоси мудири тиреза +Comment[th]=ระบบจัดการหน้าต่างขนาดเบาที่ใช้ GTK2 +Comment[tr]=Gtk2 tabanlı hafif bir pencere yöneticisi +Comment[tt]=GTK2 asılında ciñel täräzä-idäräçe +Comment[uk]=Простий менеджер вікон для GTK2 +Comment[uz]=GTK2 asosida yaratilgan oddiy oyna boshqaruvchi +Comment[uz@cyrillic]=GTK2 асосида яратилган оддий ойна бошқарувчи +Comment[vi]=Trình quản lý cửa sổ nhỏ gọn dựa trên GTK2 +Comment[wa]=On ledjir manaedjeu di purneas båzé so GTK2 +Comment[zh_CN]=轻量级 GTK2 窗口管理器 +Comment[zh_TW]=一個基於 GTK2 的輕量化視窗管理程式 diff --git a/kdm/kfrontend/sessions/mwm.desktop b/kdm/kfrontend/sessions/mwm.desktop new file mode 100644 index 000000000..b1d6e2acd --- /dev/null +++ b/kdm/kfrontend/sessions/mwm.desktop @@ -0,0 +1,78 @@ +[Desktop Entry] +Type=XSession +Exec=mwm +TryExec=mwm +Name=MWM +Name[eo]=MFA +Name[hi]=एमडबल्यूएम +Name[te]=ఎం డబ్ల్యు ఎం +Name[th]=ตัวจัดการหน้าต่าง MWM +Comment=The Motif Window Manager +Comment[af]=Die Motif venster bestuurder +Comment[ar]=مسيير النوافذ Motif +Comment[be]=Кіраўнік вокнаў Motif +Comment[bn]=দি মোটিফ উইণ্ডো ম্যানেজার +Comment[bs]=Motif Window Manager +Comment[ca]=El gestor de finestres Motif +Comment[cs]=Motif Window Manager +Comment[csb]=Menedżer òknów Motif +Comment[cy]=Y Trefnydd Ffenestri Motif +Comment[da]=Motif vindueshåndtering +Comment[de]=Motif-Fenstermanager +Comment[el]=Ο διαχειριστής παραθύρων Motif +Comment[eo]=Motifa fenestroadministrilo +Comment[es]=El gestor de ventanas de Motif +Comment[et]=Motifi aknahaldur +Comment[eu]=Motif leiho kudeatzailea +Comment[fa]=مدیر پنجره موتیف +Comment[fi]=Motif-ikkunaohjelma +Comment[fr]=Le gestionnaire de fenêtres Motif +Comment[fy]=De Motif Window Manager +Comment[ga]=Bainisteoir fuinneoga Motif +Comment[gl]=O Xestor de Fiestras Motif +Comment[he]=מנהל החלונות Motif +Comment[hi]=मोटिफ विंडो प्रबंधक +Comment[hr]=Motif upravitelj prozora +Comment[hu]=Motif ablakkezelő +Comment[is]=Motif gluggastjórinn +Comment[it]=Il window manager di Motif +Comment[ja]=Motif 風のウィンドウマネージャ +Comment[ka]=ფანჯრის მენეჯერი Motif +Comment[kk]=Motif терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួច Motif +Comment[ko]=Motif 창 관리자 +Comment[lt]=Motif langų tvarkyklė +Comment[lv]=Motif logu menedžeris +Comment[mk]=Менаџерот на прозорци Motif +Comment[mn]=Motif Цонхны удирдагч +Comment[ms]=Pengurus Tertingkap Motif +Comment[mt]=Window manager tal-Motif +Comment[nb]=Motif vindusbehandler +Comment[nds]=De Motif-Finsterpleger +Comment[ne]=मोटिफ सञ्झ्याल प्रबन्धक +Comment[nl]=De Motif Window Manager +Comment[nn]=Motif-vindaugssjefen +Comment[pa]=Motif ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Menedżer okien Motif +Comment[pt]=O gestor de janelas do Motif +Comment[pt_BR]=O gerenciador de janelas Motif +Comment[ro]=Managerul de ferestre Motif +Comment[ru]=Оконный менеджер Motif +Comment[rw]=Mugenga Dirishya Umutako +Comment[se]=Motif lásegieđahalli +Comment[sk]=Správca okien Motif +Comment[sl]=Okenski upravitelj Motif +Comment[sv]=Motifs fönsterhanterare +Comment[ta]=மோடிஃப் சாளர மேலாளர் +Comment[te]=మొటిఫ్ విండొ అభికర్త +Comment[tg]=Мавзӯъи мудири тиреза +Comment[th]=ระบบจัดการหน้าต่างของโมทิฟ +Comment[tr]=Motif Pencere Yöneticisi +Comment[tt]=Motif Täräzä İdäräçe +Comment[uk]=Менеджер вікон Motif +Comment[uz]=Motif oyna boshqaruvchi +Comment[uz@cyrillic]=Motif ойна бошқарувчи +Comment[vi]=Trình quản lý cửa sổ Motif +Comment[wa]=Li manaedjeu di purneas di Motif +Comment[zh_CN]=Motif 窗口管理器 +Comment[zh_TW]=Motif 視窗管理程式 diff --git a/kdm/kfrontend/sessions/olvwm.desktop b/kdm/kfrontend/sessions/olvwm.desktop new file mode 100644 index 000000000..e181331bc --- /dev/null +++ b/kdm/kfrontend/sessions/olvwm.desktop @@ -0,0 +1,71 @@ +[Desktop Entry] +Type=XSession +Exec= +TryExec= +Name=OLVWM +Name[br]=OVLWM +Name[eo]=OLVFA +Name[hi]=ओएलवीडबल्यूएम +Name[te]=ఒ ఎల్ వి డబ్యు ఎం +Comment=The OpenLook Virtual Window Manager. OLWM enhanced for handling of virtual desktops +Comment[af]=Die OpenLook virtuele venster bestuurder. OLWM wat uitgebrei is vir virtuele werkskerms +Comment[ar]=مدير النوافذ الوهمي OpenLook، محسّن للتعامل مع أسطح المكتب الوهمية +Comment[be]=The OpenLook Virtual Window Manager. OLWM дапрацаваны для падтрымкі віртуальных працоўных сталоў +Comment[bn]=দি ওপেনলুক ভার্চুয়াল উইণ্ডো ম্যানেজার। OLWM-এর বর্ধিত সংস্করণ যাতে ভার্চুয়াল ডেস্কটপ সমর্থিত +Comment[bs]=OpenLook Virtual Window Manager. OLWM proširen podrškom za virtualne desktope +Comment[ca]=El OpenLook Virtual Window Manager. OLWM millorat per a escriptoris virtuals de ma +Comment[csb]=OpenLook Virtual Window Manager. OLWM zbògacony ò wspiarce wirtualnëch pùltów +Comment[cy]=Y Trefnydd Ffenestri Rhith GolwgAgored (OpenLook). OLWM wedi'i wella i gynnal penbyrddau rhith +Comment[da]=OpenLook Virtual Window Manager. OLWM udvidet med håndtering af virtuelle desktoppe +Comment[de]=OpenLook virtueller Fenstermanager, OLWM mit verbesserter Verwaltung von virtuellen Arbeitsflächen +Comment[el]=Ο OpenLook εικονικός διαχειριστής παραθύρων. Ο OLWM εμπλουτισμένος με διαχείριση εικονικών επιφανειών εργασίας +Comment[eo]=Fenestroadministrilo por virtualaj labortabloj +Comment[es]=El OpenLook Virtual Window Manager, un versión mejorada de OLWM con soporte para escritorios virtuales +Comment[et]=OpenLook virtuaalne aknahaldur on OLWM, mida on täiendatud virtuaalsete töölaudade võimalusega +Comment[eu]=OpenLook leiho kudeatzaile birtuala. mahaigain birtualak kudeatzeko OLWM hobetua +Comment[fa]=مدیر پنجرۀ مجازی OpenLook. OLWM گسترشیافته برای گرداندن رومیزیهای مجازی +Comment[fi]=OpenLook virtuaalinen ikkunanhallinta. OLWM:n paranneltu versio,joka käsittelee virtuaalityöpöytiä paremmin +Comment[fr]=The OpenLook Virtual Window Manager. OLWM avec en plus la gestion des bureaux virtuels +Comment[fy]=De OpenLook Virtual Window Manager. OLWM útbreide mei firtuele buroblêden +Comment[gl]=O OpenLook Virtual Window Manager. OLWM mellorado para manexar escritórios virtuais +Comment[he]=The OpenLook Virtual Window Manager. OLWM משופר בשביל טיפול בשולחנות עבודה וירטואליים +Comment[hi]=ओपनलुक आभासी विंडो प्रबंधक. ओएलडबल्यूएम को आभासी डेस्कटॉप हैंडल करने के लिए बेहतर बनाया गया +Comment[hr]=OpenLook virtualni upravitelj prozora - OLWM unaprijeđen mogućnošću rukovanja s virtualnim radnim površinama +Comment[hu]=OpenLook Virtual Window Manager (OLWM), virtuális munkaasztalok kezelésére is képes +Comment[is]=OpenLook sýndargluggastjórinn. Endurbættur með OLWM til að styðja sýndarskjáborð +Comment[it]=L'OpenLook Virtual Window Manager. OLWM migliorato per gestire i desktop virtuali +Comment[ja]=OpenLook 仮想ウィンドウマネージャ, OLWM 強化仮想デスクトップ +Comment[ka]=გაუმჯობესებული ფანჯრის მენეჯერი OpenLook, რამდენიმე სამუშაო დაფის მხარდაჭერით +Comment[kk]=OpenLook Virtual Window Manager - виртуалды үстелдерді қолдайтын терезе менеджері +Comment[km]=OpenLook Virtual Window Manager ។ OLWM ដែលបានធ្វើឲ្យប្រសើរ សម្រាប់ការប្រើផ្ទៃតុនិមិត្ត ។ +Comment[lt]=OpenLook virtuali langų tvarkyklė. OLWM išplėsta su virtualių darbastalių palaikymu +Comment[mk]=OpenLook Virtual Window Manager. OLWM менаџерот со подобрено ракување на виртуелни површини +Comment[ms]=Pengurus Tetingkap Maya OpenLook. OLWM dipertingkat untuk mengendalikan desktop maya +Comment[mt]=OpenLook Virtual Window Manager - OLWM flimkien ma' desktops virtwali. +Comment[nb]=OpenLook Virtual Window Manager. OLWM utvidet med virtuelle skrivebord. +Comment[nds]=De "OpenLook Virtual Window Manager" is OLWM verwiedert üm virtuelle Schriefdischen +Comment[ne]=अवास्तविक डेस्कटपहरूको ह्यान्डलका लागि बृद्धि गरिएको OLWM खुला देखिने अवास्तविक सञ्झ्याल प्रबन्धक । +Comment[nl]=De OpenLook Virtual Window Manager. OLWM uitgebreid met virtuele bureaubladen +Comment[nn]=OpenLook Virtual Window Manager. OLWM utvida med virtuelle skrivebord. +Comment[pa]=OpenLook Virtual Window Manager. OLWM ਫਰਜ਼ੀ ਵੇਹੜਿਆਂ ਲਈ ਖਾਸ ਤੌਰ ਤੇ ਤਿਆਰ +Comment[pl]=OpenLook Virtual Window Manager. OLWM wzbogacony o obsługę wirtualnych pulpitów +Comment[pt]=O OpenLook Virtual Window Manager. Um OLWM melhorado para lidar com ecrãs virtuais +Comment[pt_BR]=Acrônimo para OpenLook Virtual Window Manager, o OLWM melhorado para a manipulação de áreas de trabalho virtuais +Comment[ro]=OpenLook Virtual Window Manager. Un OLWM îmbunătățit cu ecrane virtuale +Comment[ru]=Улучшенный оконный менеджер OpenLook, поддерживающий несколько рабочих столов +Comment[rw]=Mugenga Dirishya Itagaragara GufunguraKureba. OLWM ivuguruwe mu gufasha ibiro bitagaragara +Comment[se]=The OpenLook Virtual Window Manager. OLWM buoriduvvon nu ahte das leat virtuella čállinbeavddit +Comment[sk]=Virtuálny správca okien OpenLook. OLWM rozšírený o podporu virtuálnych plôch +Comment[sl]=Open Look Virtual Window Manager je izboljšan OLWM s podporo navideznim namizjem +Comment[sr]=„OpenLook Virtual Window Manager“. OLWM побољшан за управљање виртуелним радним површинама +Comment[sr@Latn]=„OpenLook Virtual Window Manager“. OLWM poboljšan za upravljanje virtuelnim radnim površinama +Comment[sv]=Open Look virtuell fönsterhanterare. OLWM utökad för att hantera virtuella skrivbord +Comment[ta]=ஓபன்லுக் மெய்நிகர் சாளர மேலாளர். மெய்நிகர் மேல்மேசைகளை கையாளுவதற்கு OLWM மேம்படுத்தப்பட்டது. +Comment[th]=ระบบจัดการหน้าต่างเสมือน OpenLook คือ OLWM ที่ถูกเพิ่มเติมความสามารถในการรับมือกับพื้นที่ทำงานเสมือน +Comment[tr]=OpenLook Sanal Pencere Yöneticisi. +Comment[tt]=OpenLook Virtual Window Manager. Xıyalí öställär öçen yaqşırtılğan OpenLook +Comment[uk]=OpenLook Virtual Window Manager. OLWM з підтримкою віртуальних стільниць +Comment[vi]=Trình Quản lý Cửa sổ Ảo "Cái nhìn Mở". Nó được cải tiến cho việc xử lý màn hình nền ảo +Comment[wa]=Li Manaedjeu di Forveyou Purnea OpenLook (OpenLook Virtual Window Manager). OLWM permete d' apougnî des forveyowès sicribannes +Comment[zh_CN]=OpenLook 虚拟窗口管理器。OLWM 特别增强了虚拟桌面的处理 +Comment[zh_TW]=Openlook 視窗管理程式。基於 OLWM 並強化管理虛擬桌面 diff --git a/kdm/kfrontend/sessions/olwm.desktop b/kdm/kfrontend/sessions/olwm.desktop new file mode 100644 index 000000000..e3368af74 --- /dev/null +++ b/kdm/kfrontend/sessions/olwm.desktop @@ -0,0 +1,76 @@ +[Desktop Entry] +Type=XSession +Exec=olwm +TryExec=olwm +Name=OLWM +Name[eo]=OLFA +Name[hi]=ओएलडबल्यूएम +Name[te]=ఒ ఎల్ డబ్యు ఎం +Name[th]=ตัวจัดการหน้าต่าง OLWM +Comment=The traditional Open Look Window Manager +Comment[af]=Die tradisionele Open Look venster bestuurder +Comment[ar]=مسيير النوافذ Open Look التقليدي +Comment[be]=Традыцыйны кіраўнік вокнаў Open Look +Comment[bn]=দি ওপেনলুক উইণ্ডো ম্যানেজার +Comment[bs]=Tradicionalni Open Look Window Manager +Comment[ca]=El tradicional gestor de finestres Open Look +Comment[cs]=Tradiční Open Look Window Manager +Comment[csb]=Tradicëjny menedżer òknów Open Look +Comment[cy]=Y Trefnydd Ffenestri GolwgAgored (OpenLook) traddodiadol +Comment[da]=Den traditionelle Open Look vindueshåndtering +Comment[de]=Der traditionelle Open-Look-Fenstermanager +Comment[el]=Ο παραδοσιακός διαχειριστής παραθύρων Open Look +Comment[eo]=La klasika OL-fenestroadministrilo +Comment[es]=El tradicional Open Look Window Manager +Comment[et]=Tavapärane OpenLooki aknahaldur +Comment[eu]=Betiko Open Look leiho kudeatzailea +Comment[fa]=مدیر پنجره Open Look سنتی +Comment[fi]=Perinteinen Open Look -ikkunaohjelma +Comment[fr]=Le gestionnaire de fenêtres traditionnel Open Look +Comment[fy]=De tradisjoneel iepen like finstersManager +Comment[gl]=O tradicional Xestor de Fiestras de Open Look +Comment[he]=Open Look Window Manager המסורתי +Comment[hi]=परम्परागत ओपन लुक विंडो प्रबंधक +Comment[hr]=Tradicionalni 'Open Look' upravitelj prozora +Comment[hu]=A hagyományos Open Look ablakkezelő +Comment[is]=Hinn hefðbundni Open Look gluggastjóri +Comment[it]=L'Open Look Window Manager tradizionale +Comment[ja]=伝統的な OpenLook ウィンドウマネージャ +Comment[ka]=OpenLook სისტემის ტრადიციული ფანჯრის მენეჯერი +Comment[kk]=Дәстүрлі Open Look терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចរូបរាងបើកចំហបុរាណ +Comment[lt]=Tradicinė Open Look langų tvarkyklė +Comment[lv]=Tradicionālais Open Look logu menedžeris +Comment[mk]=Традиционален Open Look Window Manager +Comment[mn]=Уламжилалт нээж харагч цонхны удирдагч +Comment[ms]=Pengurus Tetingkap OpenLook tradisional +Comment[mt]=Window manager tradizzjonali ta' OpenLook +Comment[nb]=Den tradisjonelle Open Look-vindusbehandleren +Comment[nds]=De traditschonelle OpenLook-Finsterpleger +Comment[ne]=खुला देखिने परम्परागत सञ्झ्याल प्रबन्धक +Comment[nl]=De traditionele Open Look Window Manager +Comment[nn]=Den tradisjonelle Open Look-vindaugssjefen +Comment[pa]=ਇੱਕ ਮੂਲ ਓਪਨ ਲੁੱਕ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Tradycyjny menedżer okien Open Look +Comment[pt]=O gestor de janelas Open Look tradicional +Comment[pt_BR]=O tradicional Open Look Window Manager +Comment[ro]=Managerul de ferestre tradițional Open Look Window Manager +Comment[ru]=Традиционный оконный менеджер системы OpenLook +Comment[rw]=Mugenga Dirishya Gufungura Kureba karande +Comment[se]=Árbevirolaš Open Look lásegieđahalli +Comment[sk]=Tradičný správca okien Open Look +Comment[sl]=Tradicionalni Open Look Window Manager +Comment[sr]=Традиционални „Open Look“ менаџер прозора +Comment[sr@Latn]=Tradicionalni „Open Look“ menadžer prozora +Comment[sv]=Den traditionella Open Look fönsterhanteraren +Comment[ta]=பழமையான சாளர மேலாளர் +Comment[te]=సాంప్రదాయ ఒపెన్ లుక్ విండొ అభికర్త +Comment[tg]=Расмшудаи Open Look-и мудири тиреза +Comment[th]=ระบบจัดการหน้าต่าง OpenLook แบบดั้งเดิม +Comment[tr]=OpenLook Pencere Yöneticisi +Comment[tt]=Open Look täräzä-idäräçeneñ töp söreme +Comment[uk]=Традиційний менеджер вікон Open Look +Comment[vi]=Trình Quản lý Cửa sổ "Cái nhìn Mở" truyền thống +Comment[wa]=Li mwaisse Manaedjeu di Purnea OpenLook (OpenLook Virtual Window Manager) +Comment[zh_CN]=传统的 OpenLook 窗口管理器 +Comment[zh_TW]=傳統的 Open Look 視窗管理程式 diff --git a/kdm/kfrontend/sessions/openbox.desktop b/kdm/kfrontend/sessions/openbox.desktop new file mode 100644 index 000000000..d0f4a06d7 --- /dev/null +++ b/kdm/kfrontend/sessions/openbox.desktop @@ -0,0 +1,84 @@ +[Desktop Entry] +Type=XSession +Exec=openbox-session +TryExec=openbox +Name=Openbox +Name[bn]=ওপেনবক্স +Name[cy]=Blwchagored (Openbox) +Name[eo]=Malfermujo +Name[hi]=ओपनबाक्स +Name[ne]=खुला बाकस +Name[pa]=ਓਪਨ ਬਕਸਾ +Name[rw]=GufunguraAgasanduku +Name[ta]=திறப்பு பெட்டி +Name[te]=ఒపెన్ బాక్స్ +Name[tg]=Кушодани қуттӣ +Comment=A lightweight window manager based on Blackbox +Comment[af]='n Lig gewig venster bestuurder wat op Blackbox gebaseer is +Comment[ar]=مدير نوافذ خفيف العبء مبني على Blackbox +Comment[be]=Лёгкі кіраўнік вокнаў, заснаваны на Blackbox +Comment[bn]=ব্ল্যাকবক্স ভিত্তিক হাল্কা উইণ্ডো ম্যানেজার +Comment[bs]=Lagani window manager baziran na Blackbox-u +Comment[ca]=Un lleuger gestor de finestres basat en Blackbox +Comment[cs]=Malý správce oken založený na Blackboxu +Comment[csb]=Menedżer òknów o môłëch żądaniach, òpairti na Blackbox +Comment[cy]=Trefnydd ffenestri ysgafn, wedi'i seilio ar Ddu-flwch +Comment[da]=En letvægts vindueshåndtering baseret på Blackbox +Comment[de]=Schlanker Fenstermanager, der auf Blackbox beruht +Comment[el]=Ένας ελαφρύς διαχειριστής παραθύρων βασισμένος στον Blackbox +Comment[eo]=Fenestroadministrilo devenigita de Negrujo +Comment[es]=Un gestor de ventanas ligero basado en BlackBox +Comment[et]=Imeväike aknahaldur, mille aluseks on Blackbox +Comment[eu]=Blackboxen oinarritutako leiho kudeatzaile arina +Comment[fa]=یک مدیر پنجرۀ سبک بر اساس Blackbox +Comment[fi]=Kevyt, Blackboxiin pohjautuva ikkunaohjelma +Comment[fr]=Un gestionnaire de fenêtres léger fondé sur Blackbox +Comment[fy]=In lichtgewicht finstersmanager, basearre op Blackbox +Comment[gl]=Un xestor de fiestras lixeiro baseado en Blackbox +Comment[he]=מנהל חלונות קל מבוסס על Blackbox +Comment[hi]=ब्लेकबाक्स आधारित हल्का विंडो प्रबंधक +Comment[hr]=Lagani upravitelj prozora zasnovan na Blackboxu +Comment[hu]=Egy nagyon egyszerű ablakkezelő a Blackbox alapján +Comment[is]=Léttur gluggastjóri byggður á Blackbox +Comment[it]=Un window manager leggero basato su BlackBox +Comment[ja]=Blackbox ベースの軽量なウィンドウマネージャ +Comment[ka]=Blackbox-ის ბაზაზე ფანჯრიოს მენეჯერი +Comment[kk]=Blackbox-негіздеген жеңіл терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចសមត្ថភាពខ្សោយ ដែលផ្អែកលើ Blackbox +Comment[ko]=Blackbox 기반 가벼운 창 관리자 +Comment[lt]=Nedaug resursų reikalaujanti langų tvarkyklė, paremta Blackbox +Comment[lv]=Viegls logu menedžeris, bāzēts uz Blackbox +Comment[mk]=Лесен менаџер на прозорци базиран на Blackbox +Comment[ms]=Pengurus tetingkap ringan berdasarkan Kotak Hitam +Comment[mt]=Window manager ħafif ibbażat fuq BlackBox +Comment[nb]=En lettvekts vindusbehandler basert på Blackbox +Comment[nds]=En ranke Finsterpleger, opbuut op Blackbox +Comment[ne]=कालो बाकसमा आधारित हल्का वजन सञ्झ्याल प्रबन्धक +Comment[nl]=Een lichtgewicht windowmanager, gebaseerd op Blackbox +Comment[nn]=Ein lett vindaugssjef basert på Blackbox +Comment[pa]=ਬਲੈਕਬਕਸੇ ਤੇ ਆਧਾਰਿਤ ਹਲਕਾ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Menedżer okien o małych wymaganiach, oparty na Blackbox +Comment[pt]=Um gestor de janelas leve baseado no Blackbox +Comment[pt_BR]=Um gerenciador de janelas leve, baseado no Blackbox +Comment[ro]=Un manager de ferestre minimal bazat pe Blackbox +Comment[ru]=Лёгкий оконный менеджер, основанный на Blackbox +Comment[rw]=Mugenga dirishya yoroshye ishingiye ku Gasandukumukara +Comment[se]=Geahpes lásegieđahalli ráhkaduvvon Blackboxa vuođul +Comment[sk]=Nenáročný správca okien založený na Blackbox +Comment[sl]=Lahek okenski upravitelj na osnovi Blackboxa +Comment[sr]=Лагани менаџер прозора заснован на Blackbox-у +Comment[sr@Latn]=Lagani menadžer prozora zasnovan na Blackbox-u +Comment[sv]=Lättviktig fönsterhanterare baserad på Blackbox +Comment[ta]=கருப்புப் பெட்டியின் அடிப்படையிலான ஒரு குறைந்த எடை சாளர மேலாளர் +Comment[te]=బ్లాక్ బాక్స్ ఆధారిత తెలికైన విండొ అభికర్త +Comment[tg]=Мудири тирезаи сабук ба асоси қуттии сиёҳ +Comment[th]=ระบบจัดการหน้าต่างขนาดเบา สร้างมาจาก Blackbox +Comment[tr]=Blackbox temelli küçük bir pencere yöneticisi +Comment[tt]=Blackbox asılında ciñel täräzä-idäräçe +Comment[uk]=Легкий менеджер вікон, заснований на Blackbox +Comment[uz]=Blackbox asosida yaratilgan oddiy oyna boshqaruvchi +Comment[uz@cyrillic]=Blackbox асосида яратилган оддий ойна бошқарувчи +Comment[vi]=Trình quản lý cửa sổ nhỏ gọn dựa trên Blackbox +Comment[wa]=On ledjir manaedjeu di purneas båzé so Blackbox +Comment[zh_CN]=基于 BlackBox 的轻量级窗口管理器 +Comment[zh_TW]=一個基於 Blackbox 且輕量化的視窗管理程式 diff --git a/kdm/kfrontend/sessions/oroborus.desktop b/kdm/kfrontend/sessions/oroborus.desktop new file mode 100644 index 000000000..b7ea37c30 --- /dev/null +++ b/kdm/kfrontend/sessions/oroborus.desktop @@ -0,0 +1,76 @@ +[Desktop Entry] +Type=XSession +Exec=oroborus +TryExec=oroborus +Name=Oroborus +Name[eo]=Koloroj +Name[hi]=ऑरोबोरस +Name[pa]=ਓਰੂਬੋਰੁਸ +Name[te]=ఒరొబొరస్ +Comment=A lightweight themeable window manager +Comment[af]='n Lig gewig, tema venster bestuurder +Comment[ar]=مدير نوافذ خفيف العبء قابل لاستخدام السمات +Comment[be]=Лёгкі кіраўнік вокнаў з падтрымкай тэмаў +Comment[bn]=হাল্কা থীমযুক্ত উইণ্ডো ম্যানেজার +Comment[bs]=Lagani window manager sa podrškom za teme +Comment[ca]=Un gestor de finestres lleuger i configurable amb temes +Comment[cs]=Malý správce oken s tématy +Comment[csb]=Menedżer òknów ò môłëch żądanich, z mòżnotą zmianë wëzdrzatkù +Comment[cy]=Trefnydd ffenestri ysgafn sy'n defnyddio themau +Comment[da]=En letvægts vindueshåndtering med temaer +Comment[de]=Schlanker Fenstermanager mit Designs +Comment[el]=Ένας ελαφρύς διαχειριστής παραθύρων με υποστήριξη θεμάτων +Comment[eo]=Fenestroadministrilo +Comment[es]=Un gestor de ventanas ligero con temas +Comment[et]=Imeväike teemadega aknahaldur +Comment[eu]=Temak onartzen dituen leiho kudeatzaile arina +Comment[fa]=یک مدیر پنجرۀ قابل چهرهبندی سبک +Comment[fi]=Kevyt teemoitettava ikkunaohjelma +Comment[fr]=Un gestionnaire de fenêtres avec gestion des thèmes +Comment[fy]=In lichtgewicht finstersmanager mei temabehear +Comment[gl]=Un xestor de fiestras lixeiro e con capacidade para temas +Comment[he]=מנהל חלונות קל ובר התאמה אישית של ערכות נושא +Comment[hi]=हल्का, प्रसंगयोग्य विंडो प्रबंधक +Comment[hr]=Lagani upravitelj prozora s temama +Comment[hu]=Kis erőforrásigényű ablakkezelő, témázási lehetőséggel +Comment[is]=Léttur þemanlegur gluggastjóri +Comment[it]=Un window manager leggero che supporta i temi +Comment[ja]=テーマ化可能な軽量のウィンドウマネージャ +Comment[ka]=მსუბუქი ფანჯრის მენეჯერი თემების მხარდაჭერით +Comment[kk]=Жеңіл, нақыштары бар терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចអាចប្ដូរស្បែកបាន តែមានសមត្ថភាពខ្សោយ +Comment[ko]=가벼운 테마를 설정할 수 있는 창 관리자 +Comment[lt]=Nedaug resursų reikalaujanti temas palaikanti langų tvarkyklė +Comment[lv]=Viegls logu menedžeris ar tēmu atbalstu +Comment[mk]=Лесен менаџер на прозорци со теми +Comment[ms]=Pengurus tetingkap boleh tema ringan +Comment[mt]=Window manager ħafif u temabbli +Comment[nb]=En lettvekts vindusbehandler med temaer +Comment[nds]=En ranke Finsterpleger, kann Mustern bruken +Comment[ne]=विषयवस्तु योग्य हल्का वजन सञ्झ्याल प्रबन्धक +Comment[nl]=Een lichtgewicht windowmanager met themabeheer +Comment[nn]=Ein lett vindaugssjef med tema +Comment[pa]=ਇੱਕ ਸਰੂਪਾਂ ਨਾਲ ਹਲਕਾ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Menedżer okien o małych wymaganiach, z możliwością zmiany wyglądu +Comment[pt]=Um gestor de janelas leve e com suporte para temas +Comment[pt_BR]=Um gerenciador de Janelas leve com vários temas +Comment[ro]=Un manager de ferestre mic și configurabil cu diverse tematici +Comment[ru]=Лёгкий оконный менеджер, поддерживающий темы +Comment[rw]=Mugenga dirishya ngirwa-nsanganyamatsiko yoroshye +Comment[se]=Geahpes lásegieđahalli mas leat fáttát +Comment[sk]=Nenáročný správca okien s podporou tém +Comment[sl]=Lahek okenski upravitelj s temami +Comment[sr]=Лагани менаџер прозора са темама +Comment[sr@Latn]=Lagani menadžer prozora sa temama +Comment[sv]=Lättviktig fönsterhanterare med teman +Comment[ta]=குறைந்தஎடை தலைப்புடைய சாளர மேலாளர் +Comment[th]=ระบบจัดการหน้าต่างขนาดเบาที่ใช้ชุดตกแต่งได้ +Comment[tr]=Küçük, hafif, temalı bir pencere yöneticisi +Comment[tt]=Tışlana torğan ciñel täräzä-idäräçe +Comment[uk]=Легкий менеджер вікон з підтримкою тем +Comment[vi]=Trình quản lý cửa sổ thay đổi được sắc thái +Comment[wa]=On ledjir manaedjeu di purneas avou des tinmes +Comment[zh_CN]=轻量级窗口管理器,可定义主题 +Comment[zh_TW]=一個輕量化且有佈景功能的視窗管理程式 +# not usable as a session +Hidden=true diff --git a/kdm/kfrontend/sessions/phluid.desktop b/kdm/kfrontend/sessions/phluid.desktop new file mode 100644 index 000000000..f1cca1d66 --- /dev/null +++ b/kdm/kfrontend/sessions/phluid.desktop @@ -0,0 +1,80 @@ +[Desktop Entry] +Type=XSession +Exec=phluid +TryExec=phluid +Name=Phluid +Name[eo]=Plena +Name[hi]=फ्लुइड +Name[pa]=ਫਲੁਇਡ +Name[te]=ఫ్లూయిడ్ +Comment=An Imlib2 based window manager +Comment[af]='n Imlib2 gebaseerde venster bestuurder +Comment[ar]=مدير نوافذ مبني على Imlib2 +Comment[be]=Кіраўнік вокнаў, заснаваны на Imlib2 +Comment[bn]=Imlib2 ভিত্তিক উইণ্ডো ম্যানেজার +Comment[bs]=Window manager baziran na Imlib2 +Comment[ca]=Un gestor de finestres basta en Imlib2 +Comment[cs]=Správce oken založený na Imlib2 +Comment[csb]=Menedżer òknów òpiarti na Imlib2 +Comment[cy]=Trefnydd ffenestri wedi'i seilio ar lmlib2 +Comment[da]=En Imlib2 baseret vindueshåndtering +Comment[de]=Imlib2-basierter Fenstermanager +Comment[el]=Ένας διαχειριστής παραθύρων βασισμένος στην imlib2 +Comment[eo]=Fenestroadministrilo +Comment[es]=Un gestor de ventanas basado en Imlib2 +Comment[et]=Aknahaldur, mille aluseks on Imlib2 +Comment[eu]=Imlib2-n oinarritutako leiho kudeatzailea +Comment[fa]=یک Imlib2 بر اساس مدیر پنجره +Comment[fi]=Imlib2-pohjainen ikkunaohjelma +Comment[fr]=Un gestionnaire de fenêtres fondé sur Imlib2 +Comment[fy]=In op imlib2 basearre finstersmanager +Comment[ga]=Bainisteoir fuinneoga bunaithe ar Imlib2 +Comment[gl]=Un xestor de fiestras baseado en Imlib2 +Comment[he]=מנהל חלונות מבוסס Imlib2 +Comment[hi]=आईएमलिब2 आधारित विंडो प्रबंधक +Comment[hr]=Upravitelj prozora zasnovan na Imlib2 +Comment[hu]=Egy Imlib2-alapú ablakkezelő +Comment[is]=Gluggastjóri sem notar Imlib2 +Comment[it]=Un window manager basato su Imlib2 +Comment[ja]=Imlib2ベースのウィンドウマネージャ +Comment[ka]=ფანჯრის მენეჯერი imlib2 ის ბაზაზე +Comment[kk]=Imlib2-негіздеген терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចផ្អែកលើ Imlib2 +Comment[ko]=Imlib2 기반 창 관리자 +Comment[lt]=Langų tvarkyklė, paremta Imlib2 +Comment[lv]=Imlib 2 bāzēts logu menedžeris +Comment[mk]=Менаџер на прозорци базиран на Imlib2 +Comment[mn]=Imlib2 дээр суурилсан цонхны удирдагч +Comment[ms]=Pengurus tetingkap berasaskan lmlib2 +Comment[mt]=Window manager ibbażat fuq Imlib2 +Comment[nb]=En vindusbehandler basert på Imlib2 +Comment[nds]=En Finsterpleger opbuut op Imlib2 +Comment[ne]=Imlib2 मा आधारित सञ्झ्याल प्रबन्धक +Comment[nl]=Een op imlib2 gebaseerde windowmanager +Comment[nn]=Ein vindaugssjef basert på Imlib2 +Comment[pa]=ਇੱਕ Imlib2 ਆਧਾਰਿਤ ਫਾਇਲ਼ ਮੈਨੇਜਰ +Comment[pl]=Menedżer okien oparty na Imlib2 +Comment[pt]=Um gestor de janelas baseado na Imlib2 +Comment[pt_BR]=Um gerenciador de janelas baseado na lmlib2 +Comment[ro]=Un manager de ferestre bazat pe Imlib2 +Comment[ru]=Оконный менеджер на основе imlib2 +Comment[rw]=Mugenga dirishya ishingiye kuri Imlib2 +Comment[se]=Imlib2-vuođđoduvvon lásegieđahalli +Comment[sk]=Správca okien založený na imlib2 +Comment[sl]=Okenski upravitelj na osnovi lmlib2 +Comment[sr]=Менаџер прозора заснован на Imlib2 +Comment[sr@Latn]=Menadžer prozora zasnovan na Imlib2 +Comment[sv]=Imlib2-baserad fönsterhanterare +Comment[ta]=சாளர மேலாளர் அடிப்படையிலான ஒரு Imlib2 +Comment[te]=ఐఎంలిబ్2 ఆధారిత విండొ అభికర్త +Comment[tg]=Imlib2 ба асосимудири тиреза +Comment[th]=ระบบจัดการหน้าต่างที่สร้างบน Imlib2 +Comment[tr]=Imlib2 tabanlı bir pencere yöneticisi +Comment[tt]=Imlib2 asılında täräzä-idäräçe +Comment[uk]=Менеджер вікон, заснований на Imlib2 +Comment[uz]=Imlib2 asosida yaratilgan oyna boshqaruvchi +Comment[uz@cyrillic]=Imlib2 асосида яратилган ойна бошқарувчи +Comment[vi]=Trình quản lý cửa sổ dựa trên lmlib2 +Comment[wa]=On manaedjeu di purneas båzé so lmlib2 +Comment[zh_CN]=基于 Imlib2 的窗口管理器 +Comment[zh_TW]=一個基於 Imlib2 的視窗管理程式 diff --git a/kdm/kfrontend/sessions/pwm.desktop b/kdm/kfrontend/sessions/pwm.desktop new file mode 100644 index 000000000..036c9bff4 --- /dev/null +++ b/kdm/kfrontend/sessions/pwm.desktop @@ -0,0 +1,72 @@ +[Desktop Entry] +Type=XSession +Exec=pwm +TryExec=pwm +Name=PWM +Name[eo]=UnuFA +Name[hi]=पीडबल्यूएम +Name[te]=పి డబ్ల్యు ఎం +Name[th]=ตัวจัดการหน้าต่าง PWM +Comment=A lightweight window manager able to attach multiple windows to one frame +Comment[af]='n Lig gewig venster bestuurder wat veelvuldige vensters aan een raam kan koppel +Comment[ar]=مدير نوافذ خفيف العبء قابل لربط عدة نوافذ إلى إطار واحد +Comment[be]=Лёгкі кіраўнік вокнаў, які можа далучаць некалькі вокнаў да аднаго фрэйма +Comment[bn]=একটি হাল্কা উইণ্ডো ম্যানেজার, যাতে একটি ফ্রেমে একাধিক উইণ্ডো সংযুক্ত করা সম্ভব +Comment[bs]=Lagani window manager koji može prikačiti više prozora na jedan okvir +Comment[ca]=Un lleuger gestor de finestres capaç d'aplegar múltiples finestres en un marc +Comment[csb]=Menedżer òknów ò môłëch żądaniach, rozmiejący doczepic wiele òknów do jedny ramë +Comment[cy]=Trefnydd ffenestri ysgafn sy'n gallu atodi mwy nag un ffenestr at un ffrâm +Comment[da]=En letvægts vindueshåndtering der kan knytte flere vinduer til én ramme +Comment[de]=Schlanker Fenstermanager, der mehrere Fenster an einen Rahmen andocken kann +Comment[el]=Ένας ελαφρύς διαχειριστής παραθύρων με δυνατότητα να προσαρτά πολλαπλά παράθυρα σε ένα πλαίσιο +Comment[eo]=Fenestroadministrilo, kiu faras unun fenestron el kelkaj +Comment[es]=Un gestor de ventanas ligero capaz de conectar varias ventanas a un mismo marco +Comment[et]=Imeväike aknahaldur, mis suudab mitu akent ühe raami külge haakida +Comment[eu]=Hainbat leiho marko bakarrean uztar ditzakeen leiho kudeatzaile arina +Comment[fa]=مدیر پنجرۀ سبک قادر به پیوست پنجرههای چندگانه در یک قابک +Comment[fi]=Kevyt ikkunaohjelma, joka osaa liittää useita ikkunoita yhteen kehykseen +Comment[fr]=Un gestionnaire de fenêtres léger capable d'attacher plusieurs fenêtres à un même cadre +Comment[fy]=In lichtgewicht finstersmanager, hokker meardere finsters kin ferbine mei in kader +Comment[gl]=Un xestor de fiestras lixeiro capaz de adxuntar varias fiestras nun marco +Comment[he]=מנהל חלונות קל המסוגל לחבר חלונות רבים למסגרת אחת +Comment[hi]=एक हल्का विंडो प्रबंधक जिसके एक फ़रमा में अनेक विंडो जोड़े जा सकते हैं +Comment[hr]=Lagani upravitelj prozora koji jednom okviru može pridodati više prozora +Comment[hu]=Alacsony erőforrásigényű ablakkezelő, több ablakot képes egy kerethez rendelni +Comment[is]=Léttur gluggastjóri sem getur tengt marga glugga við einn ramma +Comment[it]=Un window manager leggero in grado di attaccare più finestre ad una cornice +Comment[ja]=複数のウィンドウ枠を設定可能な軽量なウィンドウマネージャ +Comment[ka]=მსუბუქი ფანჯრის მენეჯერი, რომელსაც შეუძლია ბევრი ფანჯრის ერთ ჩარჩოში ჩასმა +Comment[kk]=Жеңіл, бір коршауда бірнеше терезелерді біріктірелетін, терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចសមត្ថភាពខ្សោយ អាចភ្ជាប់បង្អួចច្រើនទៅស៊ុមមួយ +Comment[lt]=Nedaug resursų reikalaujanti langų tvarkyklė, galinti prijungti daug langų prie vieno rėmo +Comment[lv]=Viegls logu menedžeris ar iespēju pievienot vairākus logus vienam kadram +Comment[mk]=Лесен менаџер на прозорци кој може да прикачи повеќе прозорци на една рамка +Comment[ms]=Pengurus tetingkap ringan yang boleh melekapkan berbilang tetingkap pada satu bingkai +Comment[mt]=Window manager ħafif li jista' jgħaqqad iżjed minn window waħda fl-istess gwarniċ +Comment[nb]=En lettvekts vindusbehandler som kan koble flere vinduer til én ramme +Comment[nds]=En ranke Finsterpleger, de mennige Finstern an een Rahmen andocken kann +Comment[ne]=एक हल्का वजन सञ्झ्याल प्रबन्धकले बहुभागिय सञ्झ्यालहरूलाई एक फ्रेममा सङ्लग्न गर्न सक्छ +Comment[nl]=Een lichtgewicht windowmanager, welke meerdere vensters kan verbinden met een frame +Comment[nn]=Ein lett vindaugssjef som kan kopla fleire vindauge til den same ramma +Comment[pa]=ਇੱਕ ਹਲਕਾ ਝਰੋਖਾ ਮੈਨੇਜਰ, ਜੋ ਕਿ ਕਈ ਝਰੋਖਿਆਂ ਨੂੰ ਇੱਕ ਫਰੇਮ ਵਿੱਚ ਰੱਖ ਸਕਦਾ ਹੈ +Comment[pl]=Menedżer okien o małych wymaganiach, potrafiący doczepić wiele okien do jednego obramowania +Comment[pt]=Um gestor de janelas leve, com a possibilidade de anexar várias janelas a uma área +Comment[pt_BR]=Um gerenciador de janelas leve, capaz de anexar múltiplas janelas em um quadro +Comment[ro]=Un manager de ferestre minimal capabil să atașeze multe ferestre la un singur cadru +Comment[ru]=Лёгкий оконный менеджер, способный объединить несколько окон в одной рамке +Comment[rw]=Mugenga dirishya yoroshye ishobora gufatishya amadirishya menshi ku ikadiri imwe. +Comment[se]=Geahpes lásegieđahalli mii sáhttá ovttastahttit máŋga láse seamma rámmii. +Comment[sk]=Nenáročný správca okien schopný spojiť viac okien do jednoho rámca +Comment[sl]=Lahek okenski upravitelj, ki lahko pripne več oken na en okvir +Comment[sr]=Лагани менаџер прозора способан да прикачи више прозора за један оквир +Comment[sr@Latn]=Lagani menadžer prozora sposoban da prikači više prozora za jedan okvir +Comment[sv]=Lättviktig fönsterhanterare som kan ansluta flera fönster till en ram +Comment[ta]=குறைந்த கனமுள்ள பல சாளரங்களை இணைக்க முடிந்த ஒற்றை சாளர மேலாளர் +Comment[th]=ระบบจัดการหน้าต่างขนาดเบา มีความสามารถในการปะติดหลายๆหน้าต่างลงใน 1 กรอบ +Comment[tr]=Düşük ağırlıklı bir çok pencereyi bir çerçeveye toplayabilen bir pencere yöneticisi +Comment[tt]=Ber qısa eçendä berniçä täräzä totaştırala torğan täräzä-idäräçe +Comment[uk]=Простий менеджер вікон, що дозволяє долучати декілька вікон до однієї рамки +Comment[vi]=Trình quản lý cửa sổ nhỏ gọn, có thể gắn nhiều cửa sổ vào một khung +Comment[wa]=On ledjir manaedjeu di purneas ki pout ataetchî sacwants purneas so-z on cåde +Comment[zh_CN]=轻量级窗口管理器,可将多个窗口附加到一个框架中 +Comment[zh_TW]=一個輕量化且可以將多個視窗結合在一起的視窗管理程式 diff --git a/kdm/kfrontend/sessions/qvwm.desktop b/kdm/kfrontend/sessions/qvwm.desktop new file mode 100644 index 000000000..922e3e0bd --- /dev/null +++ b/kdm/kfrontend/sessions/qvwm.desktop @@ -0,0 +1,80 @@ +[Desktop Entry] +Type=XSession +Exec=qvwm +TryExec=qvwm +Name=QVWM +Name[eo]=QVFA +Name[hi]=क्यूवीडबल्यूएम +Name[te]=క్యు వి డబ్ల్యు ఎం +Name[th]=ตัวจัดการหน้าต่าง QVWM +Comment=A Windows 95 like window manager +Comment[af]='n Venster bestuurder wat soos Windows 95 lyk +Comment[ar]=مدير نوافذ شبيه بويندوز 95 +Comment[be]=Кіраўнік вокнаў, падобны на Windows 95 +Comment[bn]=Windows 95-এর অনুরূপ একটি উইণ্ডো ম্যানেজার +Comment[bs]=Window manager nalik na Windows 95 +Comment[ca]=Un gestor de finestres com el Windows 95 +Comment[cs]=Správce oken se vzhledem Windows 95 +Comment[csb]=Menedżer òknów o wëzdrzatkù szlachùjącym za Windows 95 +Comment[cy]=Trefnydd Ffenestri sy'n debyg i Windows95 +Comment[da]=En Windows 95-lignende vindueshåndtering +Comment[de]=Fenstermanager im Stil von Windows 95 +Comment[el]=Ένας διαχειριστής παραθύρων παρόμοιος με τα Windows 95 +Comment[eo]=Fenestroadministrilo kiel Vindozo 95 +Comment[es]=Un gestor de ventanas similar a Windows 95 +Comment[et]=Aknahaldur, mis näeb välja nagu Windows 95 +Comment[eu]=Windows 95en itxura duen leiho kudeatzailea +Comment[fa]=یک مدیر پنجره شبیه ویندوز ۹۵ +Comment[fi]=Windows 95:n tyylinen ikkunaohjelma +Comment[fr]=Un gestionnaire de fenêtres semblable à Windows 95 +Comment[fy]=In Win95-likens finstersmanager +Comment[ga]=Bainisteoir fuinneoga cosúil le Windows 95 +Comment[gl]=Un xestor de fiestras como o de Windows 95 +Comment[he]=מנהל חלונות הדומה לחלונות 95 +Comment[hi]=विंडोज़ 95 जैसा विंडो प्रबंधक +Comment[hr]=Upravitelj prozora nalik na Windows 95 +Comment[hu]=Egy Windows 95-szerű ablakkezelő +Comment[is]=Gluggastjóri sem líkist Windows 95 +Comment[it]=Un window manager in stile Windows 95 +Comment[ja]=Windows95 風のウィンドウマネージャ +Comment[ka]=ფანჯრის მენეჯერი Windows 95-ს სტილში +Comment[kk]=Windows 95 секілді терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចស្រដៀង Windows 95 +Comment[ko]=윈도 95를 닮은 창 관리자 +Comment[lt]=Langų tvarkyklė, primenanti Windows 95 +Comment[lv]=Windows 95 līdzīgs logu menedžeris +Comment[mk]=Менаџер на прозорци со изглед на Windows 95 +Comment[mn]=Виндовс 95 шиг цонхны удирдагч +Comment[ms]=Pengurus tetingkap seperti Windows 95 +Comment[mt]=Window manager jixbaħ lil Windows95 +Comment[nb]=En vindusbehandler som likner Windows 95 +Comment[nds]=En Finsterpleger liek to Windows 95 +Comment[ne]=विण्डोज ९५ जस्तो सञ्झ्याल प्रबन्धक +Comment[nl]=Een Win95-achtige windowmanager +Comment[nn]=Ein vindaugssjef som liknar Windows 95 +Comment[pa]=ਇੱਕ ਵਿੰਡੋ 95 ਵਰਗਾ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Menedżer okien o wyglądzie podobnym do Windows 95 +Comment[pt]=Um gestor de janelas com o visual do Windows 95 +Comment[pt_BR]=Um gerenciador de janelas parecido com o Windows 95 +Comment[ro]=Un manager de ferestre cu aspect de Windows 95 +Comment[ru]=Оконный менеджер в стиле Windows 95 +Comment[rw]=Windows 95 nka mugenga dirishya +Comment[se]=Windows95-lágan lásegieđahalli +Comment[sk]=Správca okien podobný Windows 95 +Comment[sl]=Okenski upravitelj, podoben Windows 95 +Comment[sr]=Менаџер прозора налик на Windows 95 +Comment[sr@Latn]=Menadžer prozora nalik na Windows 95 +Comment[sv]=Fönsterhanterare som liknar Windows 95 +Comment[ta]=சாளர மேலாளர் போன்ற விண்டோஸ் 95 +Comment[te]=విండొస్ 95 లాంటి విండొ అభికర్త +Comment[tg]=Windows 95 монанди мудири тиреза +Comment[th]=ระบบจัดการหน้าต่างที่ดูคล้ายวินโดวส์ 95 +Comment[tr]=Windows 95 benzeri bir pencere yöneticisi +Comment[tt]=Windows 95 kebek täräzä-idäräçe +Comment[uk]=Менеджер вікон на кшталт Windows95 +Comment[uz]=Win95'ga oʻxshagan oyna boshqaruvchi +Comment[uz@cyrillic]=Win95'га ўхшаган ойна бошқарувчи +Comment[vi]=Trình quản lý cửa sổ giống Windows 95 +Comment[wa]=On manaedjeu di purneas rishonnant a Windows 95 +Comment[zh_CN]=类似 Windows 95 的窗口管理器 +Comment[zh_TW]=一個像 Win95 的視窗管理程式 diff --git a/kdm/kfrontend/sessions/ratpoison.desktop b/kdm/kfrontend/sessions/ratpoison.desktop new file mode 100644 index 000000000..1127db151 --- /dev/null +++ b/kdm/kfrontend/sessions/ratpoison.desktop @@ -0,0 +1,82 @@ +[Desktop Entry] +Type=XSession +Exec=ratpoison +TryExec=ratpoison +Name=Ratpoison +Name[bn]=র্যাট-পয়সন +Name[cy]=Gwenwynllygodmawr (Ratpoison) +Name[eo]=Raticido +Name[fa]=مرگ موش +Name[hi]=रैट-पॉइज़न +Name[ne]=र्याटपोइजन +Name[pa]=ਰਾਟਪੋਈਸਾਨ +Name[rw]=UburoziImbeba +Name[sv]=Råttgift +Name[ta]=ராட் பாய்சன் +Name[te]=ఎలుకలమందు +Name[vi]=Bả chuột +Name[wa]=Pwezon po les rats (Ratpoison) +Comment=A simple keyboard-only window manager modeled after Screen +Comment[af]='n Eenvoudige venster bestuurder wat net met die sleutel bord werk en op Screen gemoduleer is. +Comment[ar]=مدير نوافذ بسيط يستخدم لوحة المفاتيح فقط صنع مشابهاً لـScreen +Comment[be]=Просты кіраўнік вокнаў, працуе толькі з клавіятурай +Comment[bn]=একটি সরল কীবোর্ড-ভিত্তিক উইণ্ডো ম্যানেজার, Screen-এর আদর্শে তৈরি +Comment[bs]=Jednostavan windows manager samo za tastaturu, modeliran po uzoru na Screen +Comment[ca]=Un gestor de finestres simple de sols teclat modelat després de Screen +Comment[csb]=Prosti menedżer òknów òbsłùgiwôny blós z klawiaturë, szlachùje za programą screen +Comment[cy]=Trefnydd ffenestri syml sy'n defnyddio'r allweddell yn unig, wedi'i arddullio ar Sgrîn (Screen) +Comment[da]=En simpel kun-tastatur vindueshåndtering modeleret efter Screen +Comment[de]=Einfacher Fenstermanager, der nur über die Tastatur bedient wird und Screen nachgebildet ist +Comment[el]=Ένας απλός, μονό για πληκτρολόγιο, διαχειριστής παραθύρων σχεδιασμένος με βάση το Screen +Comment[en_GB]=A simple keyboard-only window manager modelled after Screen +Comment[eo]=Fenestroadministrilo por klaviaro +Comment[es]=Un gestor de ventanas sólo para teclado realizado a partir de Screen +Comment[et]=Lihtne ainult klaviatuuri abil kasutatav aknahaldur, mille eeskujuks on Screen +Comment[eu]=Screen-en oinarriturik egindako leiho kudeatzailea, teklatu hutsez erabiltzekoa +Comment[fa]=یک مدیر پنجره فقط صفحه کلید سادۀ مدلیافته پس از پرده +Comment[fi]=Yksinkertainen, vain näppäimistöltä käytettävä ikkunamanageri, screen-ohjelman tyyliin +Comment[fr]=Un gestionnaire de fenêtres simple uniquement dirigeable au clavier et fondé sur Screen +Comment[fy]=In ienfâldige finstersmanager dy allinnich mei it toetseboerd te betsjinnen is, ynspiraasje troch Screen +Comment[gl]=Un xestor de fiestras de manexo co teclado modelado despois de Screen +Comment[he]=מנהל חלונות פשוט למקלדת בלבד המעוצב בסגנון Screen +Comment[hi]=आफ्टर स्क्रीन आधारित साधारण विंडो प्रबंधक जो सिर्फ कुंजीपट के लिए है +Comment[hr]=Jednostavan, samo za tipkovnicu, upravitelj prozora napravljen prema Screenu +Comment[hu]=Egyszerű, csak billentyűzetről vezérelhető ablakkezelő (a Screen alapján) +Comment[is]=Einfaldur gluggastjóri sem notar eingöngu lyklaborðið hannaður eftir Screen forritinu +Comment[it]=Un window manager semplice solo-tastiera pensato come Screen +Comment[ja]=Screen をもとに作られた、キーボードインターフェースのみのウィンドウマネージャ +Comment[ka]=მარტივი კლაიატურით მართვადი ფანჯრის მმართველი +Comment[kk]=Screen үлгідегі, тек перенетақтадан басқарылатын, қарапайым терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចសាមញ្ញប្រើតែក្ដារចុច ដែលយកគំរូតាមអេក្រង់ +Comment[lt]=Paprasta, tik klaviatūra valdoma langų tvarkyklė, panaši į Screen +Comment[lv]=Vienkāršs tikai tastatūras logu menedžeris, līdzīgs Screen +Comment[mk]=Едноставен менаџер на прозорци кој работи само со тастатура, моделиран според Screen +Comment[ms]=Pengurus tetingkap hanya papan kekunci ringkas dimodelkan seperti Skrin +Comment[mt]=Window manager għal tastieri biss immudellat fuq Screen +Comment[nb]=En enkel tastaturbasert vindusbehandler, etter forbilde av Screen +Comment[nds]=En eenfache Finsterpleger, de bloots mit de Tastatuur bruukt warrt. Screen weer dat Modell dorför +Comment[ne]=पर्दा पछाडि सञ्झ्याल प्रबन्धक मात्र नमूना बनाउने साधारण कुञ्जीपाटी +Comment[nl]=Een eenvoudige windowmanager die alleen met het toetsenbord te bedienen is, geïnspireerd door Screen +Comment[nn]=Ein enkel tastaturbasert vindaugssjef, med Screen som førebilete +Comment[pa]=ਪਰਦੇ ਮੈਡੀਊਲ ਬਾਅਦ ਸਿਰਫ ਕੀ-ਬੋਰਡ ਤੇ ਆਧਾਰਿਤ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Prosty menedżer okien obsługiwany wyłącznie za pomocą klawiatury, stworzony na podobieństwo programu screen +Comment[pt]=Um gestor de janelas simples, só para o teclado e modelado sobre o Screen +Comment[pt_BR]=Um simples gerenciador de janelas baseado somente em teclado, modelado após a tela +Comment[ro]=Un manager de ferestre manipulat din tastatură, modelat după Screen +Comment[ru]=Простой клавиатурный оконный менеджер, моделирующий Screen +Comment[rw]=Mugenga dirishya ya mwandikisho-gusa yoroheje itunganyijwe nyuma ya Mugaragaza +Comment[se]=Oktageardánis, boallobeavdestivrejuvvon lásegieđahalli, mas lea Screen ovdagovvan +Comment[sk]=Jednoduchý správca okien ovládaný iba klávesami podľa programu Screen +Comment[sl]=Preprost okenski uporavljalnik na osnovi Screen, upravlja se samo s tipkovnico +Comment[sr]=Једноставан, само за тастатуру менаџер прозора, направљен према Screen-у +Comment[sr@Latn]=Jednostavan, samo za tastaturu menadžer prozora, napravljen prema Screen-u +Comment[sv]=Enkel fönsterhanterare bara för tangentbord modellerad efter Screen +Comment[ta]=சாளரமொன்றில் நிழல் ஏறுகிறது +Comment[th]=ตัวจัดการหน้าต่างแบบเรียบง่าย ใช้แป้นพิมพ์ควบคุมได้อย่างเดียว ถูกสร้างขึ้นมาตามหลัง Screen +Comment[tr]=Screen'den sonra modellenmiş salt klavye basit pencere yöneticisi +Comment[tt]=Töylekle genä ciñel täräzä-idäräçe +Comment[uk]=Простий менеджер вікон, розроблений на базі Screen, з підтримкою тільки клавіатури +Comment[vi]=Trình quản lý cửa sổ chỉ dùng bàn phím, dựa theo Screen +Comment[wa]=On simpe manaedjeu di purneas eployant fok li taprece, båzé so ls idêyes da «Screen» +Comment[zh_CN]=Screen 后又一只支持键盘的窗口管理器 +Comment[zh_TW]=一個簡易、只支援鍵盤的視窗管理程式 diff --git a/kdm/kfrontend/sessions/sapphire.desktop b/kdm/kfrontend/sessions/sapphire.desktop new file mode 100644 index 000000000..5cdd998d7 --- /dev/null +++ b/kdm/kfrontend/sessions/sapphire.desktop @@ -0,0 +1,84 @@ +[Desktop Entry] +Type=XSession +Exec=sapphire +TryExec=sapphire +Name=Sapphire +Name[bn]=স্যাফায়ার +Name[eo]=Safiro +Name[fa]=ياقوت كبود +Name[hi]=सेफायर +Name[ka]=საფირონი +Name[ne]=नीलमणि +Name[pa]=ਸਾਪਰਫੀਰੀ +Name[ru]=Сапфир +Name[ta]=சபையர் +Name[te]=ఇంద్రనీలం +Name[tg]=Ёқути кабуд +Comment=A minimal but configurable window manager +Comment[af]='n Minimalistiese venster bestuurder, wat nogsteeds opstel funksionaliteit bevat. +Comment[ar]=مدير نوافذ مصغّر ولكن قابل للإعداد +Comment[be]=Мінімалістычны кіраўнік вокнаў з магчымасцю настаўлення +Comment[bn]=পরিমিত কিন্তু থীমযুক্ত উইণ্ডো ম্যানেজার +Comment[bs]=Minimalan ali podesiv window manager +Comment[ca]=Un minimalista però configurable gestor de finestres +Comment[cs]=Minimalistický, ale přizpůsobitelný správce oken +Comment[csb]=Prosti menedżer òknów, równak z mòżnotą kònfigùracëji +Comment[cy]=Trefnydd ffenestri lleiafol a ffurfweddir +Comment[da]=En minimal men indstillelig vindueshåndtering +Comment[de]=Minimalistischer, aber anpassbarer Fenstermanager +Comment[el]=Ένας μικρός αλλά παραμετροποιήσιμος διαχειριστής παραθύρων +Comment[eo]=Fenestroadministrilo +Comment[es]=Un gestor de ventanas minimalista pero configurable +Comment[et]=Väga väike, kuid seadistatav aknahaldur +Comment[eu]=Leiho kudeatzaile minimal baina konfiguragarria +Comment[fa]=یک مدیر پنجرۀ کمینه، اما قابل پیکربندی +Comment[fi]=Minimaalinen, mutta muokattavissa oleva ikkunaohjelma +Comment[fr]=Un gestionnaire de fenêtres minimaliste mais configurable +Comment[fy]=In minimale mar ynstelbere finstersmanager +Comment[gl]=Un xestor de fiestras mínimo pero configurábel +Comment[he]=מנהל חלונות מינימלי אך ניתן להגדרה +Comment[hi]=एक अल्पतम परंतु कॉन्फ़िगर योग्य विंडो प्रबंधक +Comment[hr]=Minimalan, ali podesiv upravitelj prozora +Comment[hu]=Egyszerű, de jól konfigurálható ablakkezelő +Comment[is]=Einfaldur en stillanlegur gluggastjóri +Comment[it]=Un window manager minimale ma configurabile +Comment[ja]=各種設定が可能な小さなウィンドウマネージャ +Comment[ka]=მინიმალური მაგრამ კონფიგურირებადი ფანჯრის მენეჯერი +Comment[kk]=Шағын, баптауы бар, терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចតូច តែអាចកំណត់រចនាសម្ព័ន្ធបាន +Comment[ko]=설정 가능한 최소한의 창 관리자 +Comment[lt]=Minimalistinė tačiau konfigūruojama langų tvarkyklė +Comment[lv]=Minimālistisks, bet konfigurējams logu menedžeris +Comment[mk]=Минимален но конфигурабилен менаџер на прозорци +Comment[mn]=Маш жижиг тохируулах боломжгүй цонх удирдагч +Comment[ms]=Pengurus tetingkap minimum tetapi boleh konfigur +Comment[mt]=Window manager minimu imma konfigurabbli +Comment[nb]=En minimal vindusbehandler, men med innstillinger +Comment[nds]=En minimaal, man instellbor Finsterpleger +Comment[ne]=सानो तर कन्फिगर गर्न सकिने सञ्झ्याल प्रबन्धक +Comment[nl]=Een minimale maar instelbare windowmanager +Comment[nn]=Ein minimal vindaugssjef, men med innstillingar +Comment[pa]=ਇੱਕ ਨਿਊਨਤਮ, ਪਰ ਸੋਧਯੋਗ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Prosty menedżer okien, ale z możliwością konfiguracji +Comment[pt]=Um gestor de janelas configurável mas mínimo +Comment[pt_BR]=Um gerenciador de janelas mínimo, mas configurável +Comment[ro]=Un manager de ferestre minimal, dar configurabil +Comment[ru]=Минимальный, но настраиваемый оконный менеджер +Comment[rw]=Ntoya ariko mugenga dirishya ibonezwa +Comment[se]=Unna, muhto heivehahtti lásegieđahalli +Comment[sk]=Minimálny, ale nastaviteľný správca okien +Comment[sl]=Skromen, a nastavljiv okenski upravitelj +Comment[sr]=Минимални, али подесиви менаџер прозора +Comment[sr@Latn]=Minimalni, ali podesivi menadžer prozora +Comment[sv]=Minimal men anpassningsbar fönsterhanterare +Comment[ta]=சாளர மேலாளரின் மேம்பட்ட திறன்களை வடிவமைக்கலாம் +Comment[th]=ระบบจัดการหน้าต่างขนาดเล็ก แต่สามารถปรับแต่งได้ +Comment[tr]=Küçük, ancak kolayca özelleştirilebilir bir pencere yöneticisi +Comment[tt]=Ciñel bulsa da, köylänä torğan täräzä-idäräçe +Comment[uk]=Мінімальний менеджер вікон з можливістю налаштування +Comment[uz]=Oddiy, ammo moslab boʻladigan oyna boshqaruvchi +Comment[uz@cyrillic]=Оддий, аммо мослаб бўладиган ойна бошқарувчи +Comment[vi]=Trình quản lý cửa sổ tối thiểu, nhưng có thể cấu hình được +Comment[wa]=On ptit, nén apontiåve, manaedjeu di purnea +Comment[zh_CN]=很小却可配置的窗口管理器 +Comment[zh_TW]=一個小但可組態的視窗管理程式 diff --git a/kdm/kfrontend/sessions/sawfish.desktop b/kdm/kfrontend/sessions/sawfish.desktop new file mode 100644 index 000000000..668620fff --- /dev/null +++ b/kdm/kfrontend/sessions/sawfish.desktop @@ -0,0 +1,74 @@ +[Desktop Entry] +Type=XSession +Exec=sawfish +TryExec=sawfish +Name=Sawfish +Name[bn]=স-ফিশ +Name[eo]=Segfiŝo +Name[fa]=ارهماهی +Name[hi]=सा-फिश +Name[ne]=सफीस +Name[pa]=ਸ਼ਾਅਫਿਸ਼ +Name[te]=సాఫిష్ +Comment=An extensible window manager scriptable with an Emacs Lisp-like language +Comment[af]='n Uitbreibare venster bestuurder met 'n ingeboude skrip taal wat soos Emacs List lyk. +Comment[ar]=مدير نوافذ قابل للتوسعة يمكن بشفيره بلغة إيماكس ليسب +Comment[be]=Кіраўнік вокнаў з магчымасцю пашырэння сцэнарамі на мове, падобнай на Emacs Lisp +Comment[bs]=Proširiv window manager sa podškom za skriptiranje u jeziku sličnom Emacs Lisp-u +Comment[ca]=Un extensible gestor de finestres mitjançant scripts amb una aparença com el Lisp de Emacs +Comment[cs]=Rožšiřitelný správce oken skriptovatelný jazykem podobným jazyku Emacs Lisp +Comment[csb]=Menedżer òknów, jaczi je mòżno rozbùdowac dzãka skriptom w jãzëkù szlachùjącym za Emacs Lisp +Comment[cy]=Trefnydd ffenestri estynadwy a all ei sgriptio efo iaith sy'n debyg i Emacs Lisp +Comment[da]=En udvidelig vindueshåndtering der kan scriptes med et Emacs Lisp-lignende sprog +Comment[de]=Erweiterbarer Fenstermanager, der über Skripts ähnlich Emacs-Lisp gesteuert werden kann +Comment[el]=Ένας επεκτάσιμος διαχειριστής παραθύρων παραμετροποιήσιμος με μια γλώσσα παρόμοια με την Emacs Lisp +Comment[eo]=Fenestroadministrilo, kiu uzas Lispon por esti programata +Comment[es]=Un gestor de ventanas extensible con guiones escritos en un lenguaje similar a Lisp de Emacs +Comment[et]=Laiendatav aknahaldur, mis kasutab Emacs Lispi keele moodi skripte +Comment[eu]=Emacs Lisp bezalako hizkuntza batez idatziriko scripten bidez heda daitekeen leiho kudeatzailea +Comment[fa]=مدیر پنجرۀ توسعهپذیر دستنوشتهای با یک زبان شبیه Emacs Lisp +Comment[fi]=Laajennettavissa oleva ikkunaohjelma, johon voi luoda komentosarjoja Emacs lispin tyylisellä kielellä +Comment[fr]=Un gestionnaire de fenêtres extensible, à l'aide scripts dans un langage semblable au Lisp d'Emacs +Comment[fy]=In útbreidbere finstersmanager, skriptber fia in Emacs Lisp-likene taal +Comment[gl]=Un xestor de fiestras extensíbel e configurábel con scripts en linguaxe Emacs Lisp +Comment[he]=מנהל חלונות מקיף הניתן לתכנות עם שפה דמוית Emacs Lisp +Comment[hi]=ई-मेक्स लिस्प जैसे भाषा में स्क्रिप्ट किया जा सकने लायक विंडो प्रबंधक जिसे विस्तार दिया जा सकता है +Comment[hr]=Proširivi upravitelj prozora pisan u skripti nalik na jezik Emacs Lisp +Comment[hu]=Egy könnyen tovább bővíthető ablakkezelő, egy Emacs Lisp-szerű nyelvvel szkriptelhető +Comment[is]=Viðbætanlegur gluggastjóri sem er skriftanlegur á Emacs Lisp líku máli +Comment[it]=Un window manager estensibile per cui è possibile fare script in un linguaggio simile all'Emacs lisp +Comment[ja]=Emacs Lisp 言語スクリプトで機能拡張可能なウィンドウマネージャ +Comment[ka]=Lisp სკრიპტებით გაფათოვებადი Emacs ის მაგვარი ფანჯრის მენეჯერი +Comment[kk]=Emacs Lisp-секілді тілдегі скриптті қолданып, кеңейтілетін терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចដែលអាចពង្រីកបាន ហើយអាចសរសេរស្គ្រីបជាមួយភាសាដែលដូច Emacs Lisp +Comment[lt]=Langų tvarkyklė, kurią galima išplėsti Emacs Lisp primenančia programavimo kalba +Comment[lv]=Paplašināms logu menedžeris ar Emacs Lisp līdzīgas valodas atbalstu +Comment[mk]=Екстензивен менаџер на прозорци кој може да се скриптира со јазик како Emacs Lisp +Comment[ms]=Pengurus tetingkap boleh kembang dan boleh diskrip dengan bahasa seperti Emacs Lisp +Comment[mt]=Window manager li jista' jiġi estiż, b'lingwa tixbaħ lil Emacs Lisp +Comment[nb]=En utvidbar vindusbehandler som kan skriptes med et språk som likner Emacs Lisp. +Comment[nds]=En verwiederbor Finsterpleger, de över en Skriptspraak liek to Emacs-Lisp stüert warrn kann +Comment[ne]=इमाक्स लिस्प जस्तो भाषसँग स्क्रिप्ट गर्न सकिने एउटा विस्तारयोग्य सञ्झ्याल प्रबन्धक +Comment[nl]=Een uitbreidbare windowmanager, scriptbaar via een Emacs Lisp-achtige taal +Comment[nn]=Ein utvidbar vindaugssjef som kan skriptast med eit språk som liknar Emacs Lisp +Comment[pa]= Emacs Lisp-ਵਰਗੀ ਭਾਸ਼ਾ ਸਕ੍ਰਿਪਟਯੋਗ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Menedżer okien, który można rozszerzać za pomocą skrytów w języku podobnym do Emacs Lisp +Comment[pt]=Um gestor de janelas extensível e programável com uma linguagem semelhante ao Emacs Lisp +Comment[pt_BR]=um gerenciador de janelas extensível, baseado em scripts, com uma linguagem parecida com o Lisp do Emacs +Comment[ro]=Un manager de ferestre extensibil scriptabil cu un limbaj similar cu Emacs Lisp +Comment[ru]=Расширяемый скриптами Lisp наподобие Emacs оконный менеджер +Comment[rw]=Mugenga dirishya yagurwa yandikwaho hakoreshejwe Emacs Kudedemanga-nka ururimi +Comment[sk]=Rozšíriteľný správca okien, ktorého je možné ovládať programovacím jazykom podobným Emacs Lispu +Comment[sl]=Razširljiv okenski upravitelj, ki se lahko upravlja s skripti v jeziku podobnem Emacs Lisp +Comment[sr]=Проширив менаџер прозора који се може скриптовати помоћу језика налик на Emacs-ов Lisp +Comment[sr@Latn]=Proširiv menadžer prozora koji se može skriptovati pomoću jezika nalik na Emacs-ov Lisp +Comment[sv]=Utökningsbar fönsterhanterare som kan styras med ett Emacs Lisp-liknande skriptspråk +Comment[ta]=Emacs Lisp-like மொழியுடனான விரிவாக்ககூடிய சாளர மேலாளர் எழுத்தாக்கம் +Comment[th]=ระบบจัดการหน้าต่างที่สามารถเพิ่มขยายได้ และควบคุมด้วยการเขียนสคริปต์โดยใช้ภาษาแบบ Emacs Lisp +Comment[tr]=Eklenti destekli Emacs Lisp benzeri bir kodlama dili kullanan kodlanabilir bir pencere yöneticisi +Comment[tt]=Emacs Lisp-kebek tel belän kiñäylelgän täräzä-idäräçe +Comment[uk]=Менеджер вікон, що можна розширювати мовою скриптів на кшталт Emacs Lisp +Comment[vi]=Trình quản lý cửa sổ có thể viết kịch bản được với ngôn ngữ giống Emacs Lisp +Comment[wa]=On manaedjeu d' purnea k' on pout radjouter des rawetes, apontiåve e scripes dins on lingaedje do stîle Emacs Lisp +Comment[zh_CN]=可用类似 Emacs Lisp 的语法进行编程的窗口管理器 +Comment[zh_TW]=一個可用類似 Emacs Lisp 語言延伸的視窗管理程式 diff --git a/kdm/kfrontend/sessions/twm.desktop b/kdm/kfrontend/sessions/twm.desktop new file mode 100644 index 000000000..894371ae9 --- /dev/null +++ b/kdm/kfrontend/sessions/twm.desktop @@ -0,0 +1,71 @@ +[Desktop Entry] +Type=XSession +Exec=twm +TryExec=twm +Name=TWM +Name[eo]=TFA +Name[hi]=टीडबल्यूएम +Name[te]=టి డబ్ల్యు ఎం +Comment=The Tab Window Manager +Comment[af]=Die Tab venster bestuurder +Comment[ar]=مدير النوافذ Tab +Comment[be]=Кіраўнік вокнаў з укладкамі Tab Window Manager +Comment[bn]=দি ট্যাব উইণ্ডো ম্যানেজার +Comment[bs]=Tab Window Manager +Comment[ca]=El gestor de finestres Tab +Comment[csb]=Tab Window Manager +Comment[cy]=Y Trefnydd Ffenestri Tab +Comment[da]=Tab-vindueshåndtering +Comment[de]=Der Tab-Fenstermanager +Comment[el]=Ο διαχειριστής παραθύρων Tab +Comment[eo]=Taba fenestroadministrilo +Comment[es]=El Tab Window Manager +Comment[et]=Kaartidega aknahaldur +Comment[eu]=Tab leiho kudeatzailea +Comment[fa]=مدیر پنجرۀ تب +Comment[fi]=Välilehtiä tukeva ikkunaohjelma +Comment[fy]=De Ljepper Finster Behearder +Comment[gl]=O Xestor de Fiestras Tab +Comment[hi]=टैब विंडो प्रबंधक +Comment[hr]=Tab upravitelj prozora +Comment[hu]=Tab Window Manager ablakkezelő +Comment[is]=Tab gluggastjórinn +Comment[it]=Il Tab Window Manager +Comment[ja]=Tab 化ウィンドウマネージャ +Comment[ka]=X11 სისტემის ტრადიციული ფანჯრის მენეჯერი +Comment[kk]=Tab терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចជាផ្ទាំង +Comment[ko]=탭 창 관리자 +Comment[lt]=Kortelių langų tvarkyklė +Comment[lv]=Tabu logu menedžeris +Comment[mk]=Tab Window Manager +Comment[mn]=Tab Цонхны удирдагч +Comment[ms]=Pengurus Tetingkap Tab +Comment[mt]=Tab Window Manager +Comment[nb]=Tab Vindusbehandler +Comment[nds]=De Tab-Finsterpleger +Comment[ne]=ट्याब सञ्झ्याल प्रबन्धक +Comment[nl]=De Tab Window Manager +Comment[nn]=Tab Window Manager +Comment[pa]=ਟੈਬ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Tab Window Manager +Comment[pt]=O Tab Window Manager +Comment[pt_BR]=O Gerenciador de Janelas de Abas +Comment[ro]=Tab Window Manager +Comment[ru]=Традиционный оконный менеджер системы X11 +Comment[rw]=Mugenga Dirishya Agafishi +Comment[se]=Tab-láse lásegieđahalli +Comment[sk]=Správca okien s kartami +Comment[sl]=Tab Window Manager, okenski upravitelj z zavihki +Comment[sv]=Flikfönsterhanteraren +Comment[ta]=டாப் சாளர மேலாளர் +Comment[te]=టాబ్ విండొ అభికర్త +Comment[tg]=Tab-и мудири тиреза +Comment[th]=Tab Window Manager +Comment[tr]=Tab Pencere Yöneticisi +Comment[tt]=X11 sistemendäge kebek tabaqlı täräzä-idäräçe +Comment[uk]=Tab Window Manager +Comment[vi]=Trình Quản lý Cửa sổ Thẻ +Comment[wa]=Li manaedjeu di purneas avou Linwetes (Tab Window Manager) +Comment[zh_CN]=标签式窗口管理器 +Comment[zh_TW]=Tab 視窗管理程式 diff --git a/kdm/kfrontend/sessions/ude.desktop b/kdm/kfrontend/sessions/ude.desktop new file mode 100644 index 000000000..fb451da1a --- /dev/null +++ b/kdm/kfrontend/sessions/ude.desktop @@ -0,0 +1,75 @@ +[Desktop Entry] +Type=XSession +Exec=ude +TryExec=ude +Name=UDE +Name[eo]=ULĈ +Name[hi]=यूडीई +Name[te]=యుడిఈ +Comment=The UNIX Desktop Environment +Comment[af]=Die 'Unix Desktop Environment' +Comment[ar]=بيئة سطح مكتب يونكس +Comment[be]=Працоўнае асяроддзе UNIX +Comment[bn]=দি ইউনিক্স ডেস্কটপ এনভায়রনমেন্ট +Comment[br]=An endro burev UNIX +Comment[bs]=UNIX Desktop Environment +Comment[ca]=L'entorn d'escriptori de Unix +Comment[csb]=Òkrãże pùltu Uniksa +Comment[cy]=Yr Amgylchedd Penbwrdd UNIX +Comment[da]=UNIX desktopmiljø +Comment[de]=Das UNIX Desktop Environment +Comment[el]=Το περιβάλλον επιφάνειας εργασίας του UNIX +Comment[eo]=La Uniksa Labortablo Ĉirkauaĵo +Comment[es]=El UNIX Desktop Environment +Comment[et]=UNIXi töölaua keskkond +Comment[eu]=UNIX mahaigain ingurunea +Comment[fa]=محیط رومیزی یونیکس +Comment[fi]=UNIX-työpöytäympäristö +Comment[fy]=De Unix Desktop Environment +Comment[ga]=Timpeallacht Deisce UNIX (UDE) +Comment[gl]=O Entorno de Escritório de UNIX +Comment[hi]=यूनिक्स डेस्कटॉप माहौल +Comment[hr]=UNIX radno okruženje +Comment[hu]=UNIX Desktop Environment ablakkezelő +Comment[is]=UNIX skjáborðsumhverfið +Comment[it]=Lo Unix Desktop Environment +Comment[ja]=UNIX デスクトップ環境 +Comment[ka]=UNIX-ის სამუშაო გარემო +Comment[kk]=UNIX Desktop Environment +Comment[km]=UNIX Desktop Environment +Comment[ko]=UNIX 데스크톱 환경 +Comment[lt]=UNIX darbastalio aplinka +Comment[lv]= UNIX Darba virsmas vide +Comment[mk]=UNIX Desktop Environment +Comment[mn]=ЮНИКС ажлын тавцангийн орчин +Comment[ms]=Persekitaran Desktop UNIX +Comment[mt]=UNIX Desktop Environment +Comment[nb]=UNIX Desktop Environment +Comment[nds]=De UNIX-Schriefdisch-Ümgeven +Comment[ne]=युनिक्स डेस्कटप वातावरण +Comment[nl]=De Unix Desktop Environment +Comment[nn]=UNIX Desktop Environment +Comment[pa]=UNIX Desktop Environment +Comment[pl]=Środowisko pulpitu Uniksa +Comment[pt]=O Unix Desktop Environment +Comment[pt_BR]=Ambiente de Trabalho do UNIX +Comment[ro]=Mediul grafic UNIX +Comment[ru]=UNIX Desktop Environment +Comment[rw]=Ibikikije Ibiro UNIX +Comment[se]=UNIX Desktop Environment +Comment[sl]=Namizno okolje UNIX +Comment[sr]=Unix радно окружење +Comment[sr@Latn]=Unix radno okruženje +Comment[sv]=Unix-skrivbordsmiljön +Comment[ta]=யுனிக்ஸ் மேல்மேசை சூழல் +Comment[te]=యూనిక్స్ రంగస్థల పర్యావరణం +Comment[tg]=Атрофи мизи кории UNIX +Comment[th]=สภาพแวดล้อมเดสก์ทอป UNIX +Comment[tr]=Unix Masaüstü Ortamı +Comment[tt]=UNIX Desktop Environment +Comment[uz]=Unix ish stoli muhiti (Unix Desktop Environment) +Comment[uz@cyrillic]=Unix иш столи муҳити (Unix Desktop Environment) +Comment[vi]=Môi trường Màn hình nền UNIX +Comment[wa]=L' evironmint di scribanne Unix UDE +Comment[zh_CN]=UNIX 桌面环境 +Comment[zh_TW]=Unix 桌面環境 diff --git a/kdm/kfrontend/sessions/vtwm.desktop b/kdm/kfrontend/sessions/vtwm.desktop new file mode 100644 index 000000000..50af40a63 --- /dev/null +++ b/kdm/kfrontend/sessions/vtwm.desktop @@ -0,0 +1,72 @@ +[Desktop Entry] +Type=XSession +Exec=vtwm +TryExec=vtwm +Name=VTWM +Name[eo]=VTFA +Name[hi]=वीटीडबल्यूएम +Name[te]= వి టి డబ్ల్యు ఎం +Comment=The Virtual Tab Window Manager. TWM enhanced by virtual screens, etc. +Comment[af]=Die virtuele tab venster bestuurder. TWM wat met virtuele skerms uitgebreik is. +Comment[ar]=مدير نوافذ Tab الوهمي، مدير نوافذ TWM محسّن بأسطح المكتب الوهمية، إلخ. +Comment[be]=Віртуальны кіраўнік вокнаў з укладкамі Virtual Tab Window Manager. TWM, з дадатковай падтрымкай віртуальных экранаў і інш. +Comment[bn]=ভার্চুয়াল ট্যাব উইণ্ডো ম্যানেজার, ভার্চুয়াল স্ক্রীণ ইত্যাদি দ্বারা বর্ধিত +Comment[bs]=Virtual Tab Window Manager. TWM proširen virtuelnim ekranima itd. +Comment[ca]=El Virtual Tab Window Manager. TWM millorat per a pantalles virtuals, etc. +Comment[cs]=The Virtual Tab Window Manager. TWM vylepšené o virtuální obrazovky aj. +Comment[csb]=Virtual Tab Window Manager. TWM zbògacony ò wirtualné pùltë ëtp. +Comment[cy]=Y Trefnydd Ffenestri Tab Rhith. TWM wedi'i wella gan sgriniau rhith, ayyb. +Comment[da]=Virtual Tab Window Manager. TWM udvidet med virtuelle skærme osv. +Comment[de]=Der Virtual-Tab-Fenstermanager, eine Erweiterung von TWM mit virtuellen Arbeitsflächen usw. +Comment[el]=Ο εικονικός Tab διαχειριστής παραθύρων. Ο TWM εμπλουτισμένος με εικονικές οθόνες, κτλ. +Comment[eo]=La Virtuala Taba Fenestroadministrilo, TFA plibonigita per virtualaj ekranoj +Comment[es]=El Virtual Tab Window Manager, TWM mejorado con pantallas virtuales, etc. +Comment[et]=Virtuaalsete kaartidega aknahaldur ehk TWM, mida on täiendatud virtuaalsete ekraanidega jne. +Comment[eu]=Virtual Tab leiho kudeatzailea. Pantaila birtual eta abarrez hobetutako TWM-a +Comment[fa]=یک مدیر پنجرۀ تب مجازی.TWM گسترشیافته توسط پردههای مجازی و غیره. +Comment[fi]=Välilehtiä ja virtuaalityöpöytiä tukeva ikkunaohjelma, pohjautuu TWM:ään +Comment[fr]=The Virtual Tab Window Manager. TWM avec en plus la gestion des bureaux multiples +Comment[fy]=De firtuele Ljepper Window Manager. TWM útbreid mei firtuele buroblêden, ensfh. +Comment[gl]=O Virtual Tab Window Manager. TWM mellorado con pantallas virtuais, etc. +Comment[he]=The Virtual Tab Window Manager. TWM המשופר בשולחנות עבודה וירטואליים וכו' +Comment[hi]=आभासी टैब विंडो प्रबंधक. TWM को आभासी स्क्रीन इत्यादि से बेहतर बनाया गया +Comment[hr]=Comment=Virtual Tab Window Manager. TWM poboljšan virtualnim zaslonima itd. +Comment[hu]=Virtual Tab Window Manager, egy TWM-változat, támogatja a virtuális képernyőkezelést +Comment[is]=Tab gluggastjórinn endurbættur með sýndarskjám og fleiru. +Comment[it]=Il Virtual Tab Window Manager. TWM migliorato con schermi virtuali ecc. +Comment[ja]=仮想スクリーンなどの機能を拡張した TWM ベースの仮想タブウィンドウマネージャ +Comment[ka]=ვირტუალური ეკრანებით აღჭურვილი TWM-ის ვარიანტი +Comment[kk]=Virtual Tab Window Manager. TWM-негіздеген, виртуалды экрандары және т.б.с.с бар терезе менеджері. +Comment[km]=Virtual Tab Window Manager ។ TWM ដែលបានធ្វើឲ្យប្រសើរដោយអេក្រង់និមិត្ត ជាដើម ។ +Comment[lt]=Virtualių kortelių darbastalio aplinka. TWM išplėsta virtualiais darbastaliais ir pan. +Comment[lv]=Virtuālo tabu logu menedžeris. TWM papildināts ar virtuālajiem ekrāniem utml. +Comment[mk]=Virtual Tab Window Manager. TWM подобрен со виртуелни површини итн. +Comment[ms]=Pengurus Tetingkap Tab Maya. TWM dipertingkat dengan skrin maya, dsb. +Comment[mt]=Virtual Tab Window Manager. TWM flimkien ma' desktops virtwali eċċ +Comment[nb]=Virtual Tab vindusbehandler. TWM utvidet med virtuelle skjermer, osv. +Comment[nds]=De "Virtual Tab Window Manager". Dat is TWM verwiedert üm virtuelle Schirmen etc. +Comment[ne]=अवास्तविक ट्याब सञ्झ्याल प्रबन्धक । अवास्तविक पर्दाद्वारा बृद्धि गरिएको TWM, आदि +Comment[nl]=De Virtual Tab Window Manager. TWM uitgebreid met virtuele bureaubladen, etc. +Comment[nn]=Virtual Tab Window Manager. TWM utvida med virtuelle skjermar og anna. +Comment[pa]=Virtual Tab Window Manager. TWM ਫਰਜ਼ੀ ਪਰਦਿਆਂ ਨਾਲ ਲੈੱਸ +Comment[pl]=Virtual Tab Window Manager. TWM wzbogacony o wirtualne pulpity itp. +Comment[pt]=O Virtual Tab Window Manager. Um TWM melhorado com ecrãs virtuais, etc. +Comment[pt_BR]=O gerenciador de janelas de abas virtuais. TWM melhorado pelas telas virtuais. +Comment[ro]=Virtual Tab Window Manager. TWM îmbunătățit cu ecrane virtuale etc. +Comment[ru]=Вариант TWM, имеющий виртуальные экраны и т.д. +Comment[rw]=Mugenga Dirishya y'Agafishi Itaboneka.TWM ivuguruwe na mugaragaza zitagaragara, n'ibindi. +Comment[se]=The Virtual Tab Window Manager. TWM buoriduvvon virtuella čállinbevddiin, jna. +Comment[sk]=The Virtual Tab Window Manager. TWM rozšírený o virtuálne obrazovky atď. +Comment[sl]=Virtual Tab Window Manager. TWM, izboljšan z navideznimi zasloni ipd. +Comment[sr]=„The Virtual Tab Window Manager“. TWM побољшан виртуелним екранима и сл. +Comment[sr@Latn]=„The Virtual Tab Window Manager“. TWM poboljšan virtuelnim ekranima i sl. +Comment[sv]=Virtuell flikfönsterhanterare. TWM utökad med virtuella skärmar, etc. +Comment[ta]=மெய்நிகர் தத்தல் சாளர மேலாளர். TWM மெய்நிகர் திரைகளால் மேம்படுத்தப்பட்டது. +Comment[th]=The Virtual Tab Window Manager คือ TWM ที่เพิ่มความสามารถโดยหน้าจอเสมือน และอื่นๆ +Comment[tr]=Virtual Tab Masaüstü Yöneticisi. +Comment[tt]=Virtual Tab Window Manager. Öställär östälengän TWM kebek. +Comment[uk]=Virtual Tab Window Manager. TWM з підтримкою віртуальних екранів. +Comment[vi]=Trình Quản lý Cửa sổ Thẻ Ảo. TWM cải tiến với màn hình ảo, ... +Comment[wa]=Li Forveyou Manaedjeu di Purneas avou Linwetes (Virtual Tab Window Manager). TWM a sacwants forveyowès waitroûles, evnd. +Comment[zh_CN]=虚拟标签式窗口管理器。用虚拟屏幕等功能增强的 TWM。 +Comment[zh_TW]=虛擬 Tab 視窗管理程式。基於 TWM 並加強虛擬螢幕等等。 diff --git a/kdm/kfrontend/sessions/w9wm.desktop b/kdm/kfrontend/sessions/w9wm.desktop new file mode 100644 index 000000000..92633bcf7 --- /dev/null +++ b/kdm/kfrontend/sessions/w9wm.desktop @@ -0,0 +1,74 @@ +[Desktop Entry] +Type=XSession +Exec=w9wm +TryExec=w9wm +Name=W9WM +Name[eo]=F9FA +Name[hi]=डबल्यू9डबल्यूएम +Name[ta]=IceWM +Name[te]=డబ్ల్యు 9 డబ్ల్యు ఎం +Name[th]=ตัวจัดการหน้าต่าง W9WM +Comment=A window manager based on 9WM, enhanced by virtual screens and keyboard bindings +Comment[af]='n Venster bestuurder wat op 9WM gebaseer is, uitgebrei met virtuele skerms en sleutelbord bindinge +Comment[ar]=مدير نوافذ مبني على 9WM، محسّن الشاشات الوهمية والمفاتيح المرتبطة +Comment[be]=Кіраўнік вокнаў, заснаваны на 9WM, з дадатковай падтрымкай віртуальных экранаў і клавішных скаротаў +Comment[bn]=9WM ভিত্তিক উইণ্ডো ম্যানেজার, ভার্চুয়াল স্ক্রীণ এবং কীবোর্ড বাইন্ডিং দ্বারা বর্ধিত +Comment[bs]=Window manager baziran na 9WM, proširen virtuelnim ekranima i prečicama tastature +Comment[ca]=Un gestor de finestres basat en 9WM, millorat per a pantalles virtuals i amb dreceres de teclat +Comment[cs]=Správce oken založený na 9WM rozšířený o virtuální plochy a klávesové zkratky +Comment[csb]=Menedżer òknów òpiarty na 9WM, zbògacony ò wirtualné ekranë ë kònfigùracëjã klawiszowëch skrodzënów +Comment[cy]=Trefnydd ffenestri wedi'i seilio ar 9WM, wedi'i wella gan sgriniau rhith a rhwymiadau bysyll. +Comment[da]=En vindueshåndtering baseret på 9WM, udvidet med virtuelle skærme og tastaturbindinger +Comment[de]=Fenstermanager auf der Basis von 9WM, erweitert durch virtuelle Arbeitsflächen und Tastaturzuordnungen +Comment[el]=Ένας διαχειριστής παραθύρων βασισμένος στον 9WM, εμπλουτισμένος με εικονικές οθόνες και συνδυασμούς πλήκτρων +Comment[eo]=Fenestroadministrilo devenigita de 9FA, plibonigita per virtualaj ekranoj kaj klaviara uzo +Comment[es]=Un gestor de ventanas basado en 9WM, mejorado con ventanas virtuales y accesos rápidos de teclado +Comment[et]=Aknahaldur, mille aluseks on 9WM ja mida on täiendatud virtuaalsete ekraanide ja kiirklahvide võimalusega +Comment[eu]=9WM-n oinarritutako leiho kudeatzailea, pantaila birtual eta laster-teklez hobetua +Comment[fa]=یک مدیر پنجره بر اساس 9WM، گسترشیافته توسط پردههای مجازی و مقیدسازیهای صفحه کلید +Comment[fi]=9WM:ään pohjautuva ikkunaohjelma, jossa tuki virtuaalityöpöydille ja näppäimistöyhdistelmille +Comment[fr]=Un gestionnaire de fenêtres fondé sur 9WM, avec en plus la gestion des bureaux virtuels et des raccourcis clavier +Comment[fy]=In finstersmanager basearrre op 9WM. útbreid mei firtuele buroblêden en fluchtoetsen +Comment[gl]=Un xestor de fiestras baseado en 9WM, mellorado polas pantallas virtuais e atallos de teclado +Comment[he]=מנהל חלונות המבוסס על 9WM, המשופר בשולחנות עבודה וירטואליים ומיפוי מקשים +Comment[hi]= 9डबल्यूएम आधारित विंडो प्रबंधक, आभासी स्क्रीन तथा की-बोर्ड बाइंडिंग से बेहतर बनाया गया +Comment[hr]=Upravitelj prozora zasnovan na 9WM, unaprijeđen virtualnim zaslonima i prečacima tipkovnice +Comment[hu]=Egy 9WM-alapú ablakkezelő, virtuális képernyőkezeléssel, konfigurálható billentyűparancsokkal +Comment[is]=Gluggastjóri byggður á 9WM en endurbættur með sýndarskjám og lyklaborðsvörpunum +Comment[it]=Un window manager basato su 9WM, migliorato con schermi virtuali e scorciatoie per la tastiera. +Comment[ja]=仮想スクリーンとキーボードショートカット機能を強化した 9WM ベースのウィンドウマネージャ +Comment[ka]=ფანჯრის მენეჯერი 9wm-ს ბაზაზე, ვირტუალური ეკრანებით და კლავიატურის შესაბამისობებით +Comment[kk]=9WM-негіздеген, виртуалды экрандары, пернетақта тіркесімдері бар терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចផ្អែកលើ 9WM ដែលត្រូវបានធ្វើឲ្យប្រសើរដោយអេក្រង់និមិត្ត និងការចងគ្រាប់ចុច +Comment[ko]=부분적 그놈 지원과 가상 데스크톱 지원을 사용하는 AEWM 기반 창 관리자 +Comment[lt]=Langų tvarkyklė, paremta 9WM, išplėsta virtualių ekranų ir klaviatūros greitųjų klavišų palaikymu +Comment[lv]=Logu menedžeris bāzēts uz 9WM, papildināts ar virtuālajiem ekrāniem un tastatūras saīsnēm +Comment[mk]=Менаџер на прозорци базиран на 9WM, подобрен со виртуелни површини и поврзувања за тастатурата +Comment[ms]=Pengurus tetingkap berdasarkan 9WM, dipertingkat dengan skrin maya dan ikatan papan kekunci +Comment[mt]=Window manager ibbażat fuq 9WM, flimkien ma desktops virtwali u hotkeys +Comment[nb]=En vindusbehandler basert på 9WM, forbedret med virtuelle skjermer og hurtigtaster +Comment[nds]=En Finsterpleger opbuut op 9WM, verwiedert üm virtuelle Schirmen un Tastkombinatschonen +Comment[ne]=कुञ्जीपाटी बाइन्डिङ र अवास्तविक पर्दाहरूद्वारा बृद्धि गरिएको 9WM मा आधारित सञ्झ्याल प्रबन्धक +Comment[nl]=Een windowmanager gebaseerd op 9WM. Uitgebreid met virtuele bureaubladen en sneltoetsen +Comment[nn]=Ein vindaugssjef basert på 9WM, forbetra med virtuelle skjermar og snøggtastar +Comment[pa]=9WM ਤੇ ਆਧਾਰਿਤ ਝਰੋਖਾ ਮੈਨੇਜਰ, ਫਰਜ਼ੀ ਪਰਦਿਆਂ ਤੇ ਕੀ-ਬੋਰਡ ਬਾਈਡਿੰਗ ਨਾਲ ਲੈੱਸ +Comment[pl]=Menedżer okien oparty na 9WM, wzbogacony o wirtualne ekrany i konfigurowanie skrótów klawiszowych +Comment[pt]=Um gestor de janelas baseado no 9WM, melhorado com ecrãs virtuais e atalhos de teclado +Comment[pt_BR]=Um gerenciador de janelas baseado no 9Wm, melhorado pelas telas virtuais e atalhos de teclado +Comment[ro]=Un manager de ferestre bazat pe 9WM, îmbunătățir cu ecrane virtuale și acceleratori de tastatură +Comment[ru]=Оконный менеджер на основе 9wm, имеющий виртуальные экраны и привязку клавиш +Comment[rw]=Mugenga Dirishya ishingiye kuri 9WM, ivuguruwe hakoreshejwe mugaragaza itaboneka n'amahuza mwandikisho +Comment[sk]=Správca okien založený na 9WM, rozšírený o virtuálne obrazovkya klávesové skratky +Comment[sl]=Okenski upravitelj na osnovi 9WM, izboljšan z navideznimi zasloni in tipkovnimi vezmi +Comment[sr]=Менаџер прозора заснован на 9WM-у, побољшан виртуелним екранима и повезивањем тастатуре +Comment[sr@Latn]=Menadžer prozora zasnovan na 9WM-u, poboljšan virtuelnim ekranima i povezivanjem tastature +Comment[sv]=Fönsterhanterare baserad på 9WM, utökad med virtuella skärmar och tangentbindingar +Comment[ta]=9WM அடிப்படையிலான மெய்நிகர் திரை மற்றும் விசைப்பலகை சேர்ப்புகளால் மேம்படுத்தப்பட்ட சாளர மேலாளார், +Comment[th]=ระบบจัดการหน้าต่างที่สร้างมาจาก 9WM และเพิ่มความสามารถด้วยหน้าจอเสมือนและแป้นพิมพ์ลัด +Comment[tr]=9WM tabanlı, sanal ekranları ve klavye kısayolları ile geliştirilmiş bir masaüstü yöneticisi +Comment[tt]=Xıyalí öställär belän töylekne totqan täräzä-idäräçe. 9WM asılında +Comment[uk]=Менеджер вікон, заснований на 9WM, додано віртуальні екрани та прив'язки клавіш +Comment[vi]=Trình quản lý cửa sổ dựa vào 9WM, cải tiến với màn hình ảo, tổ hợp phím +Comment[wa]=On manaedjeu di purneas båzé so 9WM, avou sopoirt po les forveyous scribannes eyet les rascourtis di taprece. +Comment[zh_CN]=基于 9WM 的窗口管理器,用虚拟屏幕和键盘绑定等功能增强了 +Comment[zh_TW]=一個基於 9WM 並加強虛擬螢幕及鍵盤組合鍵功能 diff --git a/kdm/kfrontend/sessions/waimea.desktop b/kdm/kfrontend/sessions/waimea.desktop new file mode 100644 index 000000000..8981b5af6 --- /dev/null +++ b/kdm/kfrontend/sessions/waimea.desktop @@ -0,0 +1,77 @@ +[Desktop Entry] +Type=XSession +Exec=waimea +TryExec=waimea +Name=Waimea +Name[eo]=Vaimeo +Name[hi]=वाईमिया +Name[ne]=विमेआ +Name[pa]=ਵਾਈਮਿਆ +Name[te]=వైమెయా +Comment=A highly customizable window manager based on Blackbox +Comment[af]='n Hoë opstelbare venster bestuurder wat op Blackbox gebaseer is +Comment[ar]=مدير نوافذ قابل جداً للتخصيص مبني على Blackbox +Comment[be]=Кіраўнік вокнаў, заснаваны на Blackbox, з магчымасцю настаўлення +Comment[bn]=ব্ল্যাকবক্স ভিত্তিক উইণ্ডো ম্যানেজার +Comment[bs]=Visoko prilagodljiv window manager baziran na Blackbox +Comment[ca]=Un gestor de finestres altament configurable basat en Blackbox +Comment[cs]=Vysoce přizpůsobitelný správce oken založený na Blackboxu +Comment[csb]=Menedżer òknów òpiartëch na Blackbox z wiôldżima mòżnotama dopasowaniô +Comment[cy]=Trefnydd ffenestri sy'n hawdd ei ffurfweddu, wedi'i seilio ar Ddu-flwch +Comment[da]=En meget indstillelig vindueshåndtering baseret på Blackbox +Comment[de]=Vielfältig anpassbarer Fenstermanager, der auf Blackbox basiert +Comment[el]=Ένας ιδιαίτερα παραμετροποιήσιμος διαχειριστής παραθύρων βασισμένος στον Blackbox +Comment[en_GB]=A highly customisable window manager based on Blackbox +Comment[eo]=Tre agordebla fenestroadministrilo, devenigita de Negrujo +Comment[es]=Un gestor de ventanas muy personalizable basado en Blackbox +Comment[et]=Väga hästi kohandatav aknahaldur, aluseks Blackbox +Comment[eu]=Blackboxen oinarritutako leiho-kudeatzaile zeharo pertsonalizagarria +Comment[fa]=یک مدیر پنجره با قابلیت سفارشیسازی بالا بر اساس Blackbox +Comment[fi]=Blackboxiin perustuva paljon muokattavissa oleva ikkunaohjelma +Comment[fr]=Un gestionnaire de fenêtres très configurable fondé sur Blackbox +Comment[fy]=In tige ynstelbere finstersmanager, basearre op Blackbox +Comment[gl]=Un xestor de fiestras moi personalizábel baseado en Blackbox +Comment[he]=מנהל חלונות המאפשר התאמה אישית גבוהה והמבוסס על Blackbox +Comment[hi]=ब्लेक-बाक्स आधारित, अत्यंत कस्टमाइजेबल विंडो प्रबंधक +Comment[hr]=Vrlo prilagodljiv upravitelj prozora zasnovan na Blackboxu +Comment[hu]=Egy sokféle beállítási lehetőséggel rendelkező ablakkezelő a Blackbox alapján +Comment[is]=Afar stillanlegur gluggastjóri byggður á Blackbox +Comment[it]=Un window manager molto personalizzabile basato su BlackBox +Comment[ja]=Blackbox ベースの高度なカスタマイズが可能なウィンドウマネージャ +Comment[ka]=კონფიგურირებადი ფანჯრის მენეჯერი Blackbox-ს ბაზაზე +Comment[kk]=Blackbox-негіздеген, баптау жағынан бай, терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចដែលអាចប្ដូរតាមបំណងកម្រិតខ្ពស់ ដោយផ្អែកលើ Blackbox +Comment[ko]=Blackbox 기반의 사용자 정의가 가능한 관리자 +Comment[lt]=Daug konfigūravimo parinkčių turinti langų tvarkyklė, paremta Blackbox +Comment[lv]=Plaši konfigurējams logu menedžeris bāzēts uz Blackbox +Comment[mk]=Менаџер на прозорци базиран на Blackbox со голем број опции +Comment[ms]=Pengurus tetingkap boleh suai berdasarkan Kotak Hitam +Comment[mt]=Window manager konfigurabbli ibbażat fuq BlackBox +Comment[nb]=En vindusbehandler med mange tilpasninger, basert på Blackbox +Comment[nds]=En Finsterpleger mit mennige Instellen, opbuut op Blackbox +Comment[ne]=कालो बाकसमा आधारित उच्च अनुकूल योग्य एक सञ्झ्याल प्रबन्धक +Comment[nl]=Een zeer instelbare windowmanager, gebaseerd op Blackbox +Comment[nn]=Ein vindaugssjef med mange tilpassingar, basert på Blackbox +Comment[pa]=ਬਲੈਕਬਕਸੇ 'ਤੇ ਆਧਾਰਿਤ ਅਤਿ ਸੋਧਯਯੋਗ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Menedżer okien oparty na Blackbox z dużymi możliwościami dostosowania +Comment[pt]=Um gestor de janelas altamente configurável, baseado no Blackbox +Comment[pt_BR]=Um gerenciador de janelas altamente personalizável, baseado no Blackbox +Comment[ro]=Un manager de ferestre foarte configurabil bazat pe Blackbox +Comment[ru]=Настраиваемый оконный менеджер, основанный на Blackbox +Comment[rw]=Mugenga dirishya ihabwa imiterere yifuzwa mu buryo hejuru ishingiye ku Gasandukumukara +Comment[sk]=Veľmi prispôsobiteľný správca okien založený na Blackbox +Comment[sl]=Visoko nastavljiv okenski upravitelj na osnovi Blackboxa +Comment[sr]=Врло прилагодљив менаџер прозора заснован на Blackbox-у +Comment[sr@Latn]=Vrlo prilagodljiv menadžer prozora zasnovan na Blackbox-u +Comment[sv]=Ytterst anpassningsbar fönsterhanterare baserad på Blackbox +Comment[ta]=தனதாக்க வல்ல கருப்புப்பெட்டி சார்ந்த சாளர மேலாளர் +Comment[th]=ระบบจัดการหน้าต่างที่ปรับแต่งได้อย่างละเอียด สร้างมาจาก Blackbox +Comment[tr]=Blackbox temelli, kolayca özelleştirilebilir bir pencere yöneticisi +Comment[tt]=Yaqşı köylänüçän täräzä-idäräçe. Blackbox asılında +Comment[uk]=Надгнучкий менеджер вікон, заснований на Blackbox +Comment[uz]=Blackbox asosida yaratilgan, moslab boʻladigan oyna boshqaruvchi +Comment[uz@cyrillic]=Blackbox асосида яратилган, мослаб бўладиган ойна бошқарувчи +Comment[vi]=Trình quản lý cửa sổ rất dễ cá nhân hoá dựa trên Blackbox +Comment[wa]=On manaedjeu di purneas k' vos ploz pår mete da vosse båzé so Blackbox +Comment[zh_CN]=基于 BlackBox 可高度自定义的窗口管理器 +Comment[zh_TW]=一個基於 Blackbox 且高度可客製化的視窗管理程式 diff --git a/kdm/kfrontend/sessions/wm2.desktop b/kdm/kfrontend/sessions/wm2.desktop new file mode 100644 index 000000000..618c10bcc --- /dev/null +++ b/kdm/kfrontend/sessions/wm2.desktop @@ -0,0 +1,77 @@ +[Desktop Entry] +Type=XSession +Exec=wm2 +TryExec=wm2 +Name=WM2 +Name[eo]=Fa2 +Name[hi]=डबल्यूएम2 +Name[te]=డబ్ల్యు ఎం 2 +Name[th]=ตัวจัดการหน้าต่าง WM2 +Comment=A small, non-configurable window manager +Comment[af]='n Klein, nie opstelbare venster bestuurder +Comment[ar]=مدير نوافذ صغير غير قابل للإعداد +Comment[be]=Маленькі кіраўнік вокнаў, без магчымасці настаўленняў +Comment[bn]=একটি ছোটো উইণ্ডো ম্যানেজার +Comment[bs]=Mali, ne-konfigurabilni window manager +Comment[ca]=Un petit i no configurable gestor de finestres +Comment[cs]=Malý nepřizpůsobitelný správce oken +Comment[csb]=Môłi menedżer òknów bez mòżnotë kònfigùracëji +Comment[cy]=Trefnydd ffenestri bach na ellir ffurfweddu +Comment[da]=En lille, ikke-indstillelig vindueshåndtering +Comment[de]=Kleiner, nicht einstellbarer Fenstermanager +Comment[el]=Ένας μικρός, μη παραμετροποιήσιμος διαχειριστής παραθύρων +Comment[eo]=Malgranda, ne agordebla fenestroadministrilo +Comment[es]=Un gestor de ventanas pequeño y no configurable +Comment[et]=Väike ja seadistamatu aknahaldur +Comment[eu]=Leiho-kudeatzaile txikia, konfiguratu ezin dena +Comment[fa]=یک مدیر پنجرۀ کوچک و غیرقابل پیکربندی +Comment[fi]=Pieni ikkunaohjelma, jossa ei ole asetuksia +Comment[fr]=Un gestionnaire de fenêtres petit et non configurable +Comment[fy]=In lytse, net-ynstelbere finstersmanager +Comment[gl]=Un xestor de fiestras pequeno non configurábel +Comment[he]=מנהל חלונות קטן ולא ניתן להגדרה +Comment[hi]=एक छोटा विंडो प्रबंधक जो कॉन्फ़िगर नहीं हो सकता +Comment[hr]=Malen, nepodesiv upravitelj prozora +Comment[hu]=Egy nagyon egyszerű ablakkezelő, beállítási lehetőségek nélkül +Comment[is]=Lítill, einfaldur gluggastjóri sem er ekki hægt að stilla +Comment[it]=Un window manager piccolo e non configurabile +Comment[ja]=小さくて設定項目のないウィンドウマネージャ +Comment[ka]=პატარა და არაკონფიგურირებადი ფანჯრის მენეჯერი +Comment[kk]=Шағын, баптауы жоқ, терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចតូច ហើយមិនអាចកំណត់រចនាសម្ព័ន្ធបាន +Comment[ko]=설정할 수 없는 작은 창 관리자 +Comment[lt]=Maža, nekonfigūruojama langų tvarkyklė +Comment[lv]=Mazs, nekonfigurējams logu menedžeris +Comment[mk]=Мал и неконфигурабилен менаџер на прозорци +Comment[mn]=Жижиг тохируулах боломжгүй цонх удирдагч +Comment[ms]=Pengurus tetingkap yang kecil dan tidak boleh konfigur +Comment[mt]=Window manager żgħir u mhux konfigurabbli +Comment[nb]=En liten vindusbehandler uten tilpasninger +Comment[nds]=En lütte Finsterpleger ahn Instellen +Comment[ne]=सानो, कन्फिगर गर्न नसकिने सञ्झ्याल प्रबन्धक +Comment[nl]=Een kleine, niet-instelbare windowmanager +Comment[nn]=Ein liten vindaugssjef utan tilpassingar +Comment[pa]=ਇੱਕ ਹਲਕਾ ਨਾ-ਸੋਧਯੋਗ ਝਰੋਖਾ ਮੈਨੇਜਰ +Comment[pl]=Mały menedżer okien nie podlegający konfiguracji +Comment[pt]=Um gestor de janelas pequeno e não-configurável +Comment[pt_BR]=Um pequeno e não-configurável gerenciador de janelas +Comment[ro]=Un manager de ferestre mic, neconfigurabil +Comment[ru]=Маленький, не настраиваемый оконный менеджер +Comment[rw]=Mugenga Dirishya itabonezwa, ntoya +Comment[se]=Unna, ii heivehahtti lásegieđahalli +Comment[sk]=Malý, nenastaviteľný správca okien +Comment[sl]=Majhen, nenastavljiv okenski upravitelj +Comment[sr]=Мали, неподесиви менаџер прозора +Comment[sr@Latn]=Mali, nepodesivi menadžer prozora +Comment[sv]=Liten fönsterhanterare utan anpassningsmöjligheter +Comment[ta]=சிறிய, வடிவமைக்க முடியாத சாளர மேலாளர் +Comment[th]=ระบบจัดการหน้าที่ขนาดเล็ก ที่ไม่สามารถปรับแต่งอะไรได้ +Comment[tr]=Küçük ve yapılandırılamayan bir pencere yöneticisi +Comment[tt]=Caylanmí torğan keçkenä täräzä-idäräçe +Comment[uk]=Невеличкий менеджер вікон без можливості налаштування +Comment[uz]=Kichik, moslab boʻlmaydigan oyna boshqaruvchi +Comment[uz@cyrillic]=Кичик, мослаб бўлмайдиган ойна бошқарувчи +Comment[vi]=Trình quản lý cửa sổ nhỏ, không cấu hình được +Comment[wa]=On ptit, nén apontiåve, manaedjeu di purneas +Comment[zh_CN]=小巧的不可配置的窗口管理器 +Comment[zh_TW]=一個小型且不可組態的視窗管理者 diff --git a/kdm/kfrontend/sessions/wmaker.desktop b/kdm/kfrontend/sessions/wmaker.desktop new file mode 100644 index 000000000..fada3d3b5 --- /dev/null +++ b/kdm/kfrontend/sessions/wmaker.desktop @@ -0,0 +1,82 @@ +[Desktop Entry] +Type=XSession +Exec=wmaker +TryExec=wmaker +Name=WindowMaker +Name[bn]=উইণ্ডো-মেকার +Name[cy]=GwneuthuryddFfenestri (WindowMaker) +Name[eo]=Fenestroadministrilo +Name[hi]=विंडोमेकर +Name[ne]=सञ्झ्याल निर्माता +Name[pa]=ਝਰੋਖਾ-ਨਿਰਮਾਤਾ +Name[rw]=MukoraDirishya +Name[sv]=Windowmaker +Name[ta]=விண்டோஸ்மேக்கர் +Name[te]=విండొమెకర్ +Name[tg]=Созандаи тиреза +Comment=A simple window manager that resembles the NeXTStep look very closely +Comment[af]='n Eenvoudige venster bestuurder wat soos NeXTStep lyk +Comment[ar]=مدير نوافذ بسيط يمثّل مظهر NeXTStep بشكل قريب جداً +Comment[be]=Просты кіраўнік вокнаў, які вельмі дакладна паўтарае вонкавы выгляд NeXTStep +Comment[bn]=একটি উইণ্ডো ম্যানেজার যা ভীষণরকম NeXTStep-এর মত দেখতে +Comment[bs]=Jednostavan window manager koji vrlo dosljedno imitira NeXTStep izgled +Comment[ca]=Un gestor de finestres simple que s'assembla molt a l'aspecte de NeXTStep +Comment[cs]=Jendoduchý správce oken, který se velmi podobá NeXTStep +Comment[csb]=Prosti menedżer òknów szlachùjący za NeXTStep +Comment[cy]=Trefnydd ffenestri syml sy'n debyg iawn i'r golwg CamNesaf +Comment[da]=En simpel vindueshåndtering der ligner NeXTStep's udseende meget +Comment[de]=Einfacher Fenstermanager mit starker Ähnlichkeit zu NeXTStep +Comment[el]=Ένας απλός διαχειριστής παραθύρων που προσομοιώνει πολύ καλά το στυλ του NeXTStep +Comment[eo]=Simpla fenestroadministrilo +Comment[es]=Un gestor de ventanas sencillo cuyo aspecto se parece mucho al de NeXTStep +Comment[et]=Lihtne aknahaldur, mis meenutab väga tugevasti NeXTStepi +Comment[eu]=Leiho kudeatzaile sinplea, NeXTStep-en antz handia duena +Comment[fa]=یک مدیر پنجرۀ ساده که خیلی شبیه گام بعدی است +Comment[fi]=Yksinkertainen ikkunaohjelma, joka muistuttaa erittäin paljon NeXTStepiltä +Comment[fr]=Un gestionnaire de fenêtres simple qui ressemble assez précisement à NeXTStep +Comment[fy]=In ienfâldige finstersmanager dy it úterlik fan NeXTStep saer tichtby benaderd +Comment[gl]=Un xestor de fiestras sinxelo que se achega moito á apariencia de NeXTStep +Comment[he]=מנהל חלונות פשוט הדומה מאוד במראה שלו ל־NeXTStep +Comment[hi]=नेक्स्टस्टेप की तरह दिखने वाला सादा विंडो प्रबंधक +Comment[hr]=Jednostavan upravitelj prozora koji odražava vrlo blisko izgled NeXTStepa +Comment[hu]=Egy egyszerű ablakkezelő, megjelenése nagyon hasonlít a NeXTStephez +Comment[is]=Einfaldur gluggastjóri sem líkir vel eftir NeXTStep umhverfinu +Comment[it]=Un semplice window manager che assomiglia molto a NeXTStep. +Comment[ja]=NextStep にとてもよく似たシンプルなウィンドウマネージャ +Comment[ka]=NeXTStep -ის მაგვარი მარტივი ფანჯრის მენეჯერი +Comment[kk]=Қарапайым, NeXTStep-ке үқсас терезе менеджері +Comment[km]=កម្មវិធីគ្រប់គ្រងបង្អួចធម្មតាមួយ ដែលប្រហាក់ប្រហែលនឹងរូបរាង NeXTStep បំផុត +Comment[lt]=Paprasta langų tvarkyklė, išvaizda labai primenanti NeXTStep +Comment[lv]=Vienkāršs logu menedžeris, kas ir ļoti līdzīgs NeXTStep +Comment[mk]=Едноставен менаџер на прозорци кој е многу сличен на изгледот на NeXTStep +Comment[ms]=Pengurus tetingkap ringkas yang memasang NeXTStep dan menjadikannya tampak sangat hampir +Comment[mt]=Window manager li jixbaħ ħafna lil NextStep +Comment[nb]=En enkel vindusbehandler som ligner mye på NeXTStep +Comment[nds]=En eenfache Finsterpleger, de meist utsüht as NeXTStep +Comment[ne]=NeXTStep जस्तो देखिने साधारण सञ्झ्याल प्रबन्धक +Comment[nl]=Een eenvoudige windowmanager die het uiterlijk van NeXTStep zeer dicht benaderd +Comment[nn]=Ein enkel vindaugssjef som liknar mykje på NeXTStep +Comment[pa]=ਇੱਕ ਸਧਾਰਨ ਜੋ ਕਿ NeXTStep ਵਰਗਾ ਜਾਪਦਾ ਹੈ +Comment[pl]=Prosty menedżer okien przypominający bardzo wyglądem NeXTStep +Comment[pt]=Um gestor de janelas simples que faz lembrar bastante o visual do NeXTStep +Comment[pt_BR]=Um gerenciador de janelas simples, que lembra a aparência do NeXTStep +Comment[ro]=Un manager de ferestre simplu, care amintește foarte bine de aspectul NeXTStep +Comment[ru]=Простой оконный менеджер, воспроизводящий интерфейс NeXTStep +Comment[rw]=Mugenga Dirishya yoroheje ihuriza hamwe imboneko IntambweIkurikira byegeranye cyane +Comment[se]=Oktageardánis lásegieđahalli mii sulástahttá NeXTStep hui ollu +Comment[sk]=Jednoduchý správca okien, ktorý veľmi pripojíma NeXTStep +Comment[sl]=Preprost okenski upravitelj, ki zelo spominja na izgled NeXTStep +Comment[sr]=Једноставан менаџер прозора који одражава врло блиско изглед NeXTStep-а +Comment[sr@Latn]=Jednostavan menadžer prozora koji odražava vrlo blisko izgled NeXTStep-a +Comment[sv]=Enkel fönsterhanterare som mycket nära efterliknar Nextstep-utseendet +Comment[ta]=NeXTStep ஐ ஒத்த எளிய சாளர மேலாளார். +Comment[th]=ระบบจัดการหน้าต่างแบบเรียบง่าย ที่ดูคล้ายระบบ NeXTStep มากๆ +Comment[tr]=NeXTStep'e aşırı benzeyen basit bir masaüstü yöneticisi +Comment[tt]=NeXTStep küreneşendä ciñel täräzä-idäräçe +Comment[uk]=Простий менеджер вікон, що дуже нагадує NeXTStep +Comment[uz]=NeXTStep'ga juda oʻxshash oddiy oyna boshqaruvchi +Comment[uz@cyrillic]=NeXTStep'га жуда ўхшаш оддий ойна бошқарувчи +Comment[vi]=Trình quản lý cửa sổ giống với NeXTStep +Comment[wa]=On simpe manaedjeu di purneas avou l' foite rivnance di NeXTStep +Comment[zh_CN]=非常接近 NeXTStep 外观的简单窗口管理器 +Comment[zh_TW]=一個小型且與 NeXTStep 外觀很接近的視窗管理程式 diff --git a/kdm/kfrontend/sessions/xfce.desktop b/kdm/kfrontend/sessions/xfce.desktop new file mode 100644 index 000000000..6b199228b --- /dev/null +++ b/kdm/kfrontend/sessions/xfce.desktop @@ -0,0 +1,73 @@ +[Desktop Entry] +Type=XSession +Exec=xfwm +TryExec=xfwm +Name=XFce +Name[eo]=Facoj +Name[hi]=एक्सएफसीई +Name[sv]=Xfce +Name[te]=ఎక్స్ ఎఫ్ సి ఈ +Comment=The Cholesterol Free Desktop Environment. A desktop environment reminiscent of CDE +Comment[af]=Die Cholesterol Gratis Werkskerm Omgewing. 'n Werkskerm omgewing wat op CDE gebaseer is +Comment[ar]=بيئة سطح المكتب الخالي من الكوليسترول، بيئة سطح مكتب مليئة بذكريات CDE +Comment[be]=XFCE - Cholesterol Free Desktop Environment. Працоўнае асяроддзе, падобнае на CDE +Comment[bn]=দি কলেস্টরল ফ্রী ডেস্কটপ এনভায়রনমেন্ট। CDE-র কথা মনে করিয়ে দেয় এমন একটি ডেস্কটপ এনভায়রনমেন্ট +Comment[bs]=Cholesterol Free Desktop Environment. Desktop okolina nalik na CDE +Comment[ca]=El Cholesterol Free Desktop Environment. Un entorn d'escriptori amb reminiscències de CDE +Comment[cs]=Svobodné grafické prostředí neobsahující cholesterol. Prostředí připomínající CDE +Comment[csb]=Òkrãże pùltu szlachùjące za CDE +Comment[cy]=Yr Amgylchedd Penbwrdd Di-Golesterol. Amgylchedd penbwrdd sy'n atgoffaol o CDE +Comment[da]=Det kolesterolfrie desktopmiljø. Et desktopmiljø der minder om CDE +Comment[de]=Cholesterol Free Desktop Environment. Graphische Arbeitsumgebung, die an CDE erinnert +Comment[el]=Το Cholesterol Free Desktop Environment. Ένα περιβάλλον επιφάνειας εργασίας βασισμένο στο CDE +Comment[eo]=La libera labortablo ĉirkaŭajo +Comment[es]=El Cholesterol Free Desktop Environment, un entorno de escritorio que recuerda a CDE +Comment[et]=Kolesteroolivaba töölaua keskkond, mis meenutab mitmeti CDE-d +Comment[eu]=Kolesterolik gabeko mahaigain ingurunea. CDE gogorarazten duen mahaigaina +Comment[fa]=محیط رومیزی آزاد Cholesterol. یادآور محیط رومیزی CDE +Comment[fi]=Cholesterol Free -työpöytäympäristö. CDE:tä muistuttavatyöpöytäympäristö. +Comment[fr]=The Cholesterol Free Desktop Environment. Un environnement de bureau rappelant CDE +Comment[fy]=The Cholesterol Free Desktop Environment. In buroblêd omwrâld die tinken dat oan CDE +Comment[gl]=O Cholesterol Free Desktop Environment. Un entorno de escritório reminiscente de CDE +Comment[he]=The Cholesterol Free Desktop Environment. סביבת עבודה המזכירה את CDE +Comment[hi]=कॉलेस्ट्रॉल रहित डेस्कटॉप माहौल. एक डेस्कटॉप माहौल जो सीडीई जैसा है +Comment[hr]=Cholesterol Free Desktop Environment - Okruženje radne površine koje podsjeća na CDE +Comment[hu]=Cholesterol Free Desktop Environment, egy a CDE-re emlékeztető ablakkezelő +Comment[is]=Kólesterol-lausa skjáborðsumhverfið. Skjáborð sem líkist CDE +Comment[it]=Il Cholesterol Free Desktop Environment. Un desktop environment che ricorda CDE +Comment[ja]=Cholesterol Free Desktop Environment, CDE を思わせる、むだのないデスクトップ環境 +Comment[ka]=CDE-ს მაგვარი სამუშაო დაფა ქოლესტერინის გარეშე +Comment[kk]=Cholesterol Free Desktop Environment. CDE-ге үқсас графикалық орта +Comment[km]=Cholesterol Free Desktop Environment ។ បរិស្ថានផ្ទៃតុដែលសម្អាងលើ CDE +Comment[lt]=Darbatalio aplinka „Be cholesterolio“. CDE primenanti darbastalio aplinka +Comment[lv]=Darbvirsmas vide bez holesterīna. Darba virsmas vide, kas atgādina CDE +Comment[mk]=Cholesterol Free Desktop Environment. Работна околина која потсетува на CDE +Comment[ms]=Persekitaran Desktop Bebas Kolesterol. Mengenang kembali persekitaran desktop CDE +Comment[mt]=Cholesterol Free Desktop Environment. Ambjent grafiku li jixbaħ lis-CDE +Comment[nb]=Det kolesterolfrie skrivebordsmiljø. Et skrivebordsmiljø som minner om CDE +Comment[nds]=De "Cholesterol Free Desktop Environment". En Schriefdisch-Ümgeven, de wat liek is to CDE +Comment[ne]=कोलेस्ट्रोल रहित डेस्कटप परिवेश । CDE को स्मरणशील डेस्कटप परिवेश +Comment[nl]=The Cholesterol Free Desktop Environment. Een desktop environment die herinnert aan CDE +Comment[nn]=Cholesterol Free Desktop Environment. Eit skrivebordsmiljø som minner om CDE +Comment[pa]=ਚੋਲੀਸਟੀਰੋਲ ਮੁਫਤ ਵਾਤਾਵਰਣ, ਇੱਕ CDE ਵਰਗਾ ਵਾਤਾਵਰਣ +Comment[pl]=Środowisko pulpitu przypominające CDE +Comment[pt]=O Cholesterol Free Desktop Environment. Um ambiente de trabalho com vestígios do CDE +Comment[pt_BR]=Acrônimo para Cholesterol Free Desktop Environment (ou ambiente livre de colesterol), um ambiente de trabalho remanescente do CDE +Comment[ro]=Cholesterol Free Desktop Environment. Un mediu grafic cu reminescente din CDE +Comment[ru]="Не содержащая холестерина" рабочая среда, напоминающая CDE +Comment[rw]=Ibikikije Ibiro Ubuntu Kolesiterole. Ibikikije ibiro nkumburwa bya CDE +Comment[se]=The Cholesterol Free Desktop Environment. Čállinbeavdebiras mii muittuha CDE +Comment[sk]=The Cholesterol Free Desktop Environment. Pracovné prostredie pripomínajúce CDE +Comment[sl]=Cholesterol Free Desktop Environment. Namizno okolje, podobno okolju CDE +Comment[sr]=„The Cholesterol Free Desktop Environment“. Радно окружење које подсећа на CDE +Comment[sr@Latn]=„The Cholesterol Free Desktop Environment“. Radno okruženje koje podseća na CDE +Comment[sv]=Den kolesterolfria skrivbordsmiljön. En skrivbordsmiljö som påminner om CDE +Comment[ta]=கொலஸ்ட்ரால் இல்லாத மேல்மேசை சூழல். CDE பொருள்பொதிந்த மேல்மேசை சூழல் +Comment[th]=สภาพแวดล้อมสำหรับเดสก์ทอปไร้คอเลสเตอรอล เป็นสภาพแวดล้อมสำหรับเดสก์ทอปที่เหลือมาจาก CDE +Comment[tr]=Cholesterol Masaüstü Ortamı +Comment[tt]=Holesterol Bulmağan Östäl Möxite. CDE küreneşendä +Comment[uk]=The Cholesterol Free Desktop Environment. Графічне середовище, що нагадує CDE +Comment[vi]=Môi trường Màn hình nền Không có Cholesterol. Một môi trường màn hình nền gợi nhớ lại CDE +Comment[wa]=Li Libe Evironmint di Scribanne Colesterole (Cholesterol Free Desktop Environment). On evironmint d' sicribanne ki nos vént d' CDE +Comment[zh_CN]=胆固醇自由桌面环境。CDE 桌面环境的追随者 +Comment[zh_TW]=Cholesterol 免費桌面環境。一個另人懷念的 CDE 桌面環境 diff --git a/kdm/kfrontend/sessions/xfce4.desktop b/kdm/kfrontend/sessions/xfce4.desktop new file mode 100644 index 000000000..11b4097a6 --- /dev/null +++ b/kdm/kfrontend/sessions/xfce4.desktop @@ -0,0 +1,72 @@ +[Desktop Entry] +Type=XSession +Exec=startxfce4 +TryExec=startxfce4 +Name=XFce 4 +Name[eo]=Facoj 4 +Name[hi]=एक्सएफसीई4 +Name[sv]=Xfce 4 +Name[te]=ఎక్స్ ఎఫ్ సి ఈ 4 +Comment=The Cholesterol Free Desktop Environment, version 4. A desktop environment reminiscent of CDE +Comment[af]=Die Cholesterol Gratis Werkskerm Omgewing, weergawe 4. 'n Werkskerm omgewing wat op CDE gebaseer is +Comment[be]=XFCE4 - Cholesterol Free Desktop Environment, version 4. Працоўнае асяроддзе, падобнае на CDE +Comment[bn]=দি কলেস্টরল ফ্রী ডেস্কটপ এনভায়রনমেন্ট, ৪র্থ সংস্করণ। CDE-র কথা মনে করিয়ে দেয় এমন একটি ডেস্কটপ এনভায়রনমেন্ট +Comment[bs]=Cholesterol Free Desktop Environment. Desktop okolina nalik na CDE +Comment[ca]=L'entorn d'escriptori sense colesterol, versió 4. Un entorn d'escriptori que recorda a CDE +Comment[cs]=Svobodné grafické prostředí neobsahující cholesterol verze 4. Prostředí připomínající CDE +Comment[csb]=Cholesterol Free Desktop Environment, wersëjô 4 - graficzné òkrãże szlachùjące za CDE +Comment[cy]=Yr Amgylchedd Penbwrdd Di-Golesterol, fersiwn 4. Amgylchedd penbwrdd sy'n atgoffaol o CDE +Comment[da]=Det kolesterolfrie desktopmiljø, version 4. Et desktopmiljø der minder om CDE +Comment[de]=Cholesterol Free Desktop Environment, Version 4. Benutzerumgebung in der Art von CDE +Comment[el]=Το Cholesterol Free Desktop Environment, έκδοση 4. Ένα περιβάλλον επιφάνειας εργασίας βασισμένο στο CDE +Comment[eo]=La libera labortablo ĉirkaŭajo 4 +Comment[es]=El Cholesterol Free Desktop Environment, versión 4. Un entorno de escritorio que recuerda a CDE +Comment[et]=Kolesteroolivaba töölaua keskkond (versioon 4), mis meenutab mitmeti CDE-d +Comment[eu]=Kolesterolik gabeko mahaigain ingurunea, 4 bertsioa. CDE gogorarazten duen mahaigaina +Comment[fa]=محیط رومیزی آزاد Cholesterol، نسخه ۴. یادآور محیط رومیزی CDE +Comment[fi]=Cholesterol Free -työpöytäympäristö. CDE:tä muistuttavatyöpöytäympäristö. +Comment[fr]=The Cholesterol Free Desktop Environment, version 4. Un environnement de bureau rappelant CDE +Comment[fy]=De Cholesterol Free Desktop Environment, ferzje 4. In buroblêd omwrâld die tinken dat oan CDE +Comment[gl]=O Cholesterol Free Desktop Environment, versión 4. Un entorno de escritório reminiscencia de CDE +Comment[he]=The Cholesterol Free Desktop Environment. גרסה 4, סביבת עבודה המזכירה את CDE +Comment[hi]=कोलेस्ट्रॉल मुक्त डेस्कटॉप वातावरण, संस्करण 4. सीडीई की याद दिलाता एक डेस्कटॉप वातावरण +Comment[hr]=Cholesterol Free Desktop Environment verzija 4 - Okruženje radne površine koje podsjeća na CDE +Comment[hu]=The Cholesterol Free Desktop Environment, 4-es verzió. Egy CDE-szerű ablakkezelő +Comment[is]=Kólesterol-lausa skjáborðsumhverfið, útgáfa 4. Skjáborð sem líkist CDE +Comment[it]=Il Cholesterol Free Desktop Environment, versione 4. Un desktop environment che ricorda CDE +Comment[ja]=Cholesterol Free Desktop Environment, version 4, CDE を思わせる、むだのないデスクトップ環境 +Comment[ka]=CDE-ს მაგვარი უქოლესტერინო სამუშაო დაფა xfce 4 +Comment[kk]=Cholesterol Free Desktop Environment, 4-нұсқа. CDE-ге ұқсас графикалық орта +Comment[km]=Cholesterol Free Desktop Environment កំណែ 4 ។ បរិស្ថានផ្ទៃតុដែលសម្អាងលើ CDE +Comment[lt]=Darbatalio aplinka „Be cholesterolio“ , 4 versija. CDE primenanti darbastalio aplinka +Comment[lv]=Darbvirsmas vide bez holesterīna, versija 4. Darbavirsmas vide, kas atgādina CDE +Comment[mk]=Cholesterol Free Desktop Environment, верзија 4. Работна околина која потсетува на CDE +Comment[ms]=Persekitaran Desktop Bebas Kolesterol, versi 4. Mengingatkan kembali persekitaran desktop CDE +Comment[mt]=Cholesterol Free Desktop Environment (v4). Ambjent grafiku li jixbaħ lis-CDE +Comment[nb]=Det kolesterolfrie skrivebordet, versjon 4. Et skrivebordsmiljø som minner om CDE +Comment[nds]=De "Cholesterol Free Desktop Environment", Verschoon 4. En Schriefdisch-Ümgeven, de wat liek is to CDE +Comment[ne]=कोलेस्ट्रोल रहित डेस्कटप परिवेश, संस्करण ४ । CDE को स्मरणशील डेस्कटप परिवेश +Comment[nl]=De Cholesterol Free Desktop Environment, versie 4. Een desktop environment die herinnert aan CDE +Comment[nn]=Cholesterol Free Desktop Environment. Eit skrivebordsmiljø som minner om CDE +Comment[pa]=ਚੋਲੀਸਟੀਰੋਲ ਮੁਫਤ ਵਾਤਾਵਰਣ, ਵਰਜਨ 4 ਇੱਕ CDE ਵਰਗਾ ਵਾਤਾਵਰਣ +Comment[pl]=Cholesterol Free Desktop Environment, wersja 4 - środowisko graficzne podobne do CDE. +Comment[pt]=O Cholesterol Free Desktop Environment, versão 4. Um ambiente de trabalho com vestígios do CDE +Comment[pt_BR]=Acrônimo para Cholesterol Free Desktop Environment (ou ambiente livre de colesterol), versão 4; um ambiente de trabalho remanescente do CDE +Comment[ro]=Cholesterol Free Desktop Environment, versiunea 4. Un mediu grafic cu reminescente din CDE +Comment[ru]="Не содержащая холестерина" рабочая среда xfce версии 4, напоминающая CDE +Comment[rw]=Ibikikije Ibiro Ubuntu Kolesiterole, verisiyo 4. Ibikikije ibiro nkumburwa bya CDE +Comment[se]=The Cholesterol Free Desktop Environment, version 4. Čállinbeavdebiras mii muittuha CDE +Comment[sk]=The Cholesterol Free Desktop Environment verzia 4. Pracovné prostredie pripomínajúce CDE +Comment[sl]=Cholesterol Free Desktop Environment, različica 4. Namizno okolje, podobno okolju CDE +Comment[sr]=„The Cholesterol Free Desktop Environment“ 4. издање. Радно окружење које подсећа на CDE +Comment[sr@Latn]=„The Cholesterol Free Desktop Environment“ 4. izdanje. Radno okruženje koje podseća na CDE +Comment[sv]=Den kolesterolfria skrivbordsmiljön, version 4. En skrivbordsmiljö som påminner om CDE +Comment[ta]=கொலஸ்ட்ரால் இல்லாத மேல்மேசை சூழல். பதிப்பு 4. CDE பொருள்பொதிந்த மேல்மேசை சூழல் +Comment[th]=สภาพแวดล้อมสำหรับเดสก์ทอปแบบไร้คอเลสเตอรอล เวอร์ชั่น 4 เป็นสภาพแวดล้อมสำหรับเดสก์ทอปที่เหลือมาจาก CDE +Comment[tr]=Cholesterol Ücretsiz Masaüstü Ortamı, sürüm 4. CDE'nin benzeri olan masaüstü ortamı +Comment[tt]=Holesterol Bulmağan Östäl Möxiteneñ 4. söreme. CDE küreneşendä +Comment[uk]=The Cholesterol Free Desktop Environment, версія 4. Графічне середовище, що нагадує CDE +Comment[vi]=Môi trường Màn hình nền Không có Cholesterol, phiên bản 4. Một môi trường màn hình nền gợi nhớ lại CDE +Comment[wa]=Li Libe Evironmint di Scribanne Colesterole (Cholesterol Free Desktop Environment), modêye 4. On evironmint d' sicribanne ki nos vént d' CDE +Comment[zh_CN]=胆固醇自由桌面环境,版本 4。CDE 桌面环境的追随者 +Comment[zh_TW]=Cholesterol 免費桌面環境,第 4 版。一個另人懷念的 CDE 桌面環境 diff --git a/kdm/kfrontend/themer/Makefile.am b/kdm/kfrontend/themer/Makefile.am new file mode 100644 index 000000000..7f6eb5701 --- /dev/null +++ b/kdm/kfrontend/themer/Makefile.am @@ -0,0 +1,16 @@ +AM_CPPFLAGS = -I$(srcdir)/../../backend -I$(srcdir)/.. -I../.. \ + -I$(top_srcdir)/kdmlib \ + $(all_includes) + +noinst_LIBRARIES = libkdmthemer.a +libkdmthemer_a_SOURCES = \ + kdmthemer.cpp \ + kdmitem.cpp \ + kdmpixmap.cpp \ + kdmrect.cpp \ + kdmlabel.cpp \ + kdmlayout.cpp + +METASOURCES = AUTO + +libkdmthemer_a_COMPILE_FIRST = ../../config.ci diff --git a/kdm/kfrontend/themer/kdmitem.cpp b/kdm/kfrontend/themer/kdmitem.cpp new file mode 100644 index 000000000..48c0d1faf --- /dev/null +++ b/kdm/kfrontend/themer/kdmitem.cpp @@ -0,0 +1,532 @@ +/* + * Copyright (C) 2003 by Unai Garro <[email protected]> + * Copyright (C) 2004 by Enrico Ros <[email protected]> + * Copyright (C) 2004 by Stephan Kulow <[email protected]> + * Copyright (C) 2004 by Oswald Buddenhagen <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * Generic Kdm Item + */ + +//#define DRAW_OUTLINE 1 // for debugging only + +#include "kdmitem.h" +#include "kdmlayout.h" + +#include <kglobal.h> +#include <kdebug.h> + +#include <qframe.h> +#include <qwidget.h> +#include <qlayout.h> +#include <qimage.h> +#ifdef DRAW_OUTLINE +# include <qpainter.h> +#endif + +KdmItem::KdmItem( KdmItem *parent, const QDomNode &node, const char *name ) + : QObject( parent, name ) + , boxManager( 0 ) + , fixedManager( 0 ) + , image( 0 ) + , myWidget( 0 ) + , myLayoutItem( 0 ) + , buttonParent( 0 ) +{ + // Set default layout for every item + currentManager = MNone; + pos.x = pos.y = 0; + pos.width = pos.height = 1; + pos.xType = pos.yType = pos.wType = pos.hType = DTnone; + pos.anchor = "nw"; + + isShown = InitialHidden; + + // Set defaults for derived item's properties + properties.incrementalPaint = false; + state = Snormal; + + // The "toplevel" node (the screen) is really just like a fixed node + if (!parent || !parent->inherits( "KdmItem" )) { + setFixedLayout(); + return; + } + // Read the mandatory Pos tag. Other tags such as normal, prelighted, + // etc.. are read under specific implementations. + QDomNodeList childList = node.childNodes(); + for (uint nod = 0; nod < childList.count(); nod++) { + QDomNode child = childList.item( nod ); + QDomElement el = child.toElement(); + QString tagName = el.tagName(), attr; + + if (tagName == "pos") { + parseAttribute( el.attribute( "x", QString::null ), pos.x, pos.xType ); + parseAttribute( el.attribute( "y", QString::null ), pos.y, pos.yType ); + parseAttribute( el.attribute( "width", QString::null ), pos.width, pos.wType ); + parseAttribute( el.attribute( "height", QString::null ), pos.height, pos.hType ); + pos.anchor = el.attribute( "anchor", "nw" ); + } + } + + QDomNode tnode = node; + id = tnode.toElement().attribute( "id", QString::number( (ulong)this, 16 ) ); + + // Tell 'parent' to add 'me' to its children + KdmItem *parentItem = static_cast<KdmItem *>( parent ); + parentItem->addChildItem( this ); +} + +KdmItem::~KdmItem() +{ + delete boxManager; + delete fixedManager; + delete image; +} + +void +KdmItem::update() +{ +} + +void +KdmItem::needUpdate() +{ + emit needUpdate( area.x(), area.y(), area.width(), area.height() ); +} + +void +KdmItem::show( bool force ) +{ + if (isShown != InitialHidden && !force) + return; + + QValueList<KdmItem *>::Iterator it; + for (it = m_children.begin(); it != m_children.end(); ++it) + (*it)->show(); + + isShown = Shown; + + if (myWidget) + myWidget->show(); + // XXX showing of layouts not implemented, prolly pointless anyway + + needUpdate(); +} + +void +KdmItem::hide( bool force ) +{ + if (isShown == ExplicitlyHidden) + return; + + if (isShown == InitialHidden && force) { + isShown = ExplicitlyHidden; + return; // no need for further action + } + + QValueList<KdmItem *>::Iterator it; + for (it = m_children.begin(); it != m_children.end(); ++it) + (*it)->hide(); + + isShown = force ? ExplicitlyHidden : InitialHidden; + + if (myWidget) + myWidget->hide(); + // XXX hiding of layouts not implemented, prolly pointless anyway + + needUpdate(); +} + +void +KdmItem::inheritFromButton( KdmItem *button ) +{ + if (button) + buttonParent = button; + + QValueList<KdmItem *>::Iterator it; + for (it = m_children.begin(); it != m_children.end(); ++it) + (*it)->inheritFromButton( button ); +} + +KdmItem * +KdmItem::findNode( const QString &_id ) const +{ + if (id == _id) + return const_cast<KdmItem *>( this ); + + QValueList<KdmItem *>::ConstIterator it; + for (it = m_children.begin(); it != m_children.end(); ++it) { + KdmItem *t = (*it)->findNode( _id ); + if (t) + return t; + } + + return 0; +} + +void +KdmItem::setWidget( QWidget *widget ) +{ +// delete myWidget; -- themer->widget() owns the widgets + + myWidget = widget; + if (isHidden()) + myWidget->hide(); + else + myWidget->show(); + + // Remove borders so that it blends nicely with the theme background + QFrame* frame = ::qt_cast<QFrame *>( widget ); + if (frame) + frame->setFrameStyle( QFrame::NoFrame ); + + myWidget->setGeometry(area); + + connect( myWidget, SIGNAL(destroyed()), SLOT(widgetGone()) ); +} + +void +KdmItem::widgetGone() +{ + myWidget = 0; +} + +void +KdmItem::setLayoutItem( QLayoutItem *item ) +{ + myLayoutItem = item; + // XXX hiding not supported - it think it's pointless here + if (myLayoutItem->widget()) + connect( myLayoutItem->widget(), SIGNAL(destroyed()), + SLOT(layoutItemGone()) ); + else if (myLayoutItem->layout()) + connect( myLayoutItem->layout(), SIGNAL(destroyed()), + SLOT(layoutItemGone()) ); +} + +void +KdmItem::layoutItemGone() +{ + myLayoutItem = 0; +} + +/* This is called as a result of KdmLayout::update, and directly on the root */ +void +KdmItem::setGeometry( const QRect &newGeometry, bool force ) +{ + kdDebug() << " KdmItem::setGeometry " << id << newGeometry << endl; + // check if already 'in place' + if (!force && area == newGeometry) + return; + + area = newGeometry; + + if (myWidget) + myWidget->setGeometry( newGeometry ); + if (myLayoutItem) + myLayoutItem->setGeometry( newGeometry ); + + // recurr to all boxed children + if (boxManager && !boxManager->isEmpty()) + boxManager->update( newGeometry, force ); + + // recurr to all fixed children + if (fixedManager && !fixedManager->isEmpty()) + fixedManager->update( newGeometry, force ); + + // TODO send *selective* repaint signal +} + +void +KdmItem::paint( QPainter *p, const QRect &rect ) +{ + if (isHidden()) + return; + + if (myWidget || (myLayoutItem && myLayoutItem->widget())) + return; + + if (area.intersects( rect )) { + QRect contentsRect = area.intersect( rect ); + contentsRect.moveBy( QMIN( 0, -area.x() ), QMIN( 0, -area.y() ) ); + drawContents( p, contentsRect ); + } + +#ifdef DRAW_OUTLINE + // Draw bounding rect for this item + p->setPen( Qt::white ); + p->drawRect( area ); +#endif + + if (myLayoutItem) + return; + + // Dispatch paint events to children + QValueList<KdmItem *>::Iterator it; + for (it = m_children.begin(); it != m_children.end(); ++it) + (*it)->paint( p, rect ); +} + +KdmItem *KdmItem::currentActive = 0; + +void +KdmItem::mouseEvent( int x, int y, bool pressed, bool released ) +{ + if (buttonParent && buttonParent != this) { + buttonParent->mouseEvent( x, y, pressed, released ); + return; + } + + ItemState oldState = state; + if (area.contains( x, y )) { + if (released && oldState == Sactive) { + if (buttonParent) + emit activated( id ); + state = Sprelight; + currentActive = 0; + } else if (pressed || currentActive == this) { + state = Sactive; + currentActive = this; + } else if (!currentActive) + state = Sprelight; + else + state = Snormal; + } else { + if (released) + currentActive = 0; + if (currentActive == this) + state = Sprelight; + else + state = Snormal; + } + + if (!buttonParent) { + QValueList<KdmItem *>::Iterator it; + for (it = m_children.begin(); it != m_children.end(); ++it) + (*it)->mouseEvent( x, y, pressed, released ); + } + + if (oldState != state) + statusChanged(); +} + +void +KdmItem::statusChanged() +{ + if (buttonParent == this) { + QValueList<KdmItem *>::Iterator it; + for (it = m_children.begin(); it != m_children.end(); ++it) { + (*it)->state = state; + (*it)->statusChanged(); + } + } +} + +// BEGIN protected inheritable + +QSize +KdmItem::sizeHint() +{ + if (myWidget) + return myWidget->size(); + if (myLayoutItem) + return myLayoutItem->sizeHint(); + int w = pos.wType == DTpixel ? kAbs( pos.width ) : -1, + h = pos.hType == DTpixel ? kAbs( pos.height ) : -1; + return QSize( w, h ); +} + +QRect +KdmItem::placementHint( const QRect &parentRect ) +{ + QSize hintedSize = sizeHint(); + QSize boxHint; + + int x = parentRect.left(), + y = parentRect.top(), + w = parentRect.width(), + h = parentRect.height(); + + kdDebug() << "KdmItem::placementHint parentRect=" << id << parentRect << " hintedSize=" << hintedSize << endl; + // check if width or height are set to "box" + if (pos.wType == DTbox || pos.hType == DTbox) { + if (myLayoutItem || myWidget) + boxHint = hintedSize; + else { + if (!boxManager) + return parentRect; + boxHint = boxManager->sizeHint(); + } + kdDebug() << " => boxHint " << boxHint << endl; + } + + if (pos.xType == DTpixel) + x += pos.x; + else if (pos.xType == DTnpixel) + x = parentRect.right() - pos.x; + else if (pos.xType == DTpercent) + x += int( parentRect.width() / 100.0 * pos.x ); + + if (pos.yType == DTpixel) + y += pos.y; + else if (pos.yType == DTnpixel) + y = parentRect.bottom() - pos.y; + else if (pos.yType == DTpercent) + y += int( parentRect.height() / 100.0 * pos.y ); + + if (pos.wType == DTpixel) + w = pos.width; + else if (pos.wType == DTnpixel) + w -= pos.width; + else if (pos.wType == DTpercent) + w = int( parentRect.width() / 100.0 * pos.width ); + else if (pos.wType == DTbox) + w = boxHint.width(); + else if (hintedSize.width() > 0) + w = hintedSize.width(); + else + w = 0; + + if (pos.hType == DTpixel) + h = pos.height; + else if (pos.hType == DTnpixel) + h -= pos.height; + else if (pos.hType == DTpercent) + h = int( parentRect.height() / 100.0 * pos.height ); + else if (pos.hType == DTbox) + h = boxHint.height(); + else if (hintedSize.height() > 0) + h = hintedSize.height(); + else + h = 0; + + // defaults to center + int dx = -w / 2, dy = -h / 2; + + // anchor the rect to an edge / corner + if (pos.anchor.length() > 0 && pos.anchor.length() < 3) { + if (pos.anchor.find( 'n' ) >= 0) + dy = 0; + if (pos.anchor.find( 's' ) >= 0) + dy = -h; + if (pos.anchor.find( 'w' ) >= 0) + dx = 0; + if (pos.anchor.find( 'e' ) >= 0) + dx = -w; + } + // KdmItem *p = static_cast<KdmItem*>( parent() ); + kdDebug() << "KdmItem::placementHint " << id << " x=" << x << " dx=" << dx << " w=" << w << " y=" << y << " dy=" << dy << " h=" << h << " " << parentRect << endl; + y += dy; + x += dx; + + // Note: no clipping to parent because this broke many themes! + return QRect( x, y, w, h ); +} + +// END protected inheritable + + +void +KdmItem::addChildItem( KdmItem *item ) +{ + m_children.append( item ); + switch (currentManager) { + case MNone: // fallback to the 'fixed' case + setFixedLayout(); + case MFixed: + fixedManager->addItem( item ); + break; + case MBox: + boxManager->addItem( item ); + break; + } + + // signal bounce from child to parent + connect( item, SIGNAL(needUpdate( int, int, int, int )), SIGNAL(needUpdate( int, int, int, int )) ); + connect( item, SIGNAL(activated( const QString & )), SIGNAL(activated( const QString & )) ); +} + +void +KdmItem::parseAttribute( const QString &s, int &val, enum DataType &dType ) +{ + if (s.isEmpty()) + return; + + int p; + if (s == "box") { // box value + dType = DTbox; + val = 0; + } else if ((p = s.find( '%' )) >= 0) { // percent value + dType = DTpercent; + QString sCopy = s; + sCopy.remove( p, 1 ); + sCopy.replace( ',', '.' ); + val = (int)sCopy.toDouble(); + } else { // int value + dType = DTpixel; + QString sCopy = s; + if (sCopy.at( 0 ) == '-') { + sCopy.remove( 0, 1 ); + dType = DTnpixel; + } + sCopy.replace( ',', '.' ); + val = (int)sCopy.toDouble(); + } +} + +void +KdmItem::parseFont( const QString &s, QFont &font ) +{ + int splitAt = s.findRev( ' ' ); + if (splitAt < 1) + return; + font.setFamily( s.left( splitAt ) ); + int fontSize = s.mid( splitAt + 1 ).toInt(); + if (fontSize > 1) + font.setPointSize( fontSize ); +} + +void +KdmItem::parseColor( const QString &s, QColor &color ) +{ + if (s.at( 0 ) != '#') + return; + bool ok; + QString sCopy = s; + int hexColor = sCopy.remove( 0, 1 ).toInt( &ok, 16 ); + if (ok) + color.setRgb( hexColor ); +} + +void +KdmItem::setBoxLayout( const QDomNode &node ) +{ + if (!boxManager) + boxManager = new KdmLayoutBox( node ); + currentManager = MBox; +} + +void +KdmItem::setFixedLayout( const QDomNode &node ) +{ + if (!fixedManager) + fixedManager = new KdmLayoutFixed( node ); + currentManager = MFixed; +} + +#include "kdmitem.moc" diff --git a/kdm/kfrontend/themer/kdmitem.h b/kdm/kfrontend/themer/kdmitem.h new file mode 100644 index 000000000..66feedd02 --- /dev/null +++ b/kdm/kfrontend/themer/kdmitem.h @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2003 by Unai Garro <[email protected]> + * Copyright (C) 2004 by Enrico Ros <[email protected]> + * Copyright (C) 2004 by Stephan Kulow <[email protected]> + * Copyright (C) 2004 by Oswald Buddenhagen <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KDMITEM_H +#define KDMITEM_H + +#include <qobject.h> +#include <qvaluelist.h> +#include <qrect.h> +#include <qdom.h> + +class KdmItem; +class KdmLayoutBox; +class KdmLayoutFixed; + +class QPainter; +class QLayoutItem; + +/** class KdmItem + * @short Base class for every kdmthemes' element. + * + * This class provides methods for arranging it and its children to the + * screen (see note below), painting the whole area or a sub-region using + * an opened painter, handling mouse events or events in general dispatching + * them to children and sending some signals to the root (for example on + * mouse click). + * + * KdmItem sits in a hierarchical top to bottom tree with signals that + * traverse the tree back from leafs (or inner nodes) to the root. + * + * To implement a KdmItem only a few virtual protected methods must be + * reimplemented, other virtual functions are there for convenience only - + * the default implementation should satisfy your needs. + */ + +/** + * A note on layouting - how does it work? + * - setgeometry is called by parent (passing the new geometry) + * - item changes its geometry + * - if item embeds a widget, reposition it too + * - call children's box manager. box->update( my geom ) + * - sum up the whole space taken by children (via *hint calls) if + * needed for box width / height computation. note that the computed + * geometry should be equal or similar to parent's geometry. + * - pad the rectangle bounding box' contents + * - for every child + * - if vertical + * ( use a top-to-bottom insertion, spacing insertion lines by + * children's individual height ) + * - set up a zero height Parent (placed at the insertion line's + * position) and get Geom = child->placementHint( p ) + * - set up child's Size using Parent's width and Geom's height. + * - call to child->setGeometry( Parent.topLeft, Size ) + * - if horizontal + * - flows like the vertical one but uses a left-to-right insertion + * and insertion entry points are vertical lines + * - call to children's fix manager. fixed->update( my geom ) + * - for every child + * - S = get child's geometry hint (and we'll give item the whole + * space it needs, without constraints) + * - call to child->setGeometry( S ) + * - TODO: send a selective redraw signal also merging children's areas + */ + +class KdmItem : public QObject { + Q_OBJECT + + friend class KdmThemer; + +public: + /** + * Item constructor and destructor + */ + KdmItem( KdmItem *parent, const QDomNode &node = QDomNode(), const char *name = 0 ); + virtual ~KdmItem(); + + /** + * Fixup the geometry of an item and its children (even if fixed + * or boxed ones). Note that this will generate repaint signals + * when needed. The default implementation should fit all needs. + */ + virtual void setGeometry( const QRect &newGeometry, bool force ); + + /** + * Paint the item and its children using the given painter. + * This is the compositing core function. It buffers paint operations + * to speed up rendering of dynamic objects. + */ + void paint( QPainter *painter, const QRect &boundaries ); + + /** + * Update representation of contents and repaint. + */ + virtual void update(); + + /** + * Handle mouse motion and dispatch events to children. This + * leads to items prelighting, activation() on click and more.. + */ + void mouseEvent( int x, int y, bool pressed = false, bool released = false ); + + /** + * Similar to sizeHint(..), this returns the area of the item + * given the @p parentGeometry. The default implementation + * takes into account geometric constraints and layoutings. + * @param parentGeometry the geometry of the caller item or a + * null rect if the geometry of the parent is not yet defined. + */ + virtual QRect placementHint( const QRect &parentGeometry ); + + /** + * Create the box layout manager; next children will be + * managed by the box layouter + */ + void setBoxLayout( const QDomNode &node = QDomNode() ); + + /** + * Create the fixed layout manager; next children will be + * in fixed position relative to this item + */ + void setFixedLayout( const QDomNode &node = QDomNode() ); + + QString type() const { return itemType; } + void setType( const QString &t ) { itemType = t; } + void setBaseDir( const QString &bd ) { basedir = bd; } + + QString baseDir() const + { + if (basedir.isEmpty() && parent()) + return static_cast<KdmItem *>( parent()->qt_cast( "KdmItem" ) )->baseDir(); + return basedir; + } + + KdmItem *findNode( const QString &id ) const; + virtual void setWidget( QWidget *widget ); + virtual void setLayoutItem( QLayoutItem *item ); + + virtual void hide( bool force = false ); + virtual void show( bool force = false ); + + bool isHidden() const { return isShown != Shown; } + bool isExplicitlyHidden() const { return isShown == ExplicitlyHidden; } + QRect rect() const { return area; } + +signals: + void needUpdate( int x, int y, int w, int h ); + void activated( const QString &id ); + +protected slots: + void widgetGone(); + void layoutItemGone(); + +protected: + /** + * Returns the optimal/minimal size for this item. + * This should be reimplemented in items like label and pixmap. + * @return (-1,-1) if no size can be determined (so it should + * default to parent's size). + */ + virtual QSize sizeHint(); + + /** + * Low level graphical function to paint the item. + * All items must reimplement this function to draw themeselves + * (or a part of) into the @p image keeping inside the @p rect . + * Try to do this as fast as possible. + * @param painter the painter to draw the item with + * @param region the part of the the image to render + */ + virtual void drawContents( QPainter *painter, const QRect ®ion ) = 0; + + /** + * Called when item changes its 'state' variable. This must + * handle item's repaint. + */ + virtual void statusChanged(); + + /** + * emits needUpdate( int, int, int, int ) with the full widget area. + */ + void needUpdate(); + + // This enum identifies in which state the item is + enum ItemState { Snormal, Sactive, Sprelight } state; + + static KdmItem *currentActive; + + // This struct can be filled in by derived items + struct { + bool incrementalPaint; + } properties; + + // This is the placement of the item + QRect area; + + // This struct is filled in by KdmItem base class + enum DataType { DTnone, DTpixel, DTnpixel, DTpercent, DTbox }; + struct { + enum DataType xType, yType, wType, hType; + int x; + int y; + int width; + int height; + QString anchor; + } pos; + + /* For internal use ONLY + * Add a child item. This function is called automatically + * when constructing an @p item with this as the parent. + */ + void addChildItem( KdmItem *item ); + + /* For internal use ONLY + * Parse type and value of an attribute (pos tag), a font or a + * color. + */ + void parseAttribute( const QString &, int &, enum DataType & ); + void parseFont( const QString &, QFont & ); + void parseColor( const QString &, QColor & ); + + void inheritFromButton( KdmItem *button ); + + QString itemType, id; + QValueList<KdmItem *> m_children; + + // Layouting related variables + enum { MNone = 0, MFixed = 1, MBox = 2 } currentManager; + KdmLayoutBox *boxManager; + KdmLayoutFixed *fixedManager; + + // Compositing related variables + QImage *image; + + // defines the directory the theme is in (may be present in the parent) + QString basedir; + + QWidget *myWidget; + QLayoutItem *myLayoutItem; + + enum { InitialHidden, ExplicitlyHidden, Shown } isShown; + + KdmItem *buttonParent; +}; + +#endif diff --git a/kdm/kfrontend/themer/kdmlabel.cpp b/kdm/kfrontend/themer/kdmlabel.cpp new file mode 100644 index 000000000..41d7e4254 --- /dev/null +++ b/kdm/kfrontend/themer/kdmlabel.cpp @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2003 by Unai Garro <[email protected]> + * Copyright (C) 2004 by Enrico Ros <[email protected]> + * Copyright (C) 2004 by Stephan Kulow <[email protected]> + * Copyright (C) 2004 by Oswald Buddenhagen <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kdmlabel.h" +#include <kgreeter.h> + +#include <kglobal.h> +#include <klocale.h> +#include <kmacroexpander.h> +#include <kdebug.h> + +#include <qdatetime.h> +#include <qpainter.h> +#include <qfontmetrics.h> +#include <qtimer.h> + +#include <unistd.h> +#include <sys/utsname.h> +#if !defined(HAVE_GETDOMAINNAME) && defined(HAVE_SYSINFO) +# include <sys/systeminfo.h> +#endif + +KdmLabel::KdmLabel( KdmItem *parent, const QDomNode &node, const char *name ) + : KdmItem( parent, node, name ) +{ + itemType = "label"; + + // Set default values for label (note: strings are already Null) + label.active.color.setRgb( 0xFFFFFF ); + label.active.present = false; + label.prelight.present = false; + label.maximumWidth = -1; + + const QString locale = KGlobal::locale()->language(); + + // Read LABEL ID + QDomNode n = node; + QDomElement elLab = n.toElement(); + // ID types: clock, pam-error, pam-message, pam-prompt, + // pam-warning, timed-label + label.id = elLab.attribute( "id", "" ); + label.hasId = !(label.id).isEmpty(); + + // Read LABEL TAGS + QDomNodeList childList = node.childNodes(); + bool stockUsed = false; + for (uint nod = 0; nod < childList.count(); nod++) { + QDomNode child = childList.item( nod ); + QDomElement el = child.toElement(); + QString tagName = el.tagName(); + + if (tagName == "pos") + label.maximumWidth = el.attribute( "max-width", "-1" ).toInt(); + else if (tagName == "normal") { + parseColor( el.attribute( "color", "#ffffff" ), label.normal.color ); + parseFont( el.attribute( "font", "Sans 14" ), label.normal.font ); + } else if (tagName == "active") { + label.active.present = true; + parseColor( el.attribute( "color", "#ffffff" ), label.active.color ); + parseFont( el.attribute( "font", "Sans 14" ), label.active.font ); + } else if (tagName == "prelight") { + label.prelight.present = true; + parseColor( el.attribute( "color", "#ffffff" ), label.prelight.color ); + parseFont( el.attribute( "font", "Sans 14" ), label.prelight.font ); + } else if (tagName == "text" && el.attributes().count() == 0 && !stockUsed) { + label.text = el.text(); + } else if (tagName == "text" && !stockUsed) { + QString lang = el.attribute( "xml:lang", "" ); + if (lang == locale) + label.text = el.text(); + } else if (tagName == "stock") { + label.text = lookupStock( el.attribute( "type", "" ) ); + stockUsed = true; + } + } + + // Check if this is a timer label + label.isTimer = label.text.find( "%c" ) >= 0; + if (label.isTimer) { + timer = new QTimer( this ); + timer->start( 1000 ); + connect( timer, SIGNAL(timeout()), SLOT(update()) ); + } + cText = lookupText( label.text ); +} + +void +KdmLabel::setText( const QString &txt ) +{ + label.text = txt; + update(); +} + +QSize +KdmLabel::sizeHint() +{ + // choose the correct label class + struct LabelStruct::LabelClass *l = &label.normal; + if (state == Sactive && label.active.present) + l = &label.active; + else if (state == Sprelight && label.prelight.present) + l = &label.prelight; + // get the hint from font metrics + QSize hint = QFontMetrics( l->font ).size( AlignLeft | SingleLine, cText ); + // clip the result using the max-width label(pos) parameter + if (label.maximumWidth > 0 && hint.width() > label.maximumWidth) + hint.setWidth( label.maximumWidth ); + return hint; +} + +void +KdmLabel::drawContents( QPainter *p, const QRect &/*r*/ ) +{ + // choose the correct label class + struct LabelStruct::LabelClass *l = &label.normal; + if (state == Sactive && label.active.present) + l = &label.active; + else if (state == Sprelight && label.prelight.present) + l = &label.prelight; + // draw the label + p->setFont( l->font ); + p->setPen( l->color ); + //TODO paint clipped (tested but not working..) + p->drawText( area, AlignLeft | SingleLine, cText ); +} + +void +KdmLabel::statusChanged() +{ + KdmItem::statusChanged(); + if (!label.active.present && !label.prelight.present) + return; + if ((state == Sprelight && !label.prelight.present) || + (state == Sactive && !label.active.present)) + return; + needUpdate(); +} + +void +KdmLabel::update() +{ + QString text = lookupText( label.text ); + if (text != cText) { + cText = text; + needUpdate(); + } +} + +static const struct { + const char *type, *text; +} stocks[] = { + { "language", I18N_NOOP("Language") }, + { "session", I18N_NOOP("Session Type") }, + { "system", I18N_NOOP("Menu") }, // i18n("Actions"); + { "disconnect", I18N_NOOP("Disconnect") }, + { "quit", I18N_NOOP("Quit") }, + { "halt", I18N_NOOP("Power off") }, + { "suspend", I18N_NOOP("Suspend") }, + { "reboot", I18N_NOOP("Reboot") }, + { "chooser", I18N_NOOP("XDMCP Chooser") }, + { "config", I18N_NOOP("Configure") }, + { "caps-lock-warning", I18N_NOOP("You have got caps lock on.") }, + { "timed-label", I18N_NOOP("User %s will login in %d seconds") }, + { "welcome-label", I18N_NOOP("Welcome to %h") }, // _greetString + { "username-label", I18N_NOOP("Username:") }, + { "password-label", I18N_NOOP("Password:") }, + { "login", I18N_NOOP("Login") } +}; + +QString +KdmLabel::lookupStock( const QString &stock ) +{ + //FIXME add key accels! + QString type( stock.lower() ); + + for (uint i = 0; i < sizeof(stocks)/sizeof(stocks[0]); i++) + if (type == stocks[i].type) + return i18n(stocks[i].text); + + kdDebug() << "Invalid <stock> element. Check your theme!" << endl; + return stock; +} + +QString +KdmLabel::lookupText( const QString &t ) +{ + QString text = t; + + text.replace( '_', '&' ); +// text.remove( '_' ); // FIXME add key accels, remove underscores for now + + QMap<QChar,QString> m; + struct utsname uts; + uname( &uts ); + m['n'] = QString::fromLocal8Bit( uts.nodename ); + char buf[256]; + buf[sizeof(buf) - 1] = '\0'; + m['h'] = gethostname( buf, sizeof(buf) - 1 ) ? "localhost" : QString::fromLocal8Bit( buf ); +#ifdef HAVE_GETDOMAINNAME + m['o'] = getdomainname( buf, sizeof(buf) - 1 ) ? "localdomain" : QString::fromLocal8Bit( buf ); +#elif defined(HAVE_SYSINFO) + m['o'] = (unsigned)sysinfo( SI_SRPC_DOMAIN, buf, sizeof(buf) ) > sizeof(buf) ? "localdomain" : QString::fromLocal8Bit( buf ); +#endif + m['d'] = QString::number( KThemedGreeter::timedDelay ); + m['s'] = KThemedGreeter::timedUser; + // xgettext:no-c-format + KGlobal::locale()->setDateFormat( i18n("date format", "%a %d %B") ); + m['c'] = KGlobal::locale()->formatDateTime( QDateTime::currentDateTime(), false, false ); + + return KMacroExpander::expandMacros( text, m ); +} + +#include "kdmlabel.moc" diff --git a/kdm/kfrontend/themer/kdmlabel.h b/kdm/kfrontend/themer/kdmlabel.h new file mode 100644 index 000000000..b80d0189a --- /dev/null +++ b/kdm/kfrontend/themer/kdmlabel.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2003 by Unai Garro <[email protected]> + * Copyright (C) 2004 by Enrico Ros <[email protected]> + * Copyright (C) 2004 by Stephan Kulow <[email protected]> + * Copyright (C) 2004 by Oswald Buddenhagen <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KDELABEL_H +#define KDELABEL_H + +#include "kdmitem.h" + +#include <qcolor.h> +#include <qfont.h> + +class QTimer; + +/* + * KdmLabel. A label element + */ + +class KdmLabel : public KdmItem { + Q_OBJECT + +public: + KdmLabel( KdmItem *parent, const QDomNode &node, const char *name = 0 ); + void setText( const QString &txt ); + +protected: + // reimplemented; returns the minimum size of rendered text + virtual QSize sizeHint(); + + // draw the label + virtual void drawContents( QPainter *p, const QRect &r ); + + // handle switching between normal / active / prelight configurations + virtual void statusChanged(); + + struct LabelStruct { + QString text; + bool isTimer; + bool hasId; + QString id; + struct LabelClass { + QColor color; + QFont font; + bool present; + } normal, active, prelight; + int maximumWidth; + } label; + + QTimer *timer; + +public slots: + void update(); + +private: + /* Method to lookup the caption associated with an item */ + QString lookupStock( const QString &stock ); + + /* Lookup variables in the text */ + QString lookupText( const QString &t ); + + QString cText; +}; + +#endif diff --git a/kdm/kfrontend/themer/kdmlayout.cpp b/kdm/kfrontend/themer/kdmlayout.cpp new file mode 100644 index 000000000..ed93be264 --- /dev/null +++ b/kdm/kfrontend/themer/kdmlayout.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2003 by Unai Garro <[email protected]> + * Copyright (C) 2004 by Enrico Ros <[email protected]> + * Copyright (C) 2004 by Stephan Kulow <[email protected]> + * Copyright (C) 2004 by Oswald Buddenhagen <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kdmlayout.h" +#include "kdmitem.h" + +#include <kdebug.h> + +#include <qdom.h> +#include <qrect.h> + +KdmLayoutFixed::KdmLayoutFixed( const QDomNode &/*node*/ ) +{ + //Parsing FIXED parameters on 'node' [NONE!] +} + +void +KdmLayoutFixed::update( const QRect &parentGeometry, bool force ) +{ + kdDebug() << "KdmLayoutFixed::update " << parentGeometry << endl; + + // I can't layout children if the parent rectangle is not valid + if (parentGeometry.width() < 0 || parentGeometry.height() < 0) { + kdDebug() << "invalid\n"; + return; + } + // For each child in list I ask their hinted size and set it! + for (QValueList<KdmItem *>::ConstIterator it = m_children.begin(); it != m_children.end(); ++it) + (*it)->setGeometry( (*it)->placementHint( parentGeometry ), force ); +} + +KdmLayoutBox::KdmLayoutBox( const QDomNode &node ) +{ + //Parsing BOX parameters + QDomNode n = node; + QDomElement el = n.toElement(); + box.isVertical = el.attribute( "orientation", "vertical" ) != "horizontal"; + box.xpadding = el.attribute( "xpadding", "0" ).toInt(); + box.ypadding = el.attribute( "ypadding", "0" ).toInt(); + box.spacing = el.attribute( "spacing", "0" ).toInt(); + box.minwidth = el.attribute( "min-width", "0" ).toInt(); + box.minheight = el.attribute( "min-height", "0" ).toInt(); + box.homogeneous = el.attribute( "homogeneous", "false" ) == "true"; +} + +void +KdmLayoutBox::update( const QRect &parentGeometry, bool force ) +{ + kdDebug() << this << " update " << parentGeometry << endl; + + // I can't layout children if the parent rectangle is not valid + if (!parentGeometry.isValid() || parentGeometry.isEmpty()) + return; + + // Check if box size was computed. If not compute it + // TODO check if this prevents updating changing items +// if (!hintedSize.isValid()) +// sizeHint(); + +// kdDebug() << this << " hintedSize " << hintedSize << endl; + + //XXX why was this asymmetric? it broke things big time. + QRect childrenRect = /*box.isVertical ? QRect( parentGeometry.topLeft(), hintedSize ) :*/ parentGeometry; + // Begin cutting the parent rectangle to attach children on the right place + childrenRect.addCoords( box.xpadding, box.ypadding, -box.xpadding, -box.ypadding ); + + kdDebug() << this << " childrenRect " << childrenRect << endl; + + // For each child in list ... + if (box.homogeneous) { + int ccnt = 0; + for (QValueList<KdmItem *>::ConstIterator it = m_children.begin(); it != m_children.end(); ++it) + if (!(*it)->isExplicitlyHidden()) + ccnt++; + int height = (childrenRect.height() - (ccnt - 1) * box.spacing) / ccnt; + int width = (childrenRect.width() - (ccnt - 1) * box.spacing) / ccnt; + + for (QValueList<KdmItem *>::ConstIterator it = m_children.begin(); it != m_children.end(); ++it) { + if ((*it)->isExplicitlyHidden()) + continue; + if (box.isVertical) { + QRect temp( childrenRect.left(), childrenRect.top(), childrenRect.width(), height ); + (*it)->setGeometry( temp, force ); + childrenRect.setTop( childrenRect.top() + height + box.spacing ); + } else { + QRect temp( childrenRect.left(), childrenRect.top(), width, childrenRect.height() ); + kdDebug() << "placement " << *it << " " << temp << " " << (*it)->placementHint( temp ) << endl; + temp = (*it)->placementHint( temp ); + (*it)->setGeometry( temp, force ); + childrenRect.setLeft( childrenRect.left() + width + box.spacing ); + } + } + } else { + for (QValueList<KdmItem *>::ConstIterator it = m_children.begin(); it != m_children.end(); ++it) { + if ((*it)->isExplicitlyHidden()) + continue; + + QRect temp = childrenRect, itemRect; + if (box.isVertical) { + temp.setHeight( 0 ); + itemRect = (*it)->placementHint( temp ); + temp.setHeight( itemRect.height() ); + childrenRect.setTop( childrenRect.top() + itemRect.size().height() + box.spacing ); + } else { + temp.setWidth( 0 ); + itemRect = (*it)->placementHint( temp ); + kdDebug() << this << " placementHint " << *it << " " << temp << " " << itemRect << endl; + temp.setWidth( itemRect.width() ); + childrenRect.setLeft( childrenRect.left() + itemRect.size().width() + box.spacing ); + kdDebug() << "childrenRect after " << *it << " " << childrenRect << endl; + } + itemRect = (*it)->placementHint( temp ); + kdDebug() << this << " placementHint2 " << *it << " " << temp << " " << itemRect << endl; + (*it)->setGeometry( itemRect, force ); + } + } +} + +//FIXME truly experimental (is so close to greeter_geometry.c) +QSize +KdmLayoutBox::sizeHint() +{ + // Sum up area taken by children + int w = 0, h = 0; + for (QValueList<KdmItem *>::ConstIterator it = m_children.begin(); it != m_children.end(); ++it) { + QSize s = (*it)->placementHint( QRect() ).size(); + if (box.isVertical) { + if (s.width() > w) + w = s.width(); + h += s.height(); + } else { + if (s.height() > h) + h = s.height(); + w += s.width(); + } + } + + // Add padding and items spacing + w += 2 * box.xpadding; + h += 2 * box.ypadding; + if (box.isVertical) + h += box.spacing * (m_children.count() - 1); + else + w += box.spacing * (m_children.count() - 1); + + // Make hint at least equal to minimum size (if set) + return QSize( w < box.minwidth ? box.minwidth : w, + h < box.minheight ? box.minheight : h ); +} diff --git a/kdm/kfrontend/themer/kdmlayout.h b/kdm/kfrontend/themer/kdmlayout.h new file mode 100644 index 000000000..2e00675fb --- /dev/null +++ b/kdm/kfrontend/themer/kdmlayout.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2003 by Unai Garro <[email protected]> + * Copyright (C) 2004 by Enrico Ros <[email protected]> + * Copyright (C) 2004 by Stephan Kulow <[email protected]> + * Copyright (C) 2004 by Oswald Buddenhagen <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KDMLAYOUT_H +#define KDMLAYOUT_H + +/** + * this is a container for a lot of other stuff + * but can be treated like a usual widget + */ + +#include <qvaluelist.h> +#include <qsize.h> + +class KdmItem; + +class QDomNode; +class QRect; + +class KdmLayout { + +public: +// virtual ~KdmLayout() {}; + + // Adds an item that will be managed + void addItem( KdmItem *item ) { m_children.append( item ); } + + // Return false if any item are managed by this layouter + bool isEmpty() { return m_children.isEmpty(); } + + // Updates the layout of all items knowing that the parent + // has the @p parentGeometry geometry +// virtual void update( const QRect &parentGeometry ) = 0; + +protected: + QValueList<KdmItem *> m_children; +}; + +class KdmLayoutFixed : public KdmLayout { + +public: + KdmLayoutFixed( const QDomNode &node ); + + // Updates the layout of all boxed items knowing that the parent + // has the @p parentGeometry geometry + void update( const QRect &parentGeometry, bool force ); +}; + +/** + * this is a container for a lot of other stuff + * but can be treated like a usual widget + */ + +class KdmLayoutBox : public KdmLayout { + +public: + KdmLayoutBox( const QDomNode &node ); + + // Updates the layout of all boxed items knowing that they + // should fit into @p parentGeometry container + void update( const QRect &parentGeometry, bool force ); + + // Computes the size hint of the box, telling which is the + // smallest size inside which boxed items will fit + QSize sizeHint(); + +private: + struct { + bool isVertical; + int spacing; + int xpadding; + int ypadding; + int minwidth; + int minheight; + bool homogeneous; + } box; +// QSize hintedSize; +}; + +#endif diff --git a/kdm/kfrontend/themer/kdmpixmap.cpp b/kdm/kfrontend/themer/kdmpixmap.cpp new file mode 100644 index 000000000..337c19ced --- /dev/null +++ b/kdm/kfrontend/themer/kdmpixmap.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2003 by Unai Garro <[email protected]> + * Copyright (C) 2004 by Enrico Ros <[email protected]> + * Copyright (C) 2004 by Stephan Kulow <[email protected]> + * Copyright (C) 2004 by Oswald Buddenhagen <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> + +#include "kdmpixmap.h" + +#include <kimageeffect.h> +#ifdef HAVE_LIBART +#include <ksvgiconengine.h> +#endif + +#include <kdebug.h> + +#include <qpainter.h> +#include <qpixmap.h> +#include <qimage.h> + +KdmPixmap::KdmPixmap( KdmItem *parent, const QDomNode &node, const char *name ) + : KdmItem( parent, node, name ) +{ + itemType = "pixmap"; + + // Set default values for pixmap (note: strings are already Null) + pixmap.normal.tint.setRgb( 0xFFFFFF ); + pixmap.normal.alpha = 1.0; + pixmap.active.present = false; + pixmap.prelight.present = false; + + // Read PIXMAP ID + // it rarely happens that a pixmap can be a button too! + QDomNode n = node; + QDomElement elPix = n.toElement(); + + // Read PIXMAP TAGS + QDomNodeList childList = node.childNodes(); + for (uint nod = 0; nod < childList.count(); nod++) { + QDomNode child = childList.item( nod ); + QDomElement el = child.toElement(); + QString tagName = el.tagName(); + + if (tagName == "normal") { + loadPixmap( el.attribute( "file", "" ), pixmap.normal.pixmap, pixmap.normal.fullpath ); + parseColor( el.attribute( "tint", "#ffffff" ), pixmap.normal.tint ); + pixmap.normal.alpha = el.attribute( "alpha", "1.0" ).toFloat(); + } else if (tagName == "active") { + pixmap.active.present = true; + loadPixmap( el.attribute( "file", "" ), pixmap.active.pixmap, pixmap.active.fullpath ); + parseColor( el.attribute( "tint", "#ffffff" ), pixmap.active.tint ); + pixmap.active.alpha = el.attribute( "alpha", "1.0" ).toFloat(); + } else if (tagName == "prelight") { + pixmap.prelight.present = true; + loadPixmap( el.attribute( "file", "" ), pixmap.prelight.pixmap, pixmap.prelight.fullpath ); + parseColor( el.attribute( "tint", "#ffffff" ), pixmap.prelight.tint ); + pixmap.prelight.alpha = el.attribute( "alpha", "1.0" ).toFloat(); + } + } +} + +QSize +KdmPixmap::sizeHint() +{ + // choose the correct pixmap class + PixmapStruct::PixmapClass * pClass = &pixmap.normal; + if (state == Sactive && pixmap.active.present) + pClass = &pixmap.active; + if (state == Sprelight && pixmap.prelight.present) + pClass = &pixmap.prelight; + // use the pixmap size as the size hint + if (!pClass->pixmap.isNull()) + return pClass->pixmap.size(); + return KdmItem::sizeHint(); +} + +void +KdmPixmap::setGeometry( const QRect &newGeometry, bool force ) +{ + KdmItem::setGeometry( newGeometry, force ); + pixmap.active.readyPixmap.resize( 0, 0 ); + pixmap.prelight.readyPixmap.resize( 0, 0 ); + pixmap.normal.readyPixmap.resize( 0, 0 ); +} + + +void +KdmPixmap::loadPixmap( const QString &fileName, QPixmap &map, QString &fullName ) +{ + if (fileName.isEmpty()) + return; + + fullName = fileName; + if (fullName.at( 0 ) != '/') + fullName = baseDir() + "/" + fileName; + + if (!fullName.endsWith( ".svg" )) // we delay it for svgs + if (!map.load( fullName )) + fullName = QString::null; +} + +void +KdmPixmap::renderSvg( PixmapStruct::PixmapClass *pClass, const QRect &area ) +{ +#ifdef HAVE_LIBART + // Special stuff for SVG icons + KSVGIconEngine *svgEngine = new KSVGIconEngine(); + + if (svgEngine->load( area.width(), area.height(), pClass->fullpath )) { + QImage *t = svgEngine->image(); + pClass->pixmap = *t; + pClass->readyPixmap.resize( 0, 0 ); + delete t; + } else { + kdWarning() << "failed to load " << pClass->fullpath << endl; + pClass->fullpath = QString::null; + } + + delete svgEngine; +#else + Q_UNUSED(pClass); + Q_UNUSED(area); +#endif +} + +void +KdmPixmap::drawContents( QPainter *p, const QRect &r ) +{ + // choose the correct pixmap class + PixmapStruct::PixmapClass *pClass = &pixmap.normal; + if (state == Sactive && pixmap.active.present) + pClass = &pixmap.active; + if (state == Sprelight && pixmap.prelight.present) + pClass = &pixmap.prelight; + + if (pClass->pixmap.isNull()) { + if (pClass->fullpath.isEmpty()) // if neither is set, we're empty + return; + + kdDebug() << "renderSVG\n"; + renderSvg( pClass, area ); + } + + int px = area.left() + r.left(); + int py = area.top() + r.top(); + int sx = r.x(); + int sy = r.y(); + int sw = r.width(); + int sh = r.height(); + if (px < 0) { + px *= -1; + sx += px; + px = 0; + } + if (py < 0) { + py *= -1; + sy += py; + py = 0; + } + + + if (pClass->readyPixmap.isNull()) { + QImage scaledImage; + + // use the loaded pixmap or a scaled version if needed + + if (area.size() != pClass->pixmap.size()) { + if (pClass->fullpath.endsWith( ".svg" )) { + kdDebug() << "renderSVG\n"; + renderSvg( pClass, area ); + scaledImage = pClass->pixmap.convertToImage(); + } else { + kdDebug() << "convertFromImage\n"; + QImage tempImage = pClass->pixmap.convertToImage(); + scaledImage = tempImage.smoothScale( area.width(), area.height() ); + } + } else + scaledImage = pClass->pixmap.convertToImage(); + + bool haveTint = pClass->tint.rgb() != 0xFFFFFF; + bool haveAlpha = pClass->alpha < 1.0; + + if (haveTint || haveAlpha) { + // blend image(pix) with the given tint + + scaledImage = scaledImage.convertDepth( 32 ); + int w = scaledImage.width(); + int h = scaledImage.height(); + float tint_red = float( pClass->tint.red() ) / 255; + float tint_green = float( pClass->tint.green() ) / 255; + float tint_blue = float( pClass->tint.blue() ) / 255; + float tint_alpha = pClass->alpha; + + for (int y = 0; y < h; ++y) { + QRgb *ls = (QRgb *)scaledImage.scanLine( y ); + for (int x = 0; x < w; ++x) { + QRgb l = ls[x]; + int r = int( qRed( l ) * tint_red ); + int g = int( qGreen( l ) * tint_green ); + int b = int( qBlue( l ) * tint_blue ); + int a = int( qAlpha( l ) * tint_alpha ); + ls[x] = qRgba( r, g, b, a ); + } + } + + } + + pClass->readyPixmap.convertFromImage( scaledImage ); + } + // kdDebug() << "Pixmap::drawContents " << pClass->readyPixmap.size() << " " << px << " " << py << " " << sx << " " << sy << " " << sw << " " << sh << endl; + p->drawPixmap( px, py, pClass->readyPixmap, sx, sy, sw, sh ); +} + +void +KdmPixmap::statusChanged() +{ + KdmItem::statusChanged(); + if (!pixmap.active.present && !pixmap.prelight.present) + return; + if ((state == Sprelight && !pixmap.prelight.present) || + (state == Sactive && !pixmap.active.present)) + return; + needUpdate(); +} + +#include "kdmpixmap.moc" diff --git a/kdm/kfrontend/themer/kdmpixmap.h b/kdm/kfrontend/themer/kdmpixmap.h new file mode 100644 index 000000000..eb621a0a5 --- /dev/null +++ b/kdm/kfrontend/themer/kdmpixmap.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2003 by Unai Garro <[email protected]> + * Copyright (C) 2004 by Enrico Ros <[email protected]> + * Copyright (C) 2004 by Stephan Kulow <[email protected]> + * Copyright (C) 2004 by Oswald Buddenhagen <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KDMPIXMAP_H +#define KDMPIXMAP_H + +#include "kdmitem.h" + +//#include <qrect.h> +#include <qpixmap.h> + +/* + * KdmPixmap. A pixmap element + */ + +class KdmPixmap : public KdmItem { + Q_OBJECT + +public: + KdmPixmap( KdmItem *parent, const QDomNode &node, const char *name = 0 ); + +protected: + // reimplemented; returns the size of loaded pixmap + virtual QSize sizeHint(); + + // draw the pixmap + virtual void drawContents( QPainter *p, const QRect &r ); + + // handle switching between normal / active / prelight configurations + virtual void statusChanged(); + + virtual void setGeometry( const QRect &newGeometry, bool force ); + + struct PixmapStruct { + struct PixmapClass { + QString fullpath; + QPixmap pixmap; + QPixmap readyPixmap; + QColor tint; + float alpha; //TODO added: not in greeter.dtd + bool present; + } normal, active, prelight; + } pixmap; + +private: + // Method to load the pixmap given by the theme + void loadPixmap( const QString &fileName, QPixmap &p, QString &path ); + void renderSvg( PixmapStruct::PixmapClass *pClass, const QRect &area ); +}; + +#endif diff --git a/kdm/kfrontend/themer/kdmrect.cpp b/kdm/kfrontend/themer/kdmrect.cpp new file mode 100644 index 000000000..478c5c8b3 --- /dev/null +++ b/kdm/kfrontend/themer/kdmrect.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2003 by Unai Garro <[email protected]> + * Copyright (C) 2004 by Enrico Ros <[email protected]> + * Copyright (C) 2004 by Stephan Kulow <[email protected]> + * Copyright (C) 2004 by Oswald Buddenhagen <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kdmrect.h" +#include "kdmthemer.h" + +#include <kimageeffect.h> +#include <kdebug.h> + +#include <qimage.h> +#include <qpainter.h> +#include <qwidget.h> +#include <qlayout.h> + +KdmRect::KdmRect( KdmItem *parent, const QDomNode &node, const char *name ) + : KdmItem( parent, node, name ) +{ + itemType = "rect"; + + // Set default values for rect (note: strings are already Null) + rect.normal.alpha = 1; + rect.active.present = false; + rect.prelight.present = false; + rect.hasBorder = false; + + // A rect can have no properties (defaults to parent ones) + if (node.isNull()) + return; + + // Read RECT ID + QDomNode n = node; + QDomElement elRect = n.toElement(); + + // Read RECT TAGS + QDomNodeList childList = node.childNodes(); + for (uint nod = 0; nod < childList.count(); nod++) { + QDomNode child = childList.item( nod ); + QDomElement el = child.toElement(); + QString tagName = el.tagName(); + + if (tagName == "normal") { + parseColor( el.attribute( "color", QString::null ), rect.normal.color ); + rect.normal.alpha = el.attribute( "alpha", "1.0" ).toFloat(); + parseFont( el.attribute( "font", "Sans 14" ), rect.normal.font ); + } else if (tagName == "active") { + rect.active.present = true; + parseColor( el.attribute( "color", QString::null ), rect.active.color ); + rect.active.alpha = el.attribute( "alpha", "1.0" ).toFloat(); + parseFont( el.attribute( "font", "Sans 14" ), rect.active.font ); + } else if (tagName == "prelight") { + rect.prelight.present = true; + parseColor( el.attribute( "color", QString::null ), rect.prelight.color ); + rect.prelight.alpha = el.attribute( "alpha", "1.0" ).toFloat(); + parseFont( el.attribute( "font", "Sans 14" ), rect.prelight.font ); + } else if (tagName == "border") + rect.hasBorder = true; + } +} + +void +KdmRect::drawContents( QPainter *p, const QRect &r ) +{ + // choose the correct rect class + RectStruct::RectClass *rClass = &rect.normal; + if (state == Sactive && rect.active.present) + rClass = &rect.active; + if (state == Sprelight && rect.prelight.present) + rClass = &rect.prelight; + + if (rClass->alpha <= 0 || !rClass->color.isValid()) + return; + + if (rClass->alpha == 1) + p->fillRect( area, QBrush( rClass->color ) ); + else { + QRect backRect = r; + backRect.moveBy( area.x(), area.y() ); + QPixmap backPixmap( backRect.size() ); + bitBlt( &backPixmap, QPoint( 0, 0 ), p->device(), backRect ); + QImage backImage = backPixmap.convertToImage(); + KImageEffect::blend( rClass->color, backImage, rClass->alpha ); + p->drawImage( backRect.x(), backRect.y(), backImage ); + // area.moveBy(1,1); + } +} + +void +KdmRect::statusChanged() +{ + KdmItem::statusChanged(); + if (!rect.active.present && !rect.prelight.present) + return; + if ((state == Sprelight && !rect.prelight.present) || + (state == Sactive && !rect.active.present)) + return; + needUpdate(); +} + +/* +void +KdmRect::setAttribs( QWidget *widget ) +{ + widget->setFont( rect.normal.font ); +} + +void +KdmRect::recursiveSetAttribs( QLayoutItem *li ) +{ + QWidget *w; + QLayout *l; + + if ((w = li->widget())) + setAttribs( w ); + else if ((l = li->layout())) { + QLayoutIterator it = l->iterator(); + for (QLayoutItem *itm = it.current(); itm; itm = ++it) + recursiveSetAttribs( itm ); + } +} + +void +KdmRect::setWidget( QWidget *widget ) +{ + KdmItem::setWidget( widget ); + setAttribs( widget ); +} + +void +KdmRect::setLayoutItem( QLayoutItem *item ) +{ + KdmItem::setLayoutItem( item ); + recursiveSetAttribs( item ); +} +*/ + +#include "kdmrect.moc" diff --git a/kdm/kfrontend/themer/kdmrect.h b/kdm/kfrontend/themer/kdmrect.h new file mode 100644 index 000000000..2e89a6a52 --- /dev/null +++ b/kdm/kfrontend/themer/kdmrect.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2003 by Unai Garro <[email protected]> + * Copyright (C) 2004 by Enrico Ros <[email protected]> + * Copyright (C) 2004 by Stephan Kulow <[email protected]> + * Copyright (C) 2004 by Oswald Buddenhagen <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KDMRECT_H +#define KDMRECT_H + +#include "kdmitem.h" + +#include <qcolor.h> +#include <qfont.h> + +/* + * KdmRect: A themed rectangular element + */ + +class KdmRect : public KdmItem { + Q_OBJECT + +public: + KdmRect( KdmItem *parent, const QDomNode &node, const char *name = 0 ); + +protected: + // draw the rect + virtual void drawContents( QPainter *p, const QRect &r ); + + // handle switching between normal / active / prelight configurations + virtual void statusChanged(); + + struct RectStruct { + struct RectClass { + float alpha; + QColor color; + bool present; + QFont font; + } normal, active, prelight; + bool hasBorder; + } rect; + +// virtual void setWidget( QWidget *widget ); +// virtual void setLayoutItem( QLayoutItem *item ); + +private: + void setAttribs( QWidget *widget ); + void recursiveSetAttribs( QLayoutItem *item ); +}; + +#endif diff --git a/kdm/kfrontend/themer/kdmthemer.cpp b/kdm/kfrontend/themer/kdmthemer.cpp new file mode 100644 index 000000000..65eebabf4 --- /dev/null +++ b/kdm/kfrontend/themer/kdmthemer.cpp @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2003 by Unai Garro <[email protected]> + * Copyright (C) 2004 by Enrico Ros <[email protected]> + * Copyright (C) 2004 by Stephan Kulow <[email protected]> + * Copyright (C) 2004 by Oswald Buddenhagen <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kdmthemer.h" +#include "kdmitem.h" +#include "kdmpixmap.h" +#include "kdmrect.h" +#include "kdmlabel.h" + +#include <kdmconfig.h> +#include <kfdialog.h> + +#include <kiconloader.h> +#include <kimageeffect.h> +#include <klocale.h> +#include <ksimpleconfig.h> +#include <kdebug.h> + +#include <qfile.h> +#include <qfileinfo.h> +//#include <qtimer.h> // animation timer - TODO +#include <qobjectlist.h> +#include <qpainter.h> +#include <qwidget.h> +#include <qregion.h> + +#include <unistd.h> + +/* + * KdmThemer. The main theming interface + */ +KdmThemer::KdmThemer( const QString &_filename, const QString &mode, QWidget *parent ) + : QObject( parent ) + , rootItem( 0 ) + , backBuffer( 0 ) +{ + // Set the mode we're working in + m_currentMode = mode; + + // read the XML file and create DOM tree + QString filename = _filename; + if (!::access( QFile::encodeName( filename + "/GdmGreeterTheme.desktop" ), R_OK )) { + KSimpleConfig cfg( filename + "/GdmGreeterTheme.desktop" ); + cfg.setGroup( "GdmGreeterTheme" ); + filename += '/' + cfg.readEntry( "Greeter" ); + } + QFile opmlFile( filename ); + if (!opmlFile.open( IO_ReadOnly )) { + FDialog::box( widget(), errorbox, i18n( "Cannot open theme file %1" ).arg(filename) ); + return; + } + if (!domTree.setContent( &opmlFile )) { + FDialog::box( widget(), errorbox, i18n( "Cannot parse theme file %1" ).arg(filename) ); + return; + } + // Set the root (screen) item + rootItem = new KdmRect( 0, QDomNode(), "kdm root" ); + connect( rootItem, SIGNAL(needUpdate( int, int, int, int )), + widget(), SLOT(update( int, int, int, int )) ); + + rootItem->setBaseDir( QFileInfo( filename ).dirPath( true ) ); + + // generate all the items defined in the theme + generateItems( rootItem ); + + connect( rootItem, SIGNAL(activated( const QString & )), SIGNAL(activated( const QString & )) ); + +/* *TODO* + // Animation timer + QTimer *time = new QTimer( this ); + time->start( 500 ); + connect( time, SIGNAL(timeout()), SLOT(update()) ) +*/ +} + +KdmThemer::~KdmThemer() +{ + delete rootItem; + delete backBuffer; +} + +inline QWidget * +KdmThemer::widget() +{ + return static_cast<QWidget *>(parent()); +} + +KdmItem * +KdmThemer::findNode( const QString &item ) const +{ + return rootItem->findNode( item ); +} + +void +KdmThemer::updateGeometry( bool force ) +{ + rootItem->setGeometry( QRect( QPoint(), widget()->size() ), force ); +} + +// BEGIN other functions + +void +KdmThemer::widgetEvent( QEvent *e ) +{ + if (!rootItem) + return; + switch (e->type()) { + case QEvent::MouseMove: + { + QMouseEvent *me = static_cast<QMouseEvent *>(e); + rootItem->mouseEvent( me->x(), me->y() ); + } + break; + case QEvent::MouseButtonPress: + { + QMouseEvent *me = static_cast<QMouseEvent *>(e); + rootItem->mouseEvent( me->x(), me->y(), true ); + } + break; + case QEvent::MouseButtonRelease: + { + QMouseEvent *me = static_cast<QMouseEvent *>(e); + rootItem->mouseEvent( me->x(), me->y(), false, true ); + } + break; + case QEvent::Show: + rootItem->show(); + break; + case QEvent::Resize: + updateGeometry( false ); + showStructure( rootItem ); + break; + case QEvent::Paint: + { + QRect paintRect = static_cast<QPaintEvent *>(e)->rect(); + kdDebug() << "paint on: " << paintRect << endl; + + if (!backBuffer) + backBuffer = new QPixmap( widget()->size() ); + if (backBuffer->size() != widget()->size()) + backBuffer->resize( widget()->size() ); + + QPainter p; + p.begin( backBuffer ); + rootItem->paint( &p, paintRect ); + p.end(); + + bitBlt( widget(), paintRect.topLeft(), backBuffer, paintRect ); + } + break; + default: + break; + } +} + +/* +void +KdmThemer::pixmap( const QRect &r, QPixmap *px ) +{ + bitBlt( px, QPoint( 0, 0 ), widget(), r ); +} +*/ + +void +KdmThemer::generateItems( KdmItem *parent, const QDomNode &node ) +{ + if (!parent) + return; + + QDomNodeList subnodeList; //List of subnodes of this node + + /* + * Get the child nodes + */ + if (node.isNull()) { // It's the first node, get its child nodes + QDomElement theme = domTree.documentElement(); + + // Get its tag, and check it's correct ("greeter") + if (theme.tagName() != "greeter") { + kdDebug() << "This does not seem to be a correct theme file." << endl; + return; + } + // Get the list of child nodes + subnodeList = theme.childNodes(); + } else + subnodeList = node.childNodes(); + + /* + * Go through each of the child nodes + */ + for (uint nod = 0; nod < subnodeList.count(); nod++) { + QDomNode subnode = subnodeList.item( nod ); + QDomElement el = subnode.toElement(); + QString tagName = el.tagName(); + + if (tagName == "item") { + if (!willDisplay( subnode )) + continue; + // It's a new item. Draw it + QString type = el.attribute( "type" ); + + KdmItem *newItem = 0; + + if (type == "label") + newItem = new KdmLabel( parent, subnode ); + else if (type == "pixmap") + newItem = new KdmPixmap( parent, subnode ); + else if (type == "rect") + newItem = new KdmRect( parent, subnode ); + else if (type == "entry") { + newItem = new KdmRect( parent, subnode ); + newItem->setType( type ); + } + // newItem = new KdmEntry( parent, subnode ); + //else if (type=="list") + // newItem = new KdmList( parent, subnode ); + else if (type == "svg") + newItem = new KdmPixmap( parent, subnode ); + if (newItem) { + generateItems( newItem, subnode ); + if (el.attribute( "button", "false" ) == "true") + newItem->inheritFromButton( newItem ); + } + } else if (tagName == "box") { + if (!willDisplay( subnode )) + continue; + // It's a new box. Draw it + parent->setBoxLayout( subnode ); + generateItems( parent, subnode ); + } else if (tagName == "fixed") { + if (!willDisplay( subnode )) + continue; + // It's a new box. Draw it + parent->setFixedLayout( subnode ); + generateItems( parent, subnode ); + } + } +} + +bool KdmThemer::willDisplay( const QDomNode &node ) +{ + QDomNode showNode = node.namedItem( "show" ); + + // No "show" node means this item can be displayed at all times + if (showNode.isNull()) + return true; + + QDomElement el = showNode.toElement(); + + QString modes = el.attribute( "modes" ); + if (!modes.isNull()) { + QStringList modeList = QStringList::split( ",", modes ); + + // If current mode isn't in this list, do not display item + if (modeList.find( m_currentMode ) == modeList.end()) + return false; + } + + QString type = el.attribute( "type" ); + if (type == "config" || type == "suspend") + return false; // not implemented (yet) + if (type == "timed") + return _autoLoginDelay != 0; + if (type == "chooser") +#ifdef XDMCP + return _loginMode != LOGIN_LOCAL_ONLY; +#else + return false; +#endif + if (type == "halt" || type == "reboot") + return _allowShutdown != SHUT_NONE; +// if (type == "system") +// return true; + + // All tests passed, item will be displayed + return true; +} + +void +KdmThemer::showStructure( QObject *obj ) +{ + + const QObjectList *wlist = obj->children(); + static int counter = 0; + if (counter == 0) + kdDebug() << "\n\n<======= Widget tree =================" << endl; + if (wlist) { + counter++; + QObjectListIterator it( *wlist ); + QObject *object; + + while ((object = it.current()) != 0) { + ++it; + QString node; + for (int i = 1; i < counter; i++) + node += "-"; + + if (object->inherits( "KdmItem" )) { + KdmItem *widget = (KdmItem *)object; + kdDebug() << node << "|" << widget->type() << " me=" << widget->id << " " << widget->area << endl; + } + + showStructure( object ); + } + counter--; + } + if (counter == 0) + kdDebug() << "\n\n<======= Widget tree =================\n\n" << endl; +} + +#include "kdmthemer.moc" diff --git a/kdm/kfrontend/themer/kdmthemer.h b/kdm/kfrontend/themer/kdmthemer.h new file mode 100644 index 000000000..34baffbb7 --- /dev/null +++ b/kdm/kfrontend/themer/kdmthemer.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2003 by Unai Garro <[email protected]> + * Copyright (C) 2004 by Enrico Ros <[email protected]> + * Copyright (C) 2004 by Stephan Kulow <[email protected]> + * Copyright (C) 2004 by Oswald Buddenhagen <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KDMTHEMER_H +#define KDMTHEMER_H + +#include <qobject.h> +#include <qdom.h> + +class KdmThemer; +class KdmItem; +class KdmPixmap; +class KdmRect; +class KdmBox; + +class QRect; +class QWidget; +class QEvent; + +/** +* @author Unai Garro +*/ + + + +/* +* The themer widget. Whatever drawn here is just themed +* according to a XML file set by the user. +*/ + + +class KdmThemer : public QObject { + Q_OBJECT + +public: + /* + * Construct and destruct the interface + */ + + KdmThemer( const QString &path, const QString &mode, QWidget *parent ); + ~KdmThemer(); + + bool isOK() { return rootItem != 0; } + /* + * Gives a sizeHint to the widget (parent size) + */ + //QSize sizeHint() const{ return parentWidget()->size(); } + + /* + * Takes a shot of the current widget + */ +// void pixmap( const QRect &r, QPixmap *px ); + + virtual // just to put the reference in the vmt + KdmItem *findNode( const QString & ) const; + + void updateGeometry( bool force ); // force = true for external calls + + // must be called by parent widget + void widgetEvent( QEvent *e ); + +signals: + void activated( const QString &id ); + +private: + /* + * Our display mode (e.g. console, remote, ...) + */ + QString m_currentMode; + + /* + * The config file being used + */ + QDomDocument domTree; + + /* + * Stores the root of the theme + */ + KdmItem *rootItem; + + /* + * The backbuffer + */ + QPixmap *backBuffer; + + // methods + + /* + * Test whether item needs to be displayed + */ + bool willDisplay( const QDomNode &node ); + + /* + * Parses the XML file looking for the + * item list and adds those to the themer + */ + void generateItems( KdmItem *parent = 0, const QDomNode &node = QDomNode() ); + + void showStructure( QObject *obj ); + + QWidget *widget(); +}; + + +#endif diff --git a/kdm/kfrontend/themes/Makefile.am b/kdm/kfrontend/themes/Makefile.am new file mode 100644 index 000000000..bad3ea323 --- /dev/null +++ b/kdm/kfrontend/themes/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = circles diff --git a/kdm/kfrontend/themes/circles/GdmGreeterTheme.desktop b/kdm/kfrontend/themes/circles/GdmGreeterTheme.desktop new file mode 100644 index 000000000..72be02f41 --- /dev/null +++ b/kdm/kfrontend/themes/circles/GdmGreeterTheme.desktop @@ -0,0 +1,135 @@ +# This is not really a .desktop file like the rest, but it's useful to treat +# it as such + +[GdmGreeterTheme] +Greeter=circles.xml +Name=Circles +Name[ar]=الدوائر +Name[bn]=সার্কল্স +Name[br]=Kelc'hioù +Name[bs]=Krugovi +Name[ca]=Cercles +Name[cs]=Kruhy +Name[csb]=Kółka +Name[cy]=Cylchoedd +Name[da]=Cirkler +Name[de]=Kreise +Name[el]=Κύκλοι +Name[eo]=cirkloj +Name[es]=Círculos +Name[et]=Ringid +Name[fa]=دایرهها +Name[fi]=Ympyrät +Name[fy]=Cirkels +Name[ga]=Ciorcail +Name[he]=מעגלים +Name[hi]=वृत्त +Name[hr]=Krugovi +Name[hu]=Körök +Name[is]=Hringir +Name[it]=Cerchi +Name[ja]=サークル +Name[ka]=წრეები +Name[kk]=Дөңгелектер +Name[km]=រង្វង់ +Name[ko]=원 +Name[lt]=Skrituliai +Name[mk]=Кругови +Name[ms]=Bulatan +Name[nb]=Sirkler +Name[nds]=Krinken +Name[ne]=वृत्त +Name[nl]=Cirkels +Name[nn]=Sirklar +Name[pa]=ਚੱਕਰ +Name[pl]=Kółka +Name[pt]=Círculos +Name[pt_BR]=Círculos +Name[ro]=Cercuri +Name[ru]=Круги +Name[rw]=Inziga +Name[se]=Gearddut +Name[sk]=Kruhy +Name[sl]=Krogi +Name[sr]=Кругови +Name[sr@Latn]=Krugovi +Name[sv]=Cirklar +Name[ta]=வட்டங்கள் +Name[te]=వృత్తాలు +Name[tg]=Доираҳо +Name[tr]=Çemberler +Name[uk]=Кола +Name[uz]=Aylanalar +Name[uz@cyrillic]=Айланалар +Name[vi]=Vòng tròn +Name[wa]=Cekes +Name[zh_CN]=圆环 +Description=Theme with blue circles +Description[af]=Tema met blou sirkels +Description[ar]=سمة بلدوائر الزرقاء +Description[be]=Тэма з блакітнымі коламі +Description[bn]=নীল বৃত্ত সম্বলিত থীম +Description[bs]=Tema sa plavim krugovima +Description[ca]=Tema amb cercles blaus +Description[cs]=Motiv s modrými kruhy +Description[csb]=Téma z mòdrima kółkama +Description[da]=Tema med blå cirkler +Description[de]=Thema mit blauen Kreisen +Description[el]=Θέμα με μπλε κύκλους +Description[eo]=Etoso kun bluaj cirkloj +Description[es]=Tema con círculos azules +Description[et]=Siniste ringidega teema +Description[eu]=Biribil urdindun gaia +Description[fa]=چهره با دایرههای آبی +Description[fi]=Teema sinisillä ympyröillä +Description[fr]=Thème avec des cercles bleus +Description[fy]=Tema mei blauwe sirkels +Description[gl]=Tema con círculos azuis +Description[he]=ערכת נושא עם מעגלים כחולים +Description[hi]=नीले वृत्तों के साथ प्रसंग +Description[hr]=Tema s plavim krugovima +Description[hu]=Téma kék körökkel +Description[is]=Þema með bláum hringjum +Description[it]=Tema con cerchi blu +Description[ja]=青い丸のテーマ +Description[ka]=ლურჯ წრეებიანი თემა +Description[kk]=Көк дөңгелекті нақыш +Description[km]=ស្បែកមានរង្វង់ពណ៌ខៀវ +Description[ko]=파란 원이 있는 테마 +Description[lt]=Tema su melsvais skrituliais +Description[lv]=Tēma ar ziliem riņķiem +Description[mk]=Тема со сини кругови +Description[ms]=Tema dengan bulatan biru +Description[nb]=Tema med blå sirkler +Description[nds]=Muster mit blage Krinken +Description[ne]=निलो वृत्तसँग विषयवस्तु +Description[nl]=Thema met blauwe cirkels +Description[nn]=Tema med blåe sirklar +Description[pa]=ਨੀਲੇ ਚੱਕਰਾਂ ਵਾਲਾ ਸਰੂਪ +Description[pl]=Motyw z niebieskimi kółkami +Description[pt]=Tema com círculos azuis +Description[pt_BR]=Tema com círculos azuis +Description[ro]=Temă cu cercuri albastre +Description[ru]=Тема с синими кругами +Description[rw]=Insanganyamatsiko ifite inziga z'ubururu +Description[se]=Fáddá mas lea alit gearddut +Description[sk]=Téma s modrými kruhmi +Description[sl]=Tema z modrimi krogi +Description[sr]=Тема са плавим круговима +Description[sr@Latn]=Tema sa plavim krugovima +Description[sv]=Tema med blåa cirklar +Description[ta]=நீல வட்டங்களுடன் தலைப்பு +Description[tg]=Мавзӯъ бо доираҳои кабуд +Description[th]=ชุดตกแต่งมาพร้อมวงกลมสีน้ำเงิน +Description[tr]=Mavi çemberli tema +Description[tt]=Zäñgär tügäräklär belän tışlaw +Description[uk]=Тема з синіми колами +Description[uz]=Koʻk aylanalarli mavzu +Description[uz@cyrillic]=Кўк айланаларли мавзу +Description[vi]=Sắc thái với các vòng tròn xanh lam +Description[wa]=Tinme avou des bleus cekes +Description[zh_CN]=带蓝环的主题 +Description[zh_TW]=有藍色圓圈的佈景主題 +Author=Bond, James Bond +Copyright=(c) 2002 Bond, James Bond +Screenshot=screenshot.png diff --git a/kdm/kfrontend/themes/circles/Makefile.am b/kdm/kfrontend/themes/circles/Makefile.am new file mode 100644 index 000000000..97ad04b6a --- /dev/null +++ b/kdm/kfrontend/themes/circles/Makefile.am @@ -0,0 +1,11 @@ +circlesdir = $(kde_datadir)/kdm/themes/circles +circles_DATA = \ + GdmGreeterTheme.desktop \ + circles.xml \ + background.svg \ + flower.png \ + help.png \ + options.png \ + screenshot.png + +EXTRA_DIST = $(circles_DATA) diff --git a/kdm/kfrontend/themes/circles/background.svg b/kdm/kfrontend/themes/circles/background.svg new file mode 100644 index 000000000..11abc4f43 --- /dev/null +++ b/kdm/kfrontend/themes/circles/background.svg @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- Generator: Adobe Illustrator 9.0, SVG Export Plug-In -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20000303 Stylable//EN" "http://www.w3.org/TR/2000/03/WD-SVG-20000303/DTD/svg-20000303-stylable.dtd" [
+ <!ENTITY st0 "fill:url(#aigrd1);stroke:none;">
+ <!ENTITY st1 "fill:url(#aigrd3);stroke:none;">
+ <!ENTITY st2 "fill:url(#aigrd2);stroke:none;">
+ <!ENTITY st3 "fill-rule:nonzero;clip-rule:nonzero;fill:#2A569D;stroke:#000000;stroke-miterlimit:4;">
+ <!ENTITY st4 "stroke:none;">
+]>
+<svg width="508.104pt" height="383.717pt" viewBox="0 0 508.104 383.717" xml:space="preserve">
+ <g id="Layer_x0020_1" style="&st3;">
+ <g id="middle">
+ <linearGradient id="aigrd1" gradientUnits="userSpaceOnUse" x1="237.2617" y1="24.7095" x2="237.2617" y2="371.207">
+ <stop offset="0" style="stop-color:#4E77B9"/>
+ <stop offset="1" style="stop-color:#6E8CBE"/>
+ </linearGradient>
+ <path style="&st0;" d="M59.227,357.212c27.481,9.066,56.985,13.994,87.696,13.994c148.221,0,268.375-114.698,268.375-256.189c0-31.789-6.073-62.221-17.16-90.308C249.869,104.206,131.168,219.909,59.227,357.212z"/>
+ </g>
+ <linearGradient id="aigrd2" gradientUnits="userSpaceOnUse" x1="277.0767" y1="0" x2="277.0766" y2="383.7178">
+ <stop offset="0" style="stop-color:#335EA2"/>
+ <stop offset="1" style="stop-color:#6D8BBD"/>
+ </linearGradient>
+ <path id="lower_x0020_right" style="&st2;" d="M447.704,0c-16.859,7.792-33.389,16.037-49.566,24.709c11.087,28.087,17.16,58.52,17.16,90.308c0,141.491-120.154,256.189-268.375,256.189c-30.71,0-60.214-4.928-87.696-13.994
+ c-4.584,8.749-8.979,17.585-13.178,26.505h462.056V0h-60.4z"/>
+ <g id="upper_x0020_left">
+ <linearGradient id="aigrd3" gradientUnits="userSpaceOnUse" x1="199.0684" y1="0" x2="199.0683" y2="357.2129">
+ <stop offset="0" style="stop-color:#436FB6"/>
+ <stop offset="1" style="stop-color:#305CA3"/>
+ </linearGradient>
+ <path style="&st1;" d="M398.137,24.709C394.798,16.252,391.011,8.005,386.791,0H0v329.44c18.309,11.454,38.174,20.827,59.227,27.772c71.942-137.304,190.642-253.007,338.911-332.503z"/>
+ </g>
+ <g id="bottom_x0020_small">
+ <path style="&st4;" d="M0,329.44v54.277h46.048c4.2-8.92,8.594-17.756,13.178-26.505C38.174,350.267,18.309,340.894,0,329.44z"/>
+ </g>
+ <g id="top_x0020_small">
+ <path style="&st4;" d="M447.704,0h-60.913c4.221,8.005,8.008,16.252,11.347,24.709C414.315,16.037,430.844,7.792,447.704,0z"/>
+ </g>
+ </g>
+</svg>
diff --git a/kdm/kfrontend/themes/circles/circles.xml b/kdm/kfrontend/themes/circles/circles.xml new file mode 100644 index 000000000..0596e0ee7 --- /dev/null +++ b/kdm/kfrontend/themes/circles/circles.xml @@ -0,0 +1,207 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE greeter SYSTEM "greeter.dtd"> +<greeter> + <item type="svg"> + <normal file="background.svg"/> + <pos x="0" y="0" width="100%" height="-75"/> + </item> + <item type="rect"> + <normal color="#000000"/> + <pos x="0" y="-75" width="100%" height="75"/> + <fixed> + <item type="rect"> + <normal color="#ffffff"/> + <pos x="0" y="4" width="100%" height="100%"/> + <box orientation="horizontal" spacing="10" xpadding="10"> + <item type="rect" id="language_button" button="true"> + <normal color="#ffffff"/> + <pos y="50%" anchor="w" width="box" height="box"/> + <box orientation="horizontal" spacing="10" xpadding="10"> + <item type="pixmap"> + <normal file="options.png" tint="#dddddd"/> + <prelight file="options.png"/> + <active file="options.png" tint="#ff0000"/> + <pos y="50%" anchor="w"/> + </item> + <item type="label"> + <normal color="#000000" font="Sans 12"/> + <prelight color="#666666" font="Sans 12"/> + <active color="#ff0000" font="Sans 12"/> + <pos y="50%" anchor="w"/> + <!-- Stock label for: _Language --> + <stock type="language"/> + </item> + </box> + </item> + <item type="rect" id="session_button" button="true"> + <normal color="#ffffff"/> + <pos y="50%" anchor="w" width="box" height="box"/> + <box orientation="horizontal" spacing="10" xpadding="10"> + <item type="pixmap"> + <normal file="help.png" tint="#dddddd"/> + <prelight file="help.png"/> + <active file="help.png" tint="#ff0000"/> + <pos y="50%" anchor="w"/> + </item> + <item type="label"> + <normal color="#000000" font="Sans 12"/> + <prelight color="#666666" font="Sans 12"/> + <active color="#ff0000" font="Sans 12"/> + <pos y="50%" anchor="w"/> + <!-- Stock label for: _Session --> + <stock type="session"/> + </item> + </box> + </item> + <item type="rect" id="system_button" button="true"> + <normal color="#ffffff"/> + <show modes="console" type="system"/> + <pos y="50%" anchor="w" width="box" height="box"/> + <box orientation="horizontal" spacing="10" xpadding="10"> + <item type="pixmap"> + <normal file="options.png" tint="#dddddd"/> + <prelight file="options.png"/> + <active file="options.png" tint="#ff0000"/> + <pos y="50%" anchor="w"/> + </item> + <item type="label"> + <normal color="#000000" font="Sans 12"/> + <prelight color="#666666" font="Sans 12"/> + <active color="#ff0000" font="Sans 12"/> + <pos y="50%" anchor="w"/> + <!-- Stock label for: _Actions --> + <stock type="system"/> + </item> + </box> + </item> + <item type="rect" id="disconnect_button" button="true"> + <normal color="#ffffff"/> + <show modes="flexi,remote"/> + <pos y="50%" anchor="w" width="box" height="box"/> + <box orientation="horizontal" spacing="10" xpadding="10"> + <item type="pixmap"> + <normal file="options.png" tint="#dddddd"/> + <prelight file="options.png"/> + <active file="options.png" tint="#ff0000"/> + <pos y="50%" anchor="w"/> + </item> + <item type="label"> + <normal color="#000000" font="Sans 12"/> + <prelight color="#666666" font="Sans 12"/> + <active color="#ff0000" font="Sans 12"/> + <pos y="50%" anchor="w"/> + <!-- Stock label for: D_isconnect --> + <stock type="disconnect"/> + <show modes="remote"/> + </item> + <item type="label"> + <normal color="#000000" font="Sans 12"/> + <prelight color="#666666" font="Sans 12"/> + <active color="#ff0000" font="Sans 12"/> + <pos y="50%" anchor="w"/> + <!-- Stock label for: _Quit --> + <stock type="quit"/> + <show modes="flexi"/> + </item> + </box> + </item> + </box> + </item> + </fixed> + </item> + <item type="pixmap"> + <normal file="flower.png"/> + <pos x="100%" y="100%" anchor="se"/> + </item> + <item type="label" id="clock"> + <normal color="#000000" font="Sans 12"/> + <pos x="-20" y="-37" anchor="e"/> + <text>%c</text> + </item> + + <item type="rect" id="caps-lock-warning"> + <normal color="#FFFFFF" alpha="0.5"/> + <pos anchor="c" x="50%" y="75%" width="box" height="box"/> + <box orientation="vertical" min-width="400" xpadding="10" ypadding="5" spacing="0"> + <item type="label"> + <normal color="#000000" font="Sans 12"/> + <pos x="50%" anchor="n"/> + <!-- Stock label for: You've got capslock on! --> + <stock type="caps-lock-warning"/> + </item> + </box> + </item> + + <item type="rect"> + <show type="timed"/> + <normal color="#FFFFFF" alpha="0.5"/> + <pos anchor="c" x="50%" y="25%" width="box" height="box"/> + <box orientation="vertical" min-width="400" xpadding="10" ypadding="5" spacing="0"> + <item type="label" id="timed-label"> + <normal color="#000000" font="Sans 12"/> + <pos x="50%" anchor="n"/> + <!-- Stock label for: User %s will login in %d seconds --> + <stock type="timed-label"/> + </item> + </box> + </item> + + <item type="rect"> + <normal color="#FFFFFF" alpha="0.5"/> + <pos anchor="c" x="50%" y="50%" width="box" height="box"/> + <box orientation="vertical" min-width="340" xpadding="30" ypadding="30" spacing="10"> + <item type="label"> + <pos anchor="n" x="50%"/> + <normal color="#000000" font="Sans 14"/> + <!-- Stock label for: Welcome to %h --> + <stock type="welcome-label"/> + </item> + <item type="rect" id="talker"> + <normal color="#0" alpha="0"/> + <pos anchor="n" x="50%" width="box" height="box"/> + <!-- box orientation="vertical" xpadding="0" ypadding="0" spacing="10" --> + <box orientation="horizontal" xpadding="0" ypadding="0" spacing="0"> + <item type="rect"> + <normal color="#FF8080" alpha="0.0"/> + <pos anchor="w" y="50%" width="box" height="box"/> + <box orientation="vertical" xpadding="0" ypadding="0" spacing="14"> + <item type="label"> + <pos anchor="ne" x="100%"/> + <normal color="#000000" font="Sans 12"/> + <!-- Stock label for: Username: --> + <stock type="username-label"/> + </item> + <item type="label"> + <pos anchor="ne" x="100%"/> + <normal color="#000000" font="Sans 12"/> + <!-- Stock label for: Password: --> + <stock type="password-label"/> + </item> + </box> + </item> + <item type="rect"> + <normal color="#FF80FF" alpha="0.0"/> + <pos anchor="w" y="50%" width="box" height="box"/> + <box orientation="vertical" xpadding="0" ypadding="0" spacing="10"> + <item type="entry" id="user-entry"> + <pos anchor="n" x="50%" height="24" width="150"/> + </item> + <item type="entry" id="pw-entry"> + <pos anchor="n" x="50%" height="24" width="150"/> + </item> + </box> + </item> + </box> + </item> + </box> + <fixed> + <item type="label" id="pam-error"> + <pos anchor="n" x="50%" y="110%"/> + <normal color="#000000" font="Sans 12"/> + <text></text> + </item> + </fixed> + </item> +</greeter> + + diff --git a/kdm/kfrontend/themes/circles/flower.png b/kdm/kfrontend/themes/circles/flower.png Binary files differnew file mode 100644 index 000000000..92d25f32d --- /dev/null +++ b/kdm/kfrontend/themes/circles/flower.png diff --git a/kdm/kfrontend/themes/circles/help.png b/kdm/kfrontend/themes/circles/help.png Binary files differnew file mode 100644 index 000000000..b38b48a6d --- /dev/null +++ b/kdm/kfrontend/themes/circles/help.png diff --git a/kdm/kfrontend/themes/circles/options.png b/kdm/kfrontend/themes/circles/options.png Binary files differnew file mode 100644 index 000000000..3c08e02d4 --- /dev/null +++ b/kdm/kfrontend/themes/circles/options.png diff --git a/kdm/kfrontend/themes/circles/screenshot.png b/kdm/kfrontend/themes/circles/screenshot.png Binary files differnew file mode 100644 index 000000000..7120b03d5 --- /dev/null +++ b/kdm/kfrontend/themes/circles/screenshot.png |