1
0
Fork 0

Adding upstream version 48.0.

Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
This commit is contained in:
Daniel Baumann 2025-06-22 19:58:09 +02:00
parent e818d8977d
commit f9d3f51ece
Signed by: daniel.baumann
GPG key ID: BCC918A2ABD66424
244 changed files with 107813 additions and 0 deletions

69
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,69 @@
include:
- project: "GNOME/citemplates"
file: "templates/default-rules.yml"
- component: gitlab.gnome.org/GNOME/citemplates/gnomeos-basic-ci@master
inputs:
job-name: "build-gnomeos"
- component: gitlab.gnome.org/GNOME/citemplates/gnomeos-basic-ci@master
inputs:
job-name: "build-gnomeos-no-x11"
meson-options: "-Dx11=false"
- component: gitlab.gnome.org/GNOME/citemplates/release-service@master
inputs:
dist-job-name: "build-gnomeos"
tarball-artifact-path: "_builddir/meson-dist/$CI_PROJECT_NAME-$CI_COMMIT_TAG.tar.xz"
.build:
stage: "build"
variables:
_DEST: "$CI_PROJECT_DIR/destdir/"
script:
- meson setup _build $MESON_OPTIONS
- meson compile -C _build
- meson test -C _build --print-errorlogs --no-stdsplit --no-rebuild
- meson dist -C _build --no-tests
- mkdir -p $_DEST
- meson install -C _build --no-rebuild --destdir="$_DEST"
after_script:
# Cleanup the destdir and thus container volume once we are done
- rm -rvf $_DEST
artifacts:
expire_in: "2 days"
when: "always"
paths:
- "_build/meson-logs/"
reports:
junit: "_build/meson-logs/testlog.junit.xml"
build-fedora:
extends: ".build"
image: "fedora:40"
variables:
FEDORA_MESON_OPTIONS: "--prefix=/usr -Ddocbook=false -Dman=false"
COMMON_DEPENDENCIES: >-
gcc
git
json-glib-devel
meson
systemd
systemd-devel
gtk3-devel
gnome-desktop3-devel
DEPS_X11: >-
$COMMON_DEPENDENCIES
libX11-devel
libSM-devel
libICE-devel
xorg-x11-xtrans-devel
libglvnd-devel
libepoxy-devel
libXcomposite-devel
before_script:
- dnf install -y --setopt=install_weak_deps=false $DEPENDENCIES
parallel:
matrix:
- MESON_OPTIONS: "$FEDORA_MESON_OPTIONS"
DEPENDENCIES: "$DEPS_X11"
- MESON_OPTIONS: "$FEDORA_MESON_OPTIONS -Dx11=false"
DEPENDENCIES: "$COMMON_DEPENDENCIES"

22
AUTHORS Normal file
View file

@ -0,0 +1,22 @@
new code base authors
---------------------
Dan Winship <danw@gnome.org>
Lucas Rocha <lucasr@gnome.org>
William Jon McCann <jmccann@redhat.com>
original gnome-session authors
------------------------------
Tom Tromey <tromey@cygnus.com>
Felix Bellaby <felix@pooh.u-net.com>
smproxy authors
---------------
Ralph Mor, X Consortium
with modifications from
Tom Tromey <tromey@cygnus.com>
Felix Bellaby <felix@pooh.u-net.com>

339
COPYING Normal file
View file

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

50
ChangeLog Normal file
View file

@ -0,0 +1,50 @@
=== ChangeLog discontinued ===
With the move to git, this module is switching from a ChangeLog file to
relying on commit messages to provide change history. Please write commit
messages in the format described at http://live.gnome.org/Git/CommitMessages
Below is a copy of this format:
=== begin example commit ===
tag: Short explanation of the commit
Longer explanation explaining exactly what's changed, whether any
external or private interfaces changed, what bugs were fixed (with bug
tracker reference if applicable) and so forth. Be concise but not too brief.
=== end example commit ===
- The commit message is mainly for the other people, so they should be able
to understand it now and six months later.
- Always add a brief description of the commit to the _first_ line of the
commit and terminate by two newlines (it will work without the second
newline, but that is not nice for the interfaces).
- First line (the brief description) must only be one sentence and should
start with a capital letter unless it starts with a lowercase symbol or
identifier. Don't use a trailing period either. Don't exceed 72 characters.
- You can prefix the first line with one tag, to make it easier to know to
which part of the module the commit applies. For example, a commit with
"fish: Make it work with newer fortune" in the gnome-panel module clearly
applies to the fish applet.
- The main description (the body) is normal prose and should use normal
punctuation and capital letters where appropriate. Normally, for patches
sent to a mailing list, the body is copied from there. This main
description can be empty if the change is self-explanatory (eg: "Add DOAP
file").
- When committing code on behalf of others use the --author option, e.g. git
commit -a --author "Joe Coder <joe@coder.org>".
- When referring to a bug, you can use this form: bgo#12345. Use bgo for
bugzilla.gnome.org, but you can also reference bugs in other bug trackers:
rh means bugzilla.redhat.com, bnc means bugzilla.novell.com, lp means
launchpad.net, etc. Whenever possible, use the full URL of the bug, though.
- When a commit closes a bug, the commit message should contain a line like:
Closes: http://bugzilla.gnome.org/show_bug.cgi?id=12345
or simply:
http://bugzilla.gnome.org/show_bug.cgi?id=12345

2587
ChangeLog-20020212 Normal file

File diff suppressed because it is too large Load diff

1598
ChangeLog-20080310 Normal file

File diff suppressed because it is too large Load diff

4082
ChangeLog.pre-git Normal file

File diff suppressed because it is too large Load diff

24
MAINTAINERS Normal file
View file

@ -0,0 +1,24 @@
Currently active maintainers
----------------------------
Ray Strode
E-mail: rstrode@redhat.com
Userid: halfline
William Jon McCann
E-mail: mccann@jhu.edu
Userid: mccann
Non-active maintainers, who have a good understanding of the code
-----------------------------------------------------------------
#Vincent Untz
#E-mail: vuntz@gnome.org
#Userid: vuntz
#Lucas Rocha
#E-mail: lucasr@gnome.org
#Userid: lucasr
#Mark McLoughlin
#E-mail: mark@skynet.ie

175
NEWS Normal file
View file

@ -0,0 +1,175 @@
============
Version 48.0
============
The gnome-session script no longer re-runs itself as a subprocess
GNOME OS CI was ported to use a CI template
Integrated new CI-based release process
Fix bugs caused by systemd v257's inhibitor behavior changes
Make `gnome-session --version` print to stdout rather than syslog
Translation updates
README updates
==============
Version 47.0.1
==============
Add back gnome-session-ctl (oops)
============
Version 47.0
============
Allow to disable X11 sessions
CI updates
Translation updates
============
Version 46.0
============
README updates
Translation updates
=============
Version 46.rc
=============
Fix for race a shutdown with loginctl terminate-user
Updates for Access portal changes
Translation updates
================
Version 46.alpha
================
Make ctrl-c behave better in gnome-session-inhibit
Advertise support for headless session
Build goo cleanups
Copyright header cleanups
README cleanups
Translation updates
============
Version 45.0
============
Two crashers fixes
Fix inverted boolean in the "null" system backend
Translation updates
============
Version 44.0
============
No updates
=============
Version 44.rc
=============
Code clean ups
Better handling when dbus-daemon dies
Improved log output
Translation updates
============
Version 43.0
============
Build system improvements
GError clean up fix
Translation updates
============
Version 42.0
============
Give better error when failing to get renderer string
Fix failure to run sessions for program accounts
Translation updates
============
Version 40.8
============
data: Install GNOME on Wayland session for X11 preferred setups
Don't spew as much into log when falling back to non-systemd sessions
Work better with certain versions of meson
Correct screwed up check for gnome-shell
Various cleanups and leak fixes
Translation updates
==============
Version 40.1.1
==============
presence: Fix error detection when syncing initial screensaver state
============
Version 40.1
============
presence: Do not auto-start the GNOME Shell screensaver service
util: Unset LC_* variables as they may be unset at login time
============
Version 40.0
============
util: Remove undesired variables from activation environment
===============
Version 40.beta
===============
gnome-session: GNOME session will now only block sleep for suspend
inhibitors and will block shutdown if a logout inhibitor is
active.
Previously a suspend inhibitor would prevent both sleep and
logout. As such, applications may need to be updated and grab
both a logout and suspend inhibitor.
data: Re-add OnFailure= to .target units
util: Disable capturing of subpatterns
util: Only accept common space characters
Stop dbus-daemon instead of restarting it
data: Fix indirect conflict with exit.target via app.slice
Translations updates
==============
Version 3.38.0
==============
Build goo updates
Translations Updates
==============
Version 3.37.0
==============
util: Unset GNOME_SETUP_DISPLAY at login time
main: clear environment on shutdown
gnome-session: avoid setting LC_ unless LANG and region disagree
data: Add drop-in to configure launched applications
autostart-app: Place launched applications into a systemd scope
doc: Add description of important systemd units to man page
fail-whale-dialog: Make dialog visible in all monitors
gnome-session-inhibit: Add --list command
gnome-session-ctl: Add systemd service failure checker
data: Major changes in systemd configuration
Translation Updates: Brazilian Portuguese, Catalan, Chinese (Taiwan),
Czech, Friulian, German, Hebrew, Indonesian, Kazakh, Norwegian Bokmål,
Polish, Romanian, Slovak, Spanish, Swedish, Turkish, Ukrainian
==============
Version 3.36.0
==============
data: Require UsbProtection component
doap: Call this "GNOME Session Manager"
Translation Updates: Indonesian, Punjabi
==============
Version 3.35.3
==============
binary: Allow quitting early on SIGTERM/SIGINT
binary: Log a critical when our SIGTERM/SIGINT handler fails to log out
Translation updates: Chinese (China), Galician

66
README.md Normal file
View file

@ -0,0 +1,66 @@
# GNOME Session Manager
The GNOME session manager is in charge of starting the core components of the
GNOME desktop, and applications that should be launched at login time. This
module is also a natural place for various configuration files that define
important settings that are applied to the GNOME desktop, such as selecting
which xdg-desktop-portal backends to use.
You may download updates to the package from: http://download.gnome.org/sources/gnome-session/
## Contributing
To discuss `gnome-session`, you should use the
[GNOME support forum](https://discourse.gnome.org/c/platform/).
`gnome-session` development happens on
[GNOME's GitLab](http://gitlab.gnome.org/GNOME/gnome-session).
You will need to create an account to contribute.
Bugs should be reported to the
[`gnome-session` issue tracker](https://gitlab.gnome.org/GNOME/gnome-session/-/issues/).
Please read the
[GNOME Handbook's Guidance](https://handbook.gnome.org/issues/reporting.html)
on how to prepare a useful bug report.
Patches can be contributed by
[opening a merge request](https://gitlab.gnome.org/GNOME/gnome-session/-/merge_requests).
Please read the
[GNOME Handbook's Guidance](https://handbook.gnome.org/development.html)
on how to prepare a successful merge request.
## Building and Installing
Before you can build `gnome-session`, you need the following dependencies:
- A C compiler
- Meson
- Ninja
- `json-glib`
- `systemd`
- `gtk3`
- `gnome-desktop3`
If you're building with X11 support, you'll also need:
- `libX11`
- `libSM`
- `libICE`
- `xtrans`
- `libglvnd`
- `libepoxy`
- `libXcomposite`
Once you have all the necessary dependencies, you can use Meson to build
`gnome-session`:
```
$ meson setup _build
$ meson compile -C_build
```
And finally, you can use Meson to install `gnome-session` to your system:
```
$ sudo meson install -C_build
```

View file

@ -0,0 +1,5 @@
[Desktop Entry]
Name=Custom
Comment=This entry lets you select a saved session
Exec=@bindir@/gnome-session-custom-session
TryExec=@bindir@/gnome-session-custom-session

View file

@ -0,0 +1,2 @@
[GNOME Session]
Name=GNOME dummy

View file

@ -0,0 +1,6 @@
[Unit]
CollectMode=inactive-or-failed
PartOf=graphical-session.target
[Scope]
TimeoutStopSec=5s

3
data/gnome-portals.conf Normal file
View file

@ -0,0 +1,3 @@
[preferred]
default=gnome;gtk;
org.freedesktop.impl.portal.Secret=gnome-keyring;

View file

@ -0,0 +1,19 @@
[Unit]
Description=GNOME Session Failed lockdown screen (user)
OnFailure=gnome-session-shutdown.target
OnFailureJobMode=replace-irreversibly
CollectMode=inactive-or-failed
PartOf=gnome-session-failed.target
# We could do this, but it requires an intermediate target for OnFailure
# handling, so gnome-session-failed checks RUNNING_UNDER_GDM itself
#Conflicts=gnome-session@gnome-login.target
# or in the case of GDM and then not passing --allow-logout
#Requisite=gnome-session@gnome-login.target
[Service]
Type=simple
ExecStart=@libexecdir@/gnome-session-failed --allow-logout
# The fail whale doesn't trigger a shutdown itself, so do it here
ExecStopPost=-@libexecdir@/gnome-session-ctl --shutdown

View file

@ -0,0 +1,12 @@
[Unit]
Description=GNOME Session Failed
OnFailure=gnome-session-shutdown.target
OnFailureJobMode=replace-irreversibly
# We need an initialized session
Requisite=gnome-session-initialized.target
BindsTo=gnome-session-initialized.target
After=gnome-session-initialized.target
BindsTo=gnome-session-failed.service
After=gnome-session-failed.service

View file

@ -0,0 +1,19 @@
[Unit]
Description=GNOME Session is initialized
OnFailure=gnome-session-shutdown.target
OnFailureJobMode=replace-irreversibly
DefaultDependencies=no
RefuseManualStart=yes
RefuseManualStop=yes
Requires=gnome-session-pre.target
After=gnome-session-pre.target
Requisite=gnome-session.target
PartOf=gnome-session.target
Before=gnome-session.target
# Signal gnome-session that we reached the initialized target and
# that it may start applications.
Requires=gnome-session-signal-init.service
Before=gnome-session-signal-init.service

View file

@ -0,0 +1,12 @@
[Unit]
Description=GNOME Session Manager is ready
DefaultDependencies=no
RefuseManualStart=yes
RefuseManualStop=yes
Requisite=gnome-session-pre.target
After=gnome-session-pre.target
Requisite=gnome-session-initialized.target
PartOf=gnome-session-initialized.target
Before=gnome-session-initialized.target

View file

@ -0,0 +1,19 @@
[Unit]
Description=GNOME Session Manager (session: %i)
RefuseManualStart=yes
RefuseManualStop=yes
OnFailure=gnome-session-shutdown.target
OnFailureJobMode=replace-irreversibly
CollectMode=inactive-or-failed
Requisite=gnome-session-pre.target
After=gnome-session-pre.target
Requires=gnome-session-manager.target
PartOf=gnome-session-manager.target
Before=gnome-session-manager.target
[Service]
Type=notify
ExecStart=@libexecdir@/gnome-session-binary --systemd-service --session=%i
ExecStopPost=-@libexecdir@/gnome-session-ctl --shutdown

View file

@ -0,0 +1,15 @@
[Unit]
Description=Monitor Session leader for GNOME Session
CollectMode=inactive-or-failed
# All services started after gnome-session-pre.target need to be torn down
# before the session finish can be signalled back to the display manager.
PartOf=gnome-session-pre.target
Before=gnome-session-pre.target
# No After, as we want this to start up immediately
[Service]
Type=notify
ExecStart=@libexecdir@/gnome-session-ctl --monitor
TimeoutStopSec=5

View file

@ -0,0 +1,14 @@
[Unit]
Description=Tasks to be run before GNOME Session starts
OnFailure=gnome-session-shutdown.target
OnFailureJobMode=replace-irreversibly
DefaultDependencies=no
RefuseManualStart=yes
RefuseManualStop=yes
Requires=graphical-session-pre.target
After=graphical-session-pre.target
Requisite=gnome-session-initialized.target
PartOf=gnome-session-initialized.target
Before=gnome-session-initialized.target

View file

@ -0,0 +1,11 @@
[Unit]
Description=Restart DBus after GNOME Session shutdown
# Allow exit.target to start even if this unit is started with replace-irreversibly.
# For this to work, we also need to be in the root slice.
DefaultDependencies=no
[Service]
Type=notify
ExecStart=@libexecdir@/gnome-session-ctl --restart-dbus
Slice=-.slice

View file

@ -0,0 +1,33 @@
[Unit]
Description=Shutdown running GNOME Session
# Allow exit.target to start even if this unit is started with replace-irreversibly.
# All (weak) dependencies need to do the same. Services also need to ensure they
# are in the root slice by setting Slice=-.slice.
DefaultDependencies=no
Conflicts=graphical-session.target graphical-session-pre.target
After=graphical-session.target graphical-session-pre.target
# Add explicit conflicts/after lines for gnome-session targets, technically
# this should not be needed, but is an extra safety measure.
Conflicts=gnome-session.target gnome-session-manager.target
After=gnome-session.target gnome-session-manager.target
Conflicts=gnome-session-pre.target gnome-session-initialized.target gnome-session-failed.target
After=gnome-session-pre.target gnome-session-initialized.target gnome-session-failed.target
# We need to make sure this unit is stopped; primarily so that the tree of
# units that we created is completely cleaned.
# Note that this can also be improved by reversing the conflicts above and
# not listing them in the shutdown unit.
StopWhenUnneeded=true
# We trigger a restart of DBus after reaching the shutdown target this
# is a workaround so that DBus services that do not connect to the
# display server are shut down after log-out.
# This should be removed when the relevant services add a
# PartOf=graphical-session.target
# Historic bug: https://bugzilla.gnome.org/show_bug.cgi?id=764029
Wants=gnome-session-restart-dbus.service
Before=gnome-session-restart-dbus.service

View file

@ -0,0 +1,8 @@
[Unit]
Description=Signal initialization done to GNOME Session Manager
PartOf=gnome-session.target
[Service]
Type=oneshot
ExecStart=@libexecdir@/gnome-session-ctl --signal-init

View file

@ -0,0 +1,13 @@
[Unit]
Description=GNOME Wayland Session
# On wayland all is lost, do a shutdown
OnFailure=gnome-session-shutdown.target
OnFailureJobMode=replace-irreversibly
# Avoid default After/Before rules
DefaultDependencies=no
Before=gnome-session.target
PartOf=graphical-session.target
RefuseManualStart=yes
RefuseManualStop=yes

View file

@ -0,0 +1,20 @@
[Unit]
Description=GNOME Wayland Session (session: %i)
DefaultDependencies=no
# Start happens explicitly
RefuseManualStart=no
# Stop happens by starting gnome-session-shutdown.target
RefuseManualStop=yes
Conflicts=shutdown.target gnome-session-shutdown.target
PartOf=graphical-session.target
# As this is the main entry point, pull in the other toplevel gnome-session targets
Requires=gnome-session@.target
After=gnome-session@.target
Requires=gnome-session-wayland.target
After=gnome-session-wayland.target
Requires=gnome-session.target
After=gnome-session.target

View file

@ -0,0 +1,8 @@
[Unit]
Description=GNOME session X11 services
DefaultDependencies=no
BindsTo=gnome-session-x11-services.target
After=gnome-session-x11-services.target
Before=gnome-session.target

View file

@ -0,0 +1,11 @@
[Unit]
Description=GNOME session X11 services
DefaultDependencies=no
Requisite=gnome-session-initialized.target
After=gnome-session-initialized.target
PartOf=gnome-session-initialized.target
Requisite=gnome-session-x11-services-ready.target
Before=gnome-session-x11-services-ready.target
PartOf=gnome-session-x11-services-ready.target

View file

@ -0,0 +1,16 @@
[Unit]
Description=GNOME X11 Session
# On X11, try to show the fail screen
OnFailure=gnome-session-failed.target
OnFailureJobMode=replace
# Avoid default After/Before rules
DefaultDependencies=no
Before=gnome-session.target
PartOf=graphical-session.target
RefuseManualStart=yes
RefuseManualStop=yes
# Pull in all X11-specific services the session might depend on
Requires=gnome-session-x11-services.target

View file

@ -0,0 +1,22 @@
[Unit]
Description=GNOME X11 Session (session: %i)
OnFailure=gnome-session-failed.target
OnFailureJobMode=replace
DefaultDependencies=no
# Start happens explicitly
RefuseManualStart=no
# Stop happens by starting gnome-session-shutdown.target
#RefuseManualStop=yes
Conflicts=shutdown.target gnome-session-shutdown.target
PartOf=graphical-session.target
# As this is the main entry point, pull in the other toplevel gnome-session targets
BindsTo=gnome-session@.target
After=gnome-session@.target
BindsTo=gnome-session-x11.target
After=gnome-session-x11.target
BindsTo=gnome-session.target
After=gnome-session.target

15
data/gnome-session.target Normal file
View file

@ -0,0 +1,15 @@
[Unit]
Description=GNOME Session
OnFailure=gnome-session-failed.target
OnFailureJobMode=replace
DefaultDependencies=no
RefuseManualStart=yes
RefuseManualStop=yes
BindsTo=graphical-session.target
Before=graphical-session.target
# gnome-session-monitor.service will quit with the session leader process
# gnome-session.target pulls in graphical-session.target
BindsTo=gnome-session-monitor.service
After=gnome-session-monitor.service

View file

@ -0,0 +1,16 @@
[Unit]
Description=GNOME Session (session: %i)
OnFailure=gnome-session-failed.target
OnFailureJobMode=replace
DefaultDependencies=no
RefuseManualStart=yes
RefuseManualStop=yes
Requires=gnome-session-initialized.target
After=gnome-session-initialized.target
Requisite=gnome-session.target
PartOf=gnome-session.target
Before=gnome-session.target
Requires=gnome-session-manager@.service

View file

@ -0,0 +1,9 @@
[Desktop Entry]
Name=GNOME on Wayland
Comment=This session logs you into GNOME
Exec=@bindir@/gnome-session
TryExec=@bindir@/gnome-session
Type=Application
DesktopNames=GNOME
X-GDM-SessionRegisters=true
X-GDM-CanRunHeadless=true

View file

@ -0,0 +1,8 @@
[Desktop Entry]
Name=GNOME on Xorg
Comment=This session logs you into GNOME
Exec=@bindir@/gnome-session
TryExec=@bindir@/gnome-session
Type=Application
DesktopNames=GNOME
X-GDM-SessionRegisters=true

9
data/gnome.desktop.in.in Normal file
View file

@ -0,0 +1,9 @@
[Desktop Entry]
Name=GNOME
Comment=This session logs you into GNOME
Exec=@bindir@/gnome-session
TryExec=@bindir@/gnome-session
Type=Application
DesktopNames=GNOME
X-GDM-SessionRegisters=true
X-GDM-CanRunHeadless=true

View file

@ -0,0 +1,5 @@
[Unit]
# Must be in sync with gnome.session
@wants_required_components@
Requires=@requires_component@.target

View file

@ -0,0 +1,4 @@
[GNOME Session]
Name=GNOME
# Must be in sync with gnome-session@gnome.target.d/gnome.session.conf drop-in
RequiredComponents=@required_components@;

View file

@ -0,0 +1,32 @@
##
## This file contains a list of blacklist/whitelist regular expressions for
## renderer strings.
##
## The regular expressions are case-insensitive POSIX Extended Regular
## Expressions. See regex(7) for details.
##
## Syntax:
## - Comment lines start with '#'
## - Lines starting with '+' are whitelisting.
## - Lines starting with '-' are blacklisting.
## - Lines not starting with '#', '+', '-' are ignored.
##
# Intel 830-865
-Intel\(R\) 8[[:digit:]]{2,2}[^[:digit:]]
# Intel IGD
-Intel IGD
# Pre-R300 radeon
-Mesa DRI R[12]00[^[:digit:]]
-Mesa DRI R[12]00$
# Old Mesa software GL renderer
-software rasterizer
# Gallium has softpipe; we explicitly enable llvmpipe
-softpipe
# nouveau vieux NV25 doesn't work too well
-Mesa DRI nv25

194
data/meson.build Normal file
View file

@ -0,0 +1,194 @@
desktop_plain = 'gnome'
desktops = [
desktop_plain,
'gnome-wayland',
]
if have_x11
desktops += ['gnome-xorg']
endif
shell_component = {
desktop_plain: 'org.gnome.Shell',
}
required_components = {
desktop_plain: [
'org.gnome.SettingsDaemon.A11ySettings',
'org.gnome.SettingsDaemon.Color',
'org.gnome.SettingsDaemon.Datetime',
'org.gnome.SettingsDaemon.Housekeeping',
'org.gnome.SettingsDaemon.Keyboard',
'org.gnome.SettingsDaemon.MediaKeys',
'org.gnome.SettingsDaemon.Power',
'org.gnome.SettingsDaemon.PrintNotifications',
'org.gnome.SettingsDaemon.Rfkill',
'org.gnome.SettingsDaemon.ScreensaverProxy',
'org.gnome.SettingsDaemon.Sharing',
'org.gnome.SettingsDaemon.Smartcard',
'org.gnome.SettingsDaemon.Sound',
'org.gnome.SettingsDaemon.UsbProtection',
'org.gnome.SettingsDaemon.Wacom',
'org.gnome.SettingsDaemon.XSettings',
],
}
if enable_session_selector
desktops += 'gnome-custom-session'
endif
foreach name: desktops
desktop_conf = configuration_data()
desktop_conf.set('bindir', session_bindir)
desktop = name + '.desktop'
desktop_in = configure_file(
input: desktop + '.in.in',
output: desktop + '.in',
configuration: desktop_conf
)
if name.endswith('-xorg') and have_x11
install_dir = session_datadir / 'xsessions'
elif name.endswith('-wayland')
install_dir = session_datadir / 'wayland-sessions'
else
# FIXME: The same target can not be copied into two directories.
# There is a workaround in meson_post_install.py until proper
# solution arises:
# https://github.com/mesonbuild/meson/issues/2416
install_dir = session_datadir / 'xsessions'
#install_dir = [
# join_paths(session_datadir, 'xsessions'),
# join_paths(session_datadir, 'wayland-sessions')
#]
endif
desktop_target = i18n.merge_file(
type: 'desktop',
input: desktop_in,
output: desktop,
po_dir: po_dir,
install: true,
install_dir: install_dir
)
endforeach
sessions = [
'gnome',
'gnome-dummy'
]
foreach session: sessions
session_file = session + '.session'
desktop_conf = configuration_data()
desktop_conf.set('libexecdir', session_libexecdir)
desktop_conf.set('required_components', ';'.join(
[shell_component.get(session, '')] + required_components.get(session, [])))
desktop = session_file + '.desktop'
desktop_in = configure_file(
input: desktop + '.in.in',
output: desktop + '.in',
configuration: desktop_conf
)
i18n.merge_file(
type: 'desktop',
input: desktop_in,
output: session_file,
po_dir: po_dir,
install: true,
install_dir: join_paths(session_pkgdatadir, 'sessions')
)
endforeach
install_data(
'org.gnome.SessionManager.gschema.xml',
install_dir: join_paths(session_datadir, 'glib-2.0', 'schemas'),
)
unit_conf = configuration_data()
unit_conf.set('libexecdir', session_libexecdir)
systemd_service = ['gnome-session-manager@.service',
'gnome-session-signal-init.service',
'gnome-session-restart-dbus.service',
'gnome-session-monitor.service',
'gnome-session-failed.service']
foreach service: systemd_service
configure_file(
input: service + '.in',
output: service,
install: true,
install_dir: systemd_userunitdir,
configuration: unit_conf
)
endforeach
systemd_target = files('gnome-session-wayland@.target',
'gnome-session-wayland.target',
'gnome-session@.target',
'gnome-session.target',
'gnome-session-pre.target',
'gnome-session-manager.target',
'gnome-session-initialized.target',
'gnome-session-shutdown.target',
'gnome-session-failed.target',
)
if have_x11
systemd_target += [
'gnome-session-x11@.target',
'gnome-session-x11.target',
'gnome-session-x11-services.target',
'gnome-session-x11-services-ready.target',
]
endif
install_data(
systemd_target,
install_dir: systemd_userunitdir
)
install_data(
'gnome-launched-override.scope.conf',
rename: 'override.conf',
install_dir : join_paths(systemd_userunitdir, 'gnome-launched-.scope.d')
)
foreach session, req_components: required_components
wanted_targets = []
foreach component: req_components
wanted_targets += 'Wants=@0@.target'.format(component)
endforeach
configure_file(
input: session + '.session.conf.in',
output: session + '.session.conf',
configuration: {
'requires_component': shell_component[session],
'wants_required_components': '\n'.join(wanted_targets),
},
install_dir: systemd_userunitdir / 'gnome-session@@0@.target.d'.format(
session),
)
endforeach
data = files('hardware-compatibility')
if enable_session_selector
data += files('session-selector.ui')
endif
install_data(
data,
install_dir: session_pkgdatadir
)
install_data(
'gnome-portals.conf',
install_dir: session_datadir / 'xdg-desktop-portal',
)

View file

@ -0,0 +1,24 @@
<schemalist gettext-domain="gnome-session-3.0">
<schema id="org.gnome.SessionManager" path="/org/gnome/gnome-session/">
<key name="auto-save-session" type="b">
<default>false</default>
<summary>Save sessions</summary>
<description>If enabled, gnome-session will save the session automatically.</description>
</key>
<key name="auto-save-session-one-shot" type="b">
<default>false</default>
<summary>Save this session</summary>
<description>When enabled, gnome-session will automatically save the next session at log out even if auto saving is disabled.</description>
</key>
<key name="logout-prompt" type="b">
<default>true</default>
<summary>Logout prompt</summary>
<description>If enabled, gnome-session will prompt the user before ending a session.</description>
</key>
<key name="show-fallback-warning" type="b">
<default>true</default>
<summary>Show the fallback warning</summary>
<description>If enabled, gnome-session will display a warning dialog after login if the session was automatically fallen back.</description>
</key>
</schema>
</schemalist>

195
data/session-selector.ui Normal file
View file

@ -0,0 +1,195 @@
<?xml version="1.0"?>
<interface>
<requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy project-wide -->
<object class="GtkListStore" id="session-store">
<columns>
<!-- column-name name -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkTreeModelSort" id="sort-model">
<property name="model">session-store</property>
</object>
<object class="GtkWindow" id="main-window">
<property name="title" translatable="yes">Custom Session</property>
<property name="window_position">center</property>
<property name="default_width">500</property>
<property name="default_height">310</property>
<property name="decorated">False</property>
<child>
<object class="GtkFrame" id="frame1">
<property name="visible">True</property>
<property name="label_xalign">0.5</property>
<property name="shadow_type">out</property>
<child>
<object class="GtkAlignment" id="alignment3">
<property name="visible">True</property>
<property name="border_width">12</property>
<child>
<object class="GtkVBox" id="vbox3">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkInfoBar" id="info-bar">
<property name="visible">True</property>
<property name="message-type">other</property>
<child internal-child="content_area">
<object class="GtkHBox" id="info-bar-content_area">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">0</property>
<child>
<object class="GtkLabel" id="info-label">
<property name="visible">True</property>
<property name="xalign">0.0</property>
<property name="yalign">0.5</property>
<property name="label" translatable="yes">Please select a custom session to run</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox4">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<child>
<object class="GtkHBox" id="hbox3">
<property name="visible">True</property>
<property name="spacing">12</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">never</property>
<property name="vscrollbar_policy">automatic</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="session-list">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="search_column">0</property>
<property name="model">sort-model</property>
</object>
</child>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkVButtonBox" id="vbuttonbox2">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<property name="layout_style">start</property>
<child>
<object class="GtkButton" id="new-session">
<property name="label" translatable="yes">_New Session</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="remove-session">
<property name="label" translatable="yes">_Remove Session</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="rename-session">
<property name="label" translatable="yes">Rena_me Session</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHButtonBox" id="hbuttonbox2">
<property name="visible">True</property>
<property name="spacing">6</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="continue-button">
<property name="label" translatable="yes">_Continue</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="receives_default">True</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</interface>

6
doc/dbus/config.xsl Normal file
View file

@ -0,0 +1,6 @@
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
version="1.0">
<xsl:param name="html.stylesheet" select="'docbook.css'"/>
</xsl:stylesheet>

View file

@ -0,0 +1,32 @@
<!-- DTD for D-Bus Introspection Documentation -->
<!ELEMENT doc (summary?,description?,errors?,permission?,since?,deprecated,seealso?)>
<!ELEMENT summary (#PCDATA|ref)*>
<!ELEMENT description (#PCDATA|para|example)*>
<!ELEMENT errors (error)*>
<!ELEMENT permission (#PCDATA|ref|para)*>
<!ELEMENT since EMPTY>
<!ATTLIST since version CDATA #REQUIRED>
<!ELEMENT deprecated (#PCDATA|ref)>
<!ATTLIST deprecated version CDATA #REQUIRED>
<!ATTLIST deprecated instead CDATA #REQUIRED>
<!ELEMENT seealso (ref+)>
<!ELEMENT error (#PCDATA|para)*>
<!ATTLIST error name CDATA #REQUIRED>
<!ELEMENT para (#PCDATA|example|code|list|ref)*>
<!ELEMENT example (#PCDATA|para|code|ref)*>
<!ATTLIST language (c|glib|python|shell) #REQUIRED>
<!ATTLIST title CDATA #IMPLIED>
<!ELEMENT list (item*)>
<!ATTLIST list type (bullet|number) #REQUIRED>
<!ELEMENT item (term|definition)*>
<!ELEMENT term (#PCDATA|ref)*>
<!ELEMENT definition (#PCDATA|para)*>
<!ELEMENT code (#PCDATA)>
<!ATTLIST code lang CDATA #IMPLIED>
<!ELEMENT ref CDATA>
<!ATTLIST ref type (parameter|arg|signal|method|interface) #REQUIRED>
<!ATTLIST ref to CDATA #REQUIRED>

78
doc/dbus/docbook.css Normal file
View file

@ -0,0 +1,78 @@
body
{
font-family: sans-serif;
}
h1.title
{
}
.permission
{
color: #ee0000;
text-decoration: underline;
}
.synopsis, .classsynopsis
{
background: #eeeeee;
border: solid 1px #aaaaaa;
padding: 0.5em;
}
.programlisting
{
background: #eeeeff;
border: solid 1px #aaaaff;
padding: 0.5em;
}
.variablelist
{
padding: 4px;
margin-left: 3em;
}
.variablelist td:first-child
{
vertical-align: top;
}
td.shortcuts
{
color: #770000;
font-size: 80%;
}
div.refnamediv
{
margin-top: 2em;
}
div.toc
{
border: 2em;
}
a
{
text-decoration: none;
}
a:hover
{
text-decoration: underline;
color: #FF0000;
}
div.table table
{
border-collapse: collapse;
border-spacing: 0px;
border-style: solid;
border-color: #777777;
border-width: 1px;
}
div.table table td, div.table table th
{
border-style: solid;
border-color: #777777;
border-width: 1px;
padding: 3px;
vertical-align: top;
}
div.table table th
{
background-color: #eeeeee;
}

View file

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
<!ENTITY dbus-Manager SYSTEM "org.gnome.SessionManager.ref.xml">
<!ENTITY dbus-Client SYSTEM "org.gnome.SessionManager.Client.ref.xml">
<!ENTITY dbus-ClientPrivate SYSTEM "org.gnome.SessionManager.ClientPrivate.ref.xml">
<!ENTITY dbus-Inhibitor SYSTEM "org.gnome.SessionManager.Inhibitor.ref.xml">
<!ENTITY dbus-Presence SYSTEM "org.gnome.SessionManager.Presence.ref.xml">
]>
<book id="index">
<bookinfo>
<title>GNOME Session @VERSION@ Documentation</title>
<releaseinfo>Version @VERSION@</releaseinfo>
<authorgroup>
<author>
<firstname>William Jon</firstname>
<surname>McCann</surname>
<affiliation>
<address>
<email>jmccann@redhat.com</email>
</address>
</affiliation>
</author>
</authorgroup>
</bookinfo>
<part>
<title>Reference</title>
<reference id="dbus-reference">
<title>D-Bus API Reference</title>
<partintro>
<para>
This API is not yet stable and is likely to change in the future.
</para>
</partintro>
&dbus-Manager;
&dbus-Client;
&dbus-ClientPrivate;
&dbus-Inhibitor;
&dbus-Presence;
</reference>
</part>
<index>
<title>Index</title>
</index>
</book>

48
doc/dbus/meson.build Normal file
View file

@ -0,0 +1,48 @@
xsltproc = find_program('xsltproc')
ifaces_refs = []
ifaces = [
'org.gnome.SessionManager',
'org.gnome.SessionManager.Client',
'org.gnome.SessionManager.ClientPrivate',
'org.gnome.SessionManager.Inhibitor',
'org.gnome.SessionManager.Presence'
]
gnome_session_dir = join_paths(meson.project_source_root(), 'gnome-session')
spec_to_docbook = files('spec-to-docbook.xsl')
foreach iface: ifaces
iface_ref = iface + '.ref.xml'
ifaces_refs += custom_target(
iface_ref,
input: files(join_paths(gnome_session_dir, iface + '.xml')),
output: iface_ref,
command: [xsltproc, '--output', '@OUTPUT@', spec_to_docbook, '@INPUT@']
)
endforeach
session_conf = configuration_data()
session_conf.set('VERSION', session_version)
session = meson.project_name()
xml_in = configure_file(
input: session + '.xml.in',
output: session + '.xml',
configuration: session_conf
)
config_xsl = files('config.xsl')
custom_target(
session,
input: xml_in,
output: session + '.html',
command: [find_program('xmlto'), 'xhtml-nochunks', '-o', meson.current_build_dir(), '-m', config_xsl, '@INPUT@'],
depends: ifaces_refs,
install: true,
install_dir: join_paths(session_datadir, 'doc', meson.project_name(), 'dbus')
)

View file

@ -0,0 +1,555 @@
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"
exclude-result-prefixes="doc">
<!--
Convert D-Bus Glib xml into DocBook refentries
Copyright (C) 2007-2008 William Jon McCann
License: GPL
-->
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<xsl:template match="/">
<xsl:variable name="interface" select="//interface/@name"/>
<xsl:variable name="basename">
<xsl:call-template name="interface-basename">
<xsl:with-param name="str" select="$interface"/>
</xsl:call-template>
</xsl:variable>
<refentry><xsl:attribute name="id"><xsl:value-of select="$interface"/></xsl:attribute>
<refmeta>
<refentrytitle role="top_of_page"><xsl:value-of select="//interface/@name"/></refentrytitle>
</refmeta>
<refnamediv>
<refname><xsl:value-of select="$interface"/></refname>
<refpurpose><xsl:value-of select="$basename"/> interface</refpurpose>
</refnamediv>
<refsynopsisdiv role="synopsis">
<title role="synopsis.title">Methods</title>
<synopsis>
<xsl:call-template name="methods-synopsis">
<xsl:with-param name="interface" select="$interface"/>
</xsl:call-template>
</synopsis>
</refsynopsisdiv>
<xsl:choose>
<xsl:when test="count(///signal) > 0">
<refsect1 role="signal_proto">
<title role="signal_proto.title">Signals</title>
<synopsis>
<xsl:call-template name="signals-synopsis">
<xsl:with-param name="interface" select="$interface"/>
</xsl:call-template>
</synopsis>
</refsect1>
</xsl:when>
</xsl:choose>
<refsect1 role="impl_interfaces">
<title role="impl_interfaces.title">Implemented Interfaces</title>
<para>
Objects implementing <xsl:value-of select="$interface"/> also implements
org.freedesktop.DBus.Introspectable,
org.freedesktop.DBus.Properties
</para>
</refsect1>
<xsl:choose>
<xsl:when test="count(///property) > 0">
<refsect1 role="properties">
<title role="properties.title">Properties</title>
<synopsis>
<xsl:call-template name="properties-synopsis">
<xsl:with-param name="interface" select="$interface"/>
</xsl:call-template>
</synopsis>
</refsect1>
</xsl:when>
</xsl:choose>
<refsect1 role="desc">
<title role="desc.title">Description</title>
<para>
<xsl:apply-templates select="//interface/doc:doc"/>
</para>
</refsect1>
<refsect1 role="details">
<title role="details.title">Details</title>
<xsl:call-template name="method-details">
<xsl:with-param name="interface" select="$interface"/>
</xsl:call-template>
</refsect1>
<xsl:choose>
<xsl:when test="count(///signal) > 0">
<refsect1 role="signals">
<title role="signals.title">Signal Details</title>
<xsl:call-template name="signal-details">
<xsl:with-param name="interface" select="$interface"/>
</xsl:call-template>
</refsect1>
</xsl:when>
</xsl:choose>
<xsl:choose>
<xsl:when test="count(///property) > 0">
<refsect1 role="property_details">
<title role="property_details.title">Property Details</title>
<xsl:call-template name="property-details">
<xsl:with-param name="interface" select="$interface"/>
</xsl:call-template>
</refsect1>
</xsl:when>
</xsl:choose>
</refentry>
</xsl:template>
<xsl:template name="property-doc">
<xsl:apply-templates select="doc:doc/doc:description"/>
<xsl:choose>
<xsl:when test="count(arg) > 0">
<variablelist role="params">
<xsl:for-each select="arg">
<varlistentry><term><parameter><xsl:value-of select="@name"/></parameter>:</term>
<listitem><simpara><xsl:apply-templates select="doc:doc/doc:summary"/></simpara></listitem>
</varlistentry>
</xsl:for-each>
</variablelist>
</xsl:when>
</xsl:choose>
<xsl:apply-templates select="doc:doc/doc:since"/>
<xsl:apply-templates select="doc:doc/doc:deprecated"/>
<xsl:apply-templates select="doc:doc/doc:permission"/>
<xsl:apply-templates select="doc:doc/doc:seealso"/>
</xsl:template>
<xsl:template name="property-details">
<xsl:param name="interface"/>
<xsl:variable name="longest">
<xsl:call-template name="find-longest">
<xsl:with-param name="set" select="@name"/>
</xsl:call-template>
</xsl:variable>
<xsl:for-each select="///property">
<refsect2>
<title><anchor role="function"><xsl:attribute name="id"><xsl:value-of select="$interface"/>:<xsl:value-of select="@name"/></xsl:attribute></anchor>The "<xsl:value-of select="@name"/>" property</title>
<indexterm><primary><xsl:value-of select="@name"/></primary><secondary><xsl:value-of select="$interface"/></secondary></indexterm>
<programlisting>'<xsl:value-of select="@name"/>'<xsl:call-template name="pad-spaces"><xsl:with-param name="width" select="2"/></xsl:call-template>
<xsl:call-template name="property-args"><xsl:with-param name="indent" select="string-length(@name) + 2"/></xsl:call-template></programlisting>
<xsl:call-template name="property-doc"/>
</refsect2>
</xsl:for-each>
</xsl:template>
<xsl:template name="signal-doc">
<xsl:apply-templates select="doc:doc/doc:description"/>
<xsl:choose>
<xsl:when test="count(arg) > 0">
<variablelist role="params">
<xsl:for-each select="arg">
<varlistentry><term><parameter><xsl:value-of select="@name"/></parameter>:</term>
<listitem><simpara><xsl:apply-templates select="doc:doc/doc:summary"/></simpara></listitem>
</varlistentry>
</xsl:for-each>
</variablelist>
</xsl:when>
</xsl:choose>
<xsl:apply-templates select="doc:doc/doc:since"/>
<xsl:apply-templates select="doc:doc/doc:deprecated"/>
<xsl:apply-templates select="doc:doc/doc:permission"/>
<xsl:apply-templates select="doc:doc/doc:seealso"/>
</xsl:template>
<xsl:template name="signal-details">
<xsl:param name="interface"/>
<xsl:variable name="longest">
<xsl:call-template name="find-longest">
<xsl:with-param name="set" select="@name"/>
</xsl:call-template>
</xsl:variable>
<xsl:for-each select="///signal">
<refsect2>
<title><anchor role="function"><xsl:attribute name="id"><xsl:value-of select="$interface"/>::<xsl:value-of select="@name"/></xsl:attribute></anchor>The <xsl:value-of select="@name"/> signal</title>
<indexterm><primary><xsl:value-of select="@name"/></primary><secondary><xsl:value-of select="$interface"/></secondary></indexterm>
<programlisting><xsl:value-of select="@name"/> (<xsl:call-template name="signal-args"><xsl:with-param name="indent" select="string-length(@name) + 2"/><xsl:with-param name="prefix" select="."/></xsl:call-template>)</programlisting>
<xsl:call-template name="signal-doc"/>
</refsect2>
</xsl:for-each>
</xsl:template>
<xsl:template match="doc:code">
<programlisting>
<xsl:apply-templates />
</programlisting>
</xsl:template>
<xsl:template match="doc:tt">
<literal>
<xsl:apply-templates />
</literal>
</xsl:template>
<xsl:template match="doc:i">
<emphasis>
<xsl:apply-templates />
</emphasis>
</xsl:template>
<xsl:template match="doc:b">
<emphasis role="bold">
<xsl:apply-templates />
</emphasis>
</xsl:template>
<xsl:template match="doc:ulink">
<ulink>
<xsl:attribute name="url"><xsl:value-of select="@url"/></xsl:attribute>
<xsl:value-of select="."/>
</ulink>
</xsl:template>
<xsl:template match="doc:summary">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="doc:example">
<informalexample>
<xsl:apply-templates />
</informalexample>
</xsl:template>
<xsl:template name="listitems-do-term">
<xsl:param name="str"/>
<xsl:choose>
<xsl:when test="string-length($str) > 0">
<emphasis role="bold"><xsl:value-of select="$str"/>: </emphasis>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="do-listitems">
<xsl:for-each select="doc:item">
<listitem>
<simpara>
<xsl:call-template name="listitems-do-term"><xsl:with-param name="str" select="doc:term"/></xsl:call-template>
<xsl:apply-templates select="doc:definition"/>
</simpara>
</listitem>
</xsl:for-each>
</xsl:template>
<xsl:template match="doc:list">
<xsl:choose>
<xsl:when test="contains(@type,'number')">
<orderedlist>
<xsl:call-template name="do-listitems"/>
</orderedlist>
</xsl:when>
<xsl:otherwise>
<itemizedlist>
<xsl:call-template name="do-listitems"/>
</itemizedlist>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="doc:para">
<para>
<xsl:apply-templates />
</para>
</xsl:template>
<xsl:template match="doc:description">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="doc:since">
<para role="since">Since <xsl:value-of select="@version"/>
</para>
</xsl:template>
<xsl:template match="doc:deprecated">
<xsl:variable name="name" select="../../@name"/>
<xsl:variable name="parent">
<xsl:call-template name="interface-basename">
<xsl:with-param name="str" select="../../../@name"/>/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="type" select="name(../..)"/>
<para role="deprecated">
<warning><para><literal><xsl:value-of select="$name"/></literal> is deprecated since version <xsl:value-of select="@version"/> and should not be used in newly-written code. Use
<xsl:variable name="to">
<xsl:choose>
<xsl:when test="contains($type,'property')">
<xsl:value-of select="$parent"/>:<xsl:value-of select="@instead"/>
</xsl:when>
<xsl:when test="contains($type,'signal')">
<xsl:value-of select="$parent"/>::<xsl:value-of select="@instead"/>
</xsl:when>
<xsl:when test="contains($type,'method')">
<xsl:value-of select="$parent"/>.<xsl:value-of select="@instead"/>
</xsl:when>
<xsl:when test="contains($type,'interface')">
<xsl:value-of select="@instead"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@instead"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:call-template name="create-link">
<xsl:with-param name="type" select="$type"/>
<xsl:with-param name="to" select="$to"/>
<xsl:with-param name="val" select="@instead"/>
</xsl:call-template>
instead.</para></warning>
</para>
</xsl:template>
<xsl:template match="doc:permission">
<para role="permission">
<xsl:apply-templates />
</para>
</xsl:template>
<xsl:template match="doc:errors">
<para role="errors">
<xsl:apply-templates />
</para>
</xsl:template>
<xsl:template match="doc:seealso">
<para>
See also:
<xsl:apply-templates />
</para>
</xsl:template>
<xsl:template name="create-link">
<xsl:param name="type"/>
<xsl:param name="to"/>
<xsl:param name="val"/>
<xsl:choose>
<xsl:when test="contains($type,'property')">
<link><xsl:attribute name="linkend"><xsl:value-of select="$to"/></xsl:attribute><literal><xsl:value-of select="$val"/></literal></link>
</xsl:when>
<xsl:when test="contains($type,'signal')">
<link><xsl:attribute name="linkend"><xsl:value-of select="$to"/></xsl:attribute><literal><xsl:value-of select="$val"/></literal></link>
</xsl:when>
<xsl:when test="contains($type,'method')">
<link><xsl:attribute name="linkend"><xsl:value-of select="$to"/></xsl:attribute><function><xsl:value-of select="$val"/></function></link>
</xsl:when>
<xsl:when test="contains($type,'interface')">
<link><xsl:attribute name="linkend"><xsl:value-of select="$to"/></xsl:attribute><xsl:value-of select="$val"/></link>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="doc:ref">
<xsl:call-template name="create-link">
<xsl:with-param name="type" select="@type"/>
<xsl:with-param name="to" select="@to"/>
<xsl:with-param name="val" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template name="method-doc">
<xsl:apply-templates select="doc:doc/doc:description"/>
<xsl:choose>
<xsl:when test="count(arg) > 0">
<variablelist role="params">
<xsl:for-each select="arg">
<varlistentry><term><parameter><xsl:value-of select="@name"/></parameter>:</term>
<listitem><simpara><xsl:apply-templates select="doc:doc/doc:summary"/></simpara></listitem>
</varlistentry>
</xsl:for-each>
</variablelist>
</xsl:when>
</xsl:choose>
<xsl:apply-templates select="doc:doc/doc:since"/>
<xsl:apply-templates select="doc:doc/doc:deprecated"/>
<xsl:choose>
<xsl:when test="count(doc:doc/doc:errors) > 0">
<refsect3>
<title>Errors</title>
<variablelist role="errors">
<xsl:for-each select="doc:doc/doc:errors/doc:error">
<varlistentry>
<term><parameter><xsl:value-of select="@name"/></parameter>:</term>
<listitem><simpara><xsl:apply-templates select="."/></simpara></listitem>
</varlistentry>
</xsl:for-each>
</variablelist>
</refsect3>
</xsl:when>
</xsl:choose>
<xsl:choose>
<xsl:when test="count(doc:doc/doc:permission) > 0">
<refsect3>
<title>Permissions</title>
<xsl:apply-templates select="doc:doc/doc:permission"/>
</refsect3>
</xsl:when>
</xsl:choose>
<xsl:apply-templates select="doc:doc/doc:seealso"/>
</xsl:template>
<xsl:template name="method-details">
<xsl:param name="interface"/>
<xsl:variable name="longest">
<xsl:call-template name="find-longest">
<xsl:with-param name="set" select="@name"/>
</xsl:call-template>
</xsl:variable>
<xsl:for-each select="///method">
<refsect2>
<title><anchor role="function"><xsl:attribute name="id"><xsl:value-of select="$interface"/>.<xsl:value-of select="@name"/></xsl:attribute></anchor><xsl:value-of select="@name"/> ()</title>
<indexterm><primary><xsl:value-of select="@name"/></primary><secondary><xsl:value-of select="$interface"/></secondary></indexterm>
<programlisting><xsl:value-of select="@name"/> (<xsl:call-template name="method-args"><xsl:with-param name="indent" select="string-length(@name) + 2"/><xsl:with-param name="prefix" select="."/></xsl:call-template>)</programlisting>
<xsl:call-template name="method-doc"/>
</refsect2>
</xsl:for-each>
</xsl:template>
<xsl:template name="properties-synopsis">
<xsl:param name="interface"/>
<xsl:variable name="longest">
<xsl:call-template name="find-longest">
<xsl:with-param name="set" select="///property/@name"/>
</xsl:call-template>
</xsl:variable>
<xsl:for-each select="///property">
<link><xsl:attribute name="linkend"><xsl:value-of select="$interface"/>:<xsl:value-of select="@name"/></xsl:attribute>'<xsl:value-of select="@name"/>'</link><xsl:call-template name="pad-spaces"><xsl:with-param name="width" select="$longest - string-length(@name) + 1"/></xsl:call-template> <xsl:call-template name="property-args"><xsl:with-param name="indent" select="$longest + 2"/></xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="signals-synopsis">
<xsl:param name="interface"/>
<xsl:variable name="longest">
<xsl:call-template name="find-longest">
<xsl:with-param name="set" select="///signal/@name"/>
</xsl:call-template>
</xsl:variable>
<xsl:for-each select="///signal">
<link><xsl:attribute name="linkend"><xsl:value-of select="$interface"/>::<xsl:value-of select="@name"/></xsl:attribute><xsl:value-of select="@name"/></link><xsl:call-template name="pad-spaces"><xsl:with-param name="width" select="$longest - string-length(@name) + 1"/></xsl:call-template>(<xsl:call-template name="signal-args"><xsl:with-param name="indent" select="$longest + 2"/><xsl:with-param name="prefix" select="///signal"/></xsl:call-template>)
</xsl:for-each>
</xsl:template>
<xsl:template name="methods-synopsis">
<xsl:param name="interface"/>
<xsl:variable name="longest">
<xsl:call-template name="find-longest">
<xsl:with-param name="set" select="///method/@name"/>
</xsl:call-template>
</xsl:variable>
<xsl:for-each select="///method">
<link><xsl:attribute name="linkend"><xsl:value-of select="$interface"/>.<xsl:value-of select="@name"/></xsl:attribute><xsl:value-of select="@name"/></link><xsl:call-template name="pad-spaces"><xsl:with-param name="width" select="$longest - string-length(@name) + 1"/></xsl:call-template>(<xsl:call-template name="method-args"><xsl:with-param name="indent" select="$longest + 2"/><xsl:with-param name="prefix" select="///method"/></xsl:call-template>)
</xsl:for-each>
</xsl:template>
<xsl:template name="method-args"><xsl:param name="indent"/><xsl:param name="prefix"/><xsl:variable name="longest"><xsl:call-template name="find-longest"><xsl:with-param name="set" select="$prefix/arg/@type"/></xsl:call-template></xsl:variable><xsl:for-each select="arg"><xsl:value-of select="@direction"/>
<xsl:call-template name="pad-spaces"><xsl:with-param name="width" select="4 - string-length(@direction)"/></xsl:call-template>'<xsl:value-of select="@type"/>'<xsl:call-template name="pad-spaces"><xsl:with-param name="width" select="$longest - string-length(@type) + 1"/></xsl:call-template>
<xsl:value-of select="@name"/><xsl:if test="not(position() = last())">,
<xsl:call-template name="pad-spaces"><xsl:with-param name="width" select="$indent"/></xsl:call-template></xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template name="signal-args"><xsl:param name="indent"/><xsl:param name="prefix"/><xsl:variable name="longest"><xsl:call-template name="find-longest"><xsl:with-param name="set" select="$prefix/arg/@type"/></xsl:call-template></xsl:variable><xsl:for-each select="arg">'<xsl:value-of select="@type"/>'<xsl:call-template name="pad-spaces"><xsl:with-param name="width" select="$longest - string-length(@type) + 1"/></xsl:call-template>
<xsl:value-of select="@name"/><xsl:if test="not(position() = last())">,
<xsl:call-template name="pad-spaces"><xsl:with-param name="width" select="$indent"/></xsl:call-template></xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template name="property-args"><xsl:param name="indent"/>
<xsl:value-of select="@access"/><xsl:call-template name="pad-spaces"><xsl:with-param name="width" select="9 - string-length(@access) + 1"/></xsl:call-template>'<xsl:value-of select="@type"/>'
</xsl:template>
<xsl:template name="pad-spaces">
<xsl:param name="width"/>
<xsl:variable name="spaces" xml:space="preserve"> </xsl:variable>
<xsl:value-of select="substring($spaces,1,$width)"/>
</xsl:template>
<xsl:template name="find-longest">
<xsl:param name="set"/>
<xsl:param name="index" select="1"/>
<xsl:param name="longest" select="0"/>
<xsl:choose>
<xsl:when test="$index > count($set)">
<!--finished looking-->
<xsl:value-of select="$longest"/>
</xsl:when>
<xsl:when test="string-length($set[$index])>$longest">
<!--found new longest-->
<xsl:call-template name="find-longest">
<xsl:with-param name="set" select="$set"/>
<xsl:with-param name="index" select="$index + 1"/>
<xsl:with-param name="longest" select="string-length($set[$index])"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<!--this isn't any longer-->
<xsl:call-template name="find-longest">
<xsl:with-param name="set" select="$set"/>
<xsl:with-param name="index" select="$index + 1"/>
<xsl:with-param name="longest" select="$longest"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="interface-basename">
<xsl:param name="str"/>
<xsl:choose>
<xsl:when test="contains($str,'.')">
<xsl:call-template name="interface-basename">
<xsl:with-param name="str" select="substring-after($str,'.')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$str"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

View file

@ -0,0 +1,106 @@
<refentry id="gnome-session-inhibit" lang="en">
<refentryinfo>
<title>gnome-session-inhibit</title>
<productname>gnome-session</productname>
</refentryinfo>
<refmeta>
<refentrytitle>gnome-session-inhibit</refentrytitle>
<manvolnum>1</manvolnum>
<refmiscinfo class="manual">User Commands</refmiscinfo>
</refmeta>
<refnamediv>
<refname>gnome-session-inhibit</refname>
<refpurpose>inhibit gnome-session functionality</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>gnome-session-inhibit</command>
<arg choice="opt" rep="repeat">OPTION</arg>
<arg choice="opt">COMMAND</arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1><title>Description</title>
<para><command>gnome-session-inhibit</command> can inhibit certain
gnome-session functionality while executing the given COMMAND. To
achieve this, it calls the Inhibit() method of the gnome-session
D-Bus API and creates an inhibitor. The inhibitor is automatically
removed when gnome-session-inhibit exits.
</para>
<para>
A typical use case is to prevent the session from going idle (and
thus locking the screen) while a movie player is running.
</para>
</refsect1>
<refsect1><title>Options</title>
<variablelist>
<varlistentry>
<term><option>-h</option>, <option>--help</option></term>
<listitem><para>
print help and exit
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--version</option></term>
<listitem><para>
print version information and exit
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--app-id</option> ID</term>
<listitem><para>
The application id to use when calling the gnome-session Inhibit() method.
If this option is not specified, "unknown" is used.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--reason</option> REASON</term>
<listitem><para>
A human-readable reason to pass along when calling the gnome-session
Inhibit() method. If this option is not specified, "not specified" is used.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--inhibit</option> ARG</term>
<listitem><para>
ARG specifies the things to inhibit, as a colon-separated list. The
possible values are logout, switch-user, suspend, idle, automount.
If this option is used more than once, the values are combined.
If this option is not specified, "idle" is assumed.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--inhibit-only</option></term>
<listitem><para>
Do not launch COMMAND and wait forever instead
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-l</option>, <option>--list</option></term>
<listitem><para>
list the existing inhibitions and exit
</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1><title>See also</title>
<para>
<citerefentry><refentrytitle>systemd-inhibit</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</para>
</refsect1>
</refentry>

View file

@ -0,0 +1,34 @@
.\"
.\" gnome-session-quit manual page.
.\" (C) 2000 Miguel de Icaza (miguel@helixcode.com)
.\" (C) 2009-2010 Vincent Untz (vuntz@gnome.org)
.\"
.TH GNOME-SESSION-QUIT 1 "GNOME"
.SH NAME
gnome-session-quit \- End the current GNOME session
.SH SYNOPSIS
.B gnome-session-quit [\-\-logout|\-\-power-off|\-\-reboot] [\-\-force] [\-\-no-prompt]
.SH DESCRIPTION
The \fIgnome-session-quit\fP program can be used to end a GNOME session.
.SH OPTIONS
The following options are supported:
.TP
.I "--logout"
Prompt the user to confirm logout. This is the default behavior.
.TP
.I "--power-off"
Prompt the user to confirm system power off.
.TP
.I "--reboot"
Prompt the user to confirm system reboot.
.TP
.I "--force"
Ignore any inhibitors.
.TP
.I "--no-prompt"
End the session without user interaction. This only works with \fI--logout\fP.
.SH BUGS
If you find bugs in the \fIgnome-session-quit\fP program, please report
these on https://bugzilla.gnome.org.
.SH SEE ALSO
.BR gnome-session(1)

View file

@ -0,0 +1,52 @@
<refentry id="gnome-session-selector" lang="en">
<refentryinfo>
<title>gnome-session-selector</title>
<productname>gnome-session</productname>
</refentryinfo>
<refmeta>
<refentrytitle>gnome-session-selector</refentrytitle>
<manvolnum>1</manvolnum>
<refmiscinfo class="manual">User Commands</refmiscinfo>
</refmeta>
<refnamediv>
<refname>gnome-session-selector</refname>
<refpurpose>Selects a session to use with gnome-session</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>gnome-session-selector</command>
<arg choice="opt">session</arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1><title>Description</title>
<para><command>gnome-session-selector</command> can be used from a
xsession desktop file to select a session before gnome-session is run.
gnome-session reads and stores its session in the
<filename><envar>$XDG_DATA_HOME</envar>/gnome-session/saved-session</filename>
directory. <command>gnome-session-selector</command> works by replacing
the saved-session directory by a symlink to another directory. Since the
session name is used as the directory name, it may not contain '/' characters
or begin with a '.'.
</para>
<para>
When a session name is specified, <command>gnome-session-selector</command>
will create a symlink to select this session.
</para>
<para>
When started without arguments, <command>gnome-session-selector</command>
will present a dialog that allows to choose one of the existing sessions
or create a new one.
</para>
</refsect1>
<refsect1><title>See also</title>
<para>
<citerefentry><refentrytitle>gnome-session</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</para>
</refsect1>
</refentry>

170
doc/man/gnome-session.1 Normal file
View file

@ -0,0 +1,170 @@
.\"
.\" gnome-session manual page.
.\" (C) 2000 Miguel de Icaza (miguel@helixcode.com)
.\" (C) 2009-2010 Vincent Untz (vuntz@gnome.org)
.\" (C) 2019 Benjamin Berg (bberg@redhat.com)
.\" (C) 2020 Sebastian Geiger (sbastig@gmx.net)
.\"
.TH GNOME-SESSION 1 "May 2020" "GNOME"
.SH NAME
gnome-session \- Start the GNOME desktop environment
.SH SYNOPSIS
.B gnome-session [\-a|\-\-autostart=DIR] [\-\-session=SESSION] [\-\-failsafe|\-f] [\-\-debug] [\-\-whale]
.SH DESCRIPTION
The \fIgnome-session\fP program starts up the GNOME desktop
environment. This command is typically executed by your login manager
(either gdm, xdm, or from your X startup scripts). It will load
either your saved session, or it will provide a default session for the
user as defined by the system administrator (or the default GNOME
installation on your system). Note that \fIgnome-session\fP is a wrapper
script for \fIgnome-session-binary\fP.
.PP
The default session is defined in \fBgnome.session\fP, a .desktop-like
file that is looked for in
\fB$XDG_CONFIG_HOME/gnome-session/sessions\fP,
\fB$XDG_CONFIG_DIRS/gnome-session/sessions\fP and
\fB$XDG_DATA_DIRS/gnome-session/sessions\fP.
.PP
When saving a session, \fIgnome-session\fP saves the currently running
applications in the \fB$XDG_CONFIG_HOME/gnome-session/saved-session\fP
directory. Saving sessions is only supported with the legacy non-systemd
startup method.
.PP
\fIgnome-session\fP is an X11R6 session manager. It can manage GNOME
applications as well as any X11R6 SM compliant application.
.SH OPTIONS
The following options are supported:
.TP
.I "--autostart=DIR"
The directory \fBDIR\fP to be searched for autostart .desktop files. This option can be used multiple times.
When this option is present, then default autostart directories will not be searched.
.TP
.I "--session=SESSION"
Use the applications defined in \fBSESSION.session\fP. If not specified,
\fBgnome.session\fP will be used.
.TP
.I "--failsafe"
Run in fail-safe mode. User-specified applications will not be started.
.TP
.I "--debug"
Enable debugging code.
.TP
.I "--whale"
Show the fail whale in a dialog for debugging it.
.SH SESSION DEFINITION
Sessions are defined in \fB.session\fP files, that are using a .desktop-like
format, with the following keys in the \fBGNOME Session\fP group:
.TP
.I Name
Name of the session. This can be localized.
.TP
.I RequiredComponents
List of component identifiers (desktop files) that are required by the session. The required components will always run in the session.
.PP
Here is an example of a session definition:
.PP
.in +4n
.nf
[GNOME Session]
Name=GNOME
RequiredComponents=gnome-shell;gnome-settings-daemon;
.in
.fi
.PP
In \fBsystemd\fP managed sessions the RequiredComponents may be provided by
systemd units instead. In this case the corresponding \fB.desktop\fP file needs
to contain \fBX-GNOME-HiddenUnderSystemd=true\fP. \fIgnome-session\fP will
ignore these components and rely on \fIsystemd\fP to manage them appropriately,
see the \fIsystemd\fP for more information on how this works.
.PP
The \fB.session\fP files are looked for in
\fB$XDG_CONFIG_HOME/gnome-session/sessions\fP,
\fB$XDG_CONFIG_DIRS/gnome-session/sessions\fP and
\fB$XDG_DATA_DIRS/gnome-session/sessions\fP.
.SH systemd
\fIgnome-session\fP can pass much of the session management over to systemd.
In this case, startup components that have \fBX-GNOME-HiddenUnderSystemd=true\fP
set in their \fB.desktop\fP file will be ignored by \fIgnome-session\fP. It
instead relies on the fact that these components are managed by systemd.
.PP
.PP
\fBsystemd\fP provides the two special targets \fBgraphical-session.target\fP
and \fBgraphical-session-pre.target\fP which are fully functional and should be
used. \fIgnome-session\fP provides the following main targets:
.TP
.I "gnome-session.target"
Generic unit that will be active throughout the session. Similar to
\fBgraphical-session.target\fP.
.TP
.I "gnome-session-pre.target"
Used for tasks that need to be done before session startup. Similar to
\fBgraphical-session-pre.target\fP.
.TP
.I "gnome-session-x11@SESSION.target" "gnome-session-wayland@SESSION.target"
Main unit started for X11/wayland based session. \fBSESSION\fP is set according
to the session that is passed in \fI--session\fP.
.TP
.I "gnome-session-x11.target" "gnome-session-wayland.target"
Convenience units without the session embedded into the target.
.TP
.I "gnome-session@SESSION.target"
Convenience unit with just the \fBSESSION\fP information embedded.
.TP
.I "gnome-session-x11-services.target"
Special unit started when X11 services are needed. This will be used from GNOME
3.36 onwards. Programs will need to use the special \fBGNOME_SETUP_DISPLAY\fP
environment variable instead of \fIDISPLAY\fP.
.PP
Note that care must be taken to set appropriate \fBAfter=\fP rules. It is also
strongly recommended to always do this in combination with \fBBindsTo=\fP or
\fBPartOf=\fP on one of the core targets (e.g. \fBgraphical-session.target\fP).
.PP
Units are required to set \fBCollectMode=inactive-or-failed\fP. In addition, it
is strongly recommended to set \fBTimeoutStopSec=5\fP so that logout
will not be delayed indefinitely in case the process does not stop properly.
.SH ENVIRONMENT
\fIgnome-session\fP sets several environment variables for the use of
its child processes:
.PP
.B SESSION_MANAGER
.IP
This variable is used by session-manager aware clients to contact
gnome-session.
.PP
.B DISPLAY
.IP
This variable is set to the X display being used by
\fIgnome-session\fP. Note that if the \fI--display\fP option is used
this might be different from the setting of the environment variable
when gnome-session is invoked.
.PP
Behavior of \fIgnome-session\fP ifself can be modified via the following environment variable:
.PP
.B GNOME_SESSION_AUTOSTART_DIR
.IP
This variable specifies a list of directories to the searched for autostart
files. This variable overrides all directories specified via the
\fI--autostart\fP option, as well as all default autostart directories.
.SH FILES
.PP
.B $XDG_CONFIG_HOME/autostart
.B $XDG_CONFIG_DIRS/autostart
.B /usr/share/gnome/autostart
.IP
Applications defined via .desktop files in those directories will be started on login.
.PP
.B $XDG_CONFIG_HOME/gnome-session/sessions
.B $XDG_CONFIG_DIRS/gnome-session/sessions
.B $XDG_DATA_DIRS/gnome-session/sessions
.IP
These directories contain the \fB.session\fP files that can be used
with the \fI--session\fP option.
.PP
.B $XDG_CONFIG_HOME/gnome-session/saved-session
.IP
This directory contains the list of applications of the saved session.
.SH BUGS
If you find bugs in the \fIgnome-session\fP program, please report
these on https://gitlab.gnome.org/GNOME/gnome-session/issues.
.SH SEE ALSO
.BR gnome-session-quit(1)

44
doc/man/meson.build Normal file
View file

@ -0,0 +1,44 @@
xsltproc = find_program('xsltproc')
man1_dir = join_paths(get_option('mandir'), 'man1')
xsltproc_cmd = [
xsltproc,
'--output', '@OUTPUT@',
'--nonet',
'--stringparam', 'man.output.quietly', '1',
'--stringparam', 'funcsynopsis.style', 'ansi',
'--stringparam', 'man.th.extra1.suppress', '1',
'--stringparam', 'man.authors.section.enabled', '0',
'--stringparam', 'man.copyright.section.enabled', '0',
'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl',
'@INPUT@'
]
mans = ['gnome-session-inhibit']
if enable_session_selector
mans += 'gnome-session-selector'
endif
foreach man: mans
output = man + '.1'
custom_target(
output,
input: man + '.xml',
output: output,
command: xsltproc_cmd,
install: true,
install_dir: man1_dir
)
endforeach
man_data = files(
'gnome-session.1',
'gnome-session-quit.1'
)
install_data(
man_data,
install_dir: man1_dir
)

7
doc/meson.build Normal file
View file

@ -0,0 +1,7 @@
if get_option('docbook')
subdir('dbus')
endif
if get_option('man')
subdir('man')
endif

64
gnome-session.doap Normal file
View file

@ -0,0 +1,64 @@
<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:foaf="http://xmlns.com/foaf/0.1/"
xmlns:gnome="http://api.gnome.org/doap-extensions#"
xmlns="http://usefulinc.com/ns/doap#">
<name xml:lang="en">GNOME Session Manager</name>
<shortdesc xml:lang="en">The GNOME Session Manager</shortdesc>
<description>The GNOME Session Manager is in charge of starting the core
components of the GNOME desktop, and applications that should be launched at
login time.
GNOME Session also provides the definition of what a standard GNOME session
consists of.</description>
<homepage rdf:resource="https://wiki.gnome.org/Projects/SessionManagement" />
<developer-forum rdf:resource="https://discourse.gnome.org/c/platform/5" />
<download-page rdf:resource="https://download.gnome.org/sources/gnome-session/" />
<bug-database rdf:resource="https://gitlab.gnome.org/GNOME/gnome-session/issues" />
<category rdf:resource="http://api.gnome.org/doap-extensions#core" />
<programming-language>C</programming-language>
<maintainer>
<foaf:Person>
<foaf:name>Adrian Vovk</foaf:name>
<foaf:mbox rdf:resource="mailto:adrianvovk@gmail.com" />
<gnome:userid>AdrianVovk</gnome:userid>
</foaf:Person>
</maintainer>
<maintainer>
<foaf:Person>
<foaf:name>Ray Strode</foaf:name>
<foaf:mbox rdf:resource="mailto:rstrode@redhat.com" />
<gnome:userid>halfline</gnome:userid>
</foaf:Person>
</maintainer>
<maintainer>
<foaf:Person>
<foaf:name>William Jon McCann</foaf:name>
<foaf:mbox rdf:resource="mailto:mccann@jhu.edu" />
<!-- <gnome:userid>mccann</gnome:userid> -->
</foaf:Person>
</maintainer>
<maintainer>
<foaf:Person>
<foaf:name>Colin Walters</foaf:name>
<foaf:mbox rdf:resource="mailto:walters@verbum.org" />
<gnome:userid>walters</gnome:userid>
</foaf:Person>
</maintainer>
<maintainer>
<foaf:Person>
<foaf:name>Iain Lane</foaf:name>
<foaf:mbox rdf:resource="mailto:iainl@gnome.org" />
<gnome:userid>iainl</gnome:userid>
</foaf:Person>
</maintainer>
<maintainer>
<foaf:Person>
<foaf:name>Benjamin Berg</foaf:name>
<foaf:mbox rdf:resource="mailto:bberg@gnome.org" />
<gnome:userid>bberg</gnome:userid>
</foaf:Person>
</maintainer>
</Project>

69
gnome-session/README Normal file
View file

@ -0,0 +1,69 @@
See also http://live.gnome.org/SessionManagement/NewGnomeSession
Startup
-------
main() creates the GsmSession object representing the session (either
failsafe or normal). gsm_session_new() reads the appropriate autostart
and session files to create a list of GsmApps to be started.
(GsmAppAutostart represents an autostarted app, and GsmAppResumed
represents an app resumed from the previous saved session.)
Startup is divided into 7 phases (GsmManagerPhase):
* GSM_MANAGER_PHASE_STARTUP covers gnome-session's internal
startup, which also includes starting dbus-daemon (if
it's not already running). Gnome-session starts up those
explicitly because it needs them for its own purposes.
* GSM_MANAGER_PHASE_EARLY_INITIALIZATION is the first phase of
"normal" startup (ie, startup controlled by .desktop files
rather than hardcoding). It covers the possible installation of
files in $HOME by gnome-initial-setup and must be done before
other components such as gnome-keyring use those files.
* GSM_MANAGER_PHASE_INITIALIZATION covers low-level stuff like
gnome-settings-daemon helpers, that need to be
running very early (before any windows are displayed).
Apps in this phase can make use of a D-Bus interface
(org.gnome.SessionManager.Setenv) to set environment variables
in gnome-session's environment. This can be used for things like
$GTK_MODULES, $GNOME_KEYRING_SOCKET, etc
* GSM_MANAGER_PHASE_WINDOW_MANAGER includes window managers and
compositing managers, and anything else that has to be running
before any windows are mapped
* GSM_MANAGER_PHASE_PANEL includes anything that permanently takes
up screen real estate (via EWMH struts). This is the first phase
where things actually appear on the screen.
* GSM_MANAGER_PHASE_DESKTOP includes anything that draws directly
on the desktop (eg, nautilus).
* GSM_MANAGER_PHASE_APPLICATION is everything else (normal apps,
tray icons, etc)
For each startup phase, GsmSession launches the appropriate GsmApps.
When apps connect to the XSMP or D-Bus servers, GsmClients are created
and added to the session. The session tries to map these clients to
GsmApps. GsmApps signal when they register (via XSMP or SN) or exit,
and GsmSession uses this to decide when the phase is complete.
FIXME: after starting the session, we need to run the DiscardCommands
of resumed apps.
Running/Shutdown
----------------
GSM_MANAGER_PHASE_RUNNING is pretty similar to the old gnome-session;
mostly it just tracks XSMP clients, and watches for
SmRestartImmediately clients exiting (NOTE: THIS DOESN'T HAPPEN YET).
GsmClient is in theory not XSMP-specific, but it's very very
XSMP-like, and the shutdown procedure is also very XSMP-like. This is
just because there's no way to do XSMP shutdown correctly otherwise.
However, GsmClientDBus will still be able to present a more sane
protocol to its clients than GsmClient presents to it.

205
gnome-session/gdm-log.c Normal file
View file

@ -0,0 +1,205 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
*
* 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, see <http://www.gnu.org/licenses/>.
*
* Authors: William Jon McCann <mccann@jhu.edu>
*
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <syslog.h>
#include <glib.h>
#include <glib/gstdio.h>
#include "gdm-log.h"
static gboolean initialized = FALSE;
static int syslog_levels = (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING);
static void
log_level_to_priority_and_prefix (GLogLevelFlags log_level,
int *priorityp,
const char **prefixp)
{
int priority;
const char *prefix;
/* Process the message prefix and priority */
switch (log_level & G_LOG_LEVEL_MASK) {
case G_LOG_FLAG_FATAL:
priority = LOG_EMERG;
prefix = "FATAL";
break;
case G_LOG_LEVEL_ERROR:
priority = LOG_ERR;
prefix = "ERROR";
break;
case G_LOG_LEVEL_CRITICAL:
priority = LOG_CRIT;
prefix = "CRITICAL";
break;
case G_LOG_LEVEL_WARNING:
priority = LOG_WARNING;
prefix = "WARNING";
break;
case G_LOG_LEVEL_MESSAGE:
priority = LOG_NOTICE;
prefix = "MESSAGE";
break;
case G_LOG_LEVEL_INFO:
priority = LOG_INFO;
prefix = "INFO";
break;
case G_LOG_LEVEL_DEBUG:
/* if debug was requested then bump this up to ERROR
* to ensure it is seen in a log */
if (syslog_levels & G_LOG_LEVEL_DEBUG) {
priority = LOG_WARNING;
prefix = "DEBUG(+)";
} else {
priority = LOG_DEBUG;
prefix = "DEBUG";
}
break;
default:
priority = LOG_DEBUG;
prefix = "UNKNOWN";
break;
}
if (priorityp != NULL) {
*priorityp = priority;
}
if (prefixp != NULL) {
*prefixp = prefix;
}
}
void
gdm_log_default_handler (const gchar *log_domain,
GLogLevelFlags log_level,
const gchar *message,
gpointer unused_data)
{
GString *gstring;
int priority;
const char *level_prefix;
char *string;
gboolean do_log;
gboolean is_fatal;
is_fatal = (log_level & G_LOG_FLAG_FATAL) != 0;
do_log = (log_level & syslog_levels);
if (! do_log) {
return;
}
if (! initialized) {
gdm_log_init ();
}
log_level_to_priority_and_prefix (log_level,
&priority,
&level_prefix);
gstring = g_string_new (NULL);
if (log_domain != NULL) {
g_string_append (gstring, log_domain);
g_string_append_c (gstring, '-');
}
g_string_append (gstring, level_prefix);
g_string_append (gstring, ": ");
if (message == NULL) {
g_string_append (gstring, "(NULL) message");
} else {
g_string_append (gstring, message);
}
if (is_fatal) {
g_string_append (gstring, "\naborting...\n");
} else {
g_string_append (gstring, "\n");
}
string = g_string_free (gstring, FALSE);
syslog (priority, "%s", string);
g_free (string);
}
void
gdm_log_toggle_debug (void)
{
if (syslog_levels & G_LOG_LEVEL_DEBUG) {
g_debug ("Debugging disabled");
syslog_levels &= ~G_LOG_LEVEL_DEBUG;
} else {
syslog_levels |= G_LOG_LEVEL_DEBUG;
g_debug ("Debugging enabled");
}
}
void
gdm_log_set_debug (gboolean debug)
{
if (debug) {
syslog_levels |= G_LOG_LEVEL_DEBUG;
g_debug ("Enabling debugging");
} else {
g_debug ("Disabling debugging");
syslog_levels &= ~G_LOG_LEVEL_DEBUG;
}
}
void
gdm_log_init (void)
{
const char *prg_name;
int options;
g_log_set_default_handler (gdm_log_default_handler, NULL);
prg_name = g_get_prgname ();
options = LOG_PID;
#ifdef LOG_PERROR
options |= LOG_PERROR;
#endif
openlog (prg_name, options, LOG_DAEMON);
initialized = TRUE;
}
void
gdm_log_shutdown (void)
{
closelog ();
initialized = FALSE;
}

50
gnome-session/gdm-log.h Normal file
View file

@ -0,0 +1,50 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
*
* 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, see <http://www.gnu.org/licenses/>.
*
* Authors: William Jon McCann <mccann@jhu.edu>
*
*/
#ifndef __GDM_LOG_H
#define __GDM_LOG_H
#include <stdarg.h>
#include <glib.h>
G_BEGIN_DECLS
void gdm_log_default_handler (const gchar *log_domain,
GLogLevelFlags log_level,
const gchar *message,
gpointer unused_data);
void gdm_log_set_debug (gboolean debug);
void gdm_log_toggle_debug (void);
void gdm_log_init (void);
void gdm_log_shutdown (void);
/* compatibility */
#define gdm_fail g_critical
#define gdm_error g_warning
#define gdm_info g_message
#define gdm_debug g_debug
#define gdm_assert g_assert
#define gdm_assert_not_reached g_assert_not_reached
G_END_DECLS
#endif /* __GDM_LOG_H */

39
gnome-session/gnome-session.in Executable file
View file

@ -0,0 +1,39 @@
#!/bin/sh
if [ "x$XDG_SESSION_TYPE" = "xwayland" ] &&
[ "x$XDG_SESSION_CLASS" != "xgreeter" ] &&
[ -n "$SHELL" ] &&
grep -q "$SHELL" /etc/shells &&
! (echo "$SHELL" | grep -q "false") &&
! (echo "$SHELL" | grep -q "nologin"); then
if [ "$1" != '-l' ]; then
exec bash -c "exec -l '$SHELL' -c 'exec $0 -l $*'"
else
shift
fi
fi
SETTING=$(G_MESSAGES_DEBUG='' gsettings get org.gnome.system.locale region)
REGION=${SETTING#\'}
REGION=${REGION%\'}
if [ -n "$REGION" ]; then
unset LC_TIME LC_NUMERIC LC_MONETARY LC_MEASUREMENT LC_PAPER
if [ "$LANG" != "$REGION" ] ; then
# LC_CTYPE
export LC_NUMERIC=$REGION
export LC_TIME=$REGION
# LC_COLLATE
export LC_MONETARY=$REGION
# LC_MESSAGES
export LC_PAPER=$REGION
# LC_NAME
export LC_ADDRESS=$REGION
export LC_TELEPHONE=$REGION
export LC_MEASUREMENT=$REGION
# LC_IDENTIFICATION
fi
fi
exec @libexecdir@/gnome-session-binary "$@"

593
gnome-session/gsm-app.c Normal file
View file

@ -0,0 +1,593 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2007 Novell, Inc.
* Copyright (C) 2008 Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <glib.h>
#include <string.h>
#include "gsm-app.h"
#include "org.gnome.SessionManager.App.h"
/* If a component crashes twice within a minute, we count that as a fatal error */
#define _GSM_APP_RESPAWN_RATELIMIT_SECONDS 60
typedef struct
{
char *id;
char *app_id;
int phase;
char *startup_id;
gboolean registered;
GTimeVal last_restart_time;
GDBusConnection *connection;
GsmExportedApp *skeleton;
} GsmAppPrivate;
enum {
EXITED,
DIED,
LAST_SIGNAL
};
static guint32 app_serial = 1;
static guint signals[LAST_SIGNAL] = { 0 };
enum {
PROP_0,
PROP_ID,
PROP_STARTUP_ID,
PROP_PHASE,
PROP_REGISTERED,
LAST_PROP
};
G_DEFINE_TYPE_WITH_PRIVATE (GsmApp, gsm_app, G_TYPE_OBJECT)
GQuark
gsm_app_error_quark (void)
{
static GQuark ret = 0;
if (ret == 0) {
ret = g_quark_from_static_string ("gsm_app_error");
}
return ret;
}
static gboolean
gsm_app_get_app_id (GsmExportedApp *skeleton,
GDBusMethodInvocation *invocation,
GsmApp *app)
{
const gchar *id;
id = GSM_APP_GET_CLASS (app)->impl_get_app_id (app);
gsm_exported_app_complete_get_app_id (skeleton, invocation, id);
return TRUE;
}
static gboolean
gsm_app_get_startup_id (GsmExportedApp *skeleton,
GDBusMethodInvocation *invocation,
GsmApp *app)
{
GsmAppPrivate *priv = gsm_app_get_instance_private (app);
const gchar *id;
id = g_strdup (priv->startup_id);
gsm_exported_app_complete_get_startup_id (skeleton, invocation, id);
return TRUE;
}
static gboolean
gsm_app_get_phase (GsmExportedApp *skeleton,
GDBusMethodInvocation *invocation,
GsmApp *app)
{
GsmAppPrivate *priv = gsm_app_get_instance_private (app);
gsm_exported_app_complete_get_phase (skeleton, invocation, priv->phase);
return TRUE;
}
static guint32
get_next_app_serial (void)
{
guint32 serial;
serial = app_serial++;
if ((gint32)app_serial < 0) {
app_serial = 1;
}
return serial;
}
static gboolean
register_app (GsmApp *app)
{
GsmAppPrivate *priv = gsm_app_get_instance_private (app);
GError *error;
GsmExportedApp *skeleton;
error = NULL;
priv->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
if (error != NULL) {
g_critical ("error getting session bus: %s", error->message);
g_error_free (error);
return FALSE;
}
skeleton = gsm_exported_app_skeleton_new ();
priv->skeleton = skeleton;
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (skeleton),
priv->connection, priv->id,
&error);
if (error != NULL) {
g_critical ("error registering app on session bus: %s", error->message);
g_error_free (error);
return FALSE;
}
g_signal_connect (skeleton, "handle-get-app-id",
G_CALLBACK (gsm_app_get_app_id), app);
g_signal_connect (skeleton, "handle-get-phase",
G_CALLBACK (gsm_app_get_phase), app);
g_signal_connect (skeleton, "handle-get-startup-id",
G_CALLBACK (gsm_app_get_startup_id), app);
return TRUE;
}
static GObject *
gsm_app_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
GsmApp *app;
GsmAppPrivate *priv;
gboolean res;
app = GSM_APP (G_OBJECT_CLASS (gsm_app_parent_class)->constructor (type,
n_construct_properties,
construct_properties));
priv = gsm_app_get_instance_private (app);
g_free (priv->id);
priv->id = g_strdup_printf ("/org/gnome/SessionManager/App%u", get_next_app_serial ());
res = register_app (app);
if (! res) {
g_warning ("Unable to register app with session bus");
}
return G_OBJECT (app);
}
static void
gsm_app_init (GsmApp *app)
{
}
static void
gsm_app_set_phase (GsmApp *app,
int phase)
{
GsmAppPrivate *priv = gsm_app_get_instance_private (app);
g_return_if_fail (GSM_IS_APP (app));
priv->phase = phase;
}
static void
gsm_app_set_id (GsmApp *app,
const char *id)
{
GsmAppPrivate *priv = gsm_app_get_instance_private (app);
g_return_if_fail (GSM_IS_APP (app));
g_free (priv->id);
priv->id = g_strdup (id);
g_object_notify (G_OBJECT (app), "id");
}
static void
gsm_app_set_startup_id (GsmApp *app,
const char *startup_id)
{
GsmAppPrivate *priv = gsm_app_get_instance_private (app);
g_return_if_fail (GSM_IS_APP (app));
g_free (priv->startup_id);
priv->startup_id = g_strdup (startup_id);
g_object_notify (G_OBJECT (app), "startup-id");
}
static void
gsm_app_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GsmApp *app = GSM_APP (object);
switch (prop_id) {
case PROP_STARTUP_ID:
gsm_app_set_startup_id (app, g_value_get_string (value));
break;
case PROP_ID:
gsm_app_set_id (app, g_value_get_string (value));
break;
case PROP_PHASE:
gsm_app_set_phase (app, g_value_get_int (value));
break;
case PROP_REGISTERED:
gsm_app_set_registered (app, g_value_get_boolean (value));
break;
default:
break;
}
}
static void
gsm_app_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GsmApp *app = GSM_APP (object);
GsmAppPrivate *priv = gsm_app_get_instance_private (app);
switch (prop_id) {
case PROP_STARTUP_ID:
g_value_set_string (value, priv->startup_id);
break;
case PROP_ID:
g_value_set_string (value, priv->id);
break;
case PROP_PHASE:
g_value_set_int (value, priv->phase);
break;
case PROP_REGISTERED:
g_value_set_boolean (value, priv->registered);
break;
default:
break;
}
}
static void
gsm_app_dispose (GObject *object)
{
GsmApp *app = GSM_APP (object);
GsmAppPrivate *priv = gsm_app_get_instance_private (app);
g_free (priv->startup_id);
priv->startup_id = NULL;
g_free (priv->id);
priv->id = NULL;
if (priv->skeleton != NULL) {
g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (priv->skeleton),
priv->connection);
g_clear_object (&priv->skeleton);
}
g_clear_object (&priv->connection);
G_OBJECT_CLASS (gsm_app_parent_class)->dispose (object);
}
static void
gsm_app_class_init (GsmAppClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->set_property = gsm_app_set_property;
object_class->get_property = gsm_app_get_property;
object_class->dispose = gsm_app_dispose;
object_class->constructor = gsm_app_constructor;
klass->impl_start = NULL;
klass->impl_get_app_id = NULL;
klass->impl_get_autorestart = NULL;
klass->impl_provides = NULL;
klass->impl_get_provides = NULL;
klass->impl_is_running = NULL;
g_object_class_install_property (object_class,
PROP_PHASE,
g_param_spec_int ("phase",
"Phase",
"Phase",
-1,
G_MAXINT,
-1,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_ID,
g_param_spec_string ("id",
"ID",
"ID",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_STARTUP_ID,
g_param_spec_string ("startup-id",
"startup ID",
"Session management startup ID",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_REGISTERED,
g_param_spec_boolean ("registered",
"Registered",
"Registered",
FALSE,
G_PARAM_READWRITE));
signals[EXITED] =
g_signal_new ("exited",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GsmAppClass, exited),
NULL, NULL, NULL,
G_TYPE_NONE,
1, G_TYPE_UCHAR);
signals[DIED] =
g_signal_new ("died",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GsmAppClass, died),
NULL, NULL, NULL,
G_TYPE_NONE,
1, G_TYPE_INT);
}
const char *
gsm_app_peek_id (GsmApp *app)
{
GsmAppPrivate *priv = gsm_app_get_instance_private (app);
return priv->id;
}
const char *
gsm_app_peek_app_id (GsmApp *app)
{
return GSM_APP_GET_CLASS (app)->impl_get_app_id (app);
}
const char *
gsm_app_peek_startup_id (GsmApp *app)
{
GsmAppPrivate *priv = gsm_app_get_instance_private (app);
return priv->startup_id;
}
/**
* gsm_app_peek_phase:
* @app: a %GsmApp
*
* Returns @app's startup phase.
*
* Return value: @app's startup phase
**/
GsmManagerPhase
gsm_app_peek_phase (GsmApp *app)
{
GsmAppPrivate *priv = gsm_app_get_instance_private (app);
g_return_val_if_fail (GSM_IS_APP (app), GSM_MANAGER_PHASE_APPLICATION);
return priv->phase;
}
gboolean
gsm_app_peek_is_disabled (GsmApp *app)
{
g_return_val_if_fail (GSM_IS_APP (app), FALSE);
if (GSM_APP_GET_CLASS (app)->impl_is_disabled) {
return GSM_APP_GET_CLASS (app)->impl_is_disabled (app);
} else {
return FALSE;
}
}
gboolean
gsm_app_peek_is_conditionally_disabled (GsmApp *app)
{
g_return_val_if_fail (GSM_IS_APP (app), FALSE);
if (GSM_APP_GET_CLASS (app)->impl_is_conditionally_disabled) {
return GSM_APP_GET_CLASS (app)->impl_is_conditionally_disabled (app);
} else {
return FALSE;
}
}
gboolean
gsm_app_is_running (GsmApp *app)
{
g_return_val_if_fail (GSM_IS_APP (app), FALSE);
if (GSM_APP_GET_CLASS (app)->impl_is_running) {
return GSM_APP_GET_CLASS (app)->impl_is_running (app);
} else {
return FALSE;
}
}
gboolean
gsm_app_peek_autorestart (GsmApp *app)
{
g_return_val_if_fail (GSM_IS_APP (app), FALSE);
if (GSM_APP_GET_CLASS (app)->impl_get_autorestart) {
return GSM_APP_GET_CLASS (app)->impl_get_autorestart (app);
} else {
return FALSE;
}
}
gboolean
gsm_app_provides (GsmApp *app, const char *service)
{
if (GSM_APP_GET_CLASS (app)->impl_provides) {
return GSM_APP_GET_CLASS (app)->impl_provides (app, service);
} else {
return FALSE;
}
}
char **
gsm_app_get_provides (GsmApp *app)
{
if (GSM_APP_GET_CLASS (app)->impl_get_provides) {
return GSM_APP_GET_CLASS (app)->impl_get_provides (app);
} else {
return NULL;
}
}
gboolean
gsm_app_has_autostart_condition (GsmApp *app,
const char *condition)
{
if (GSM_APP_GET_CLASS (app)->impl_has_autostart_condition) {
return GSM_APP_GET_CLASS (app)->impl_has_autostart_condition (app, condition);
} else {
return FALSE;
}
}
gboolean
gsm_app_start (GsmApp *app,
GError **error)
{
GsmAppPrivate *priv = gsm_app_get_instance_private (app);
g_debug ("Starting app: %s", priv->id);
return GSM_APP_GET_CLASS (app)->impl_start (app, error);
}
gboolean
gsm_app_restart (GsmApp *app,
GError **error)
{
GsmAppPrivate *priv = gsm_app_get_instance_private (app);
GTimeVal current_time;
g_debug ("Re-starting app: %s", priv->id);
g_get_current_time (&current_time);
if (priv->last_restart_time.tv_sec > 0
&& (current_time.tv_sec - priv->last_restart_time.tv_sec) < _GSM_APP_RESPAWN_RATELIMIT_SECONDS) {
g_warning ("App '%s' respawning too quickly", gsm_app_peek_app_id (app));
g_set_error (error,
GSM_APP_ERROR,
GSM_APP_ERROR_RESTART_LIMIT,
"Component '%s' crashing too quickly",
gsm_app_peek_app_id (app));
return FALSE;
}
priv->last_restart_time = current_time;
return GSM_APP_GET_CLASS (app)->impl_restart (app, error);
}
gboolean
gsm_app_stop (GsmApp *app,
GError **error)
{
return GSM_APP_GET_CLASS (app)->impl_stop (app, error);
}
void
gsm_app_exited (GsmApp *app,
guchar exit_code)
{
g_return_if_fail (GSM_IS_APP (app));
g_signal_emit (app, signals[EXITED], 0, exit_code);
}
void
gsm_app_died (GsmApp *app,
int signal)
{
g_return_if_fail (GSM_IS_APP (app));
g_signal_emit (app, signals[DIED], 0, signal);
}
gboolean
gsm_app_get_registered (GsmApp *app)
{
GsmAppPrivate *priv = gsm_app_get_instance_private (app);
g_return_val_if_fail (GSM_IS_APP (app), FALSE);
return priv->registered;
}
void
gsm_app_set_registered (GsmApp *app,
gboolean registered)
{
GsmAppPrivate *priv = gsm_app_get_instance_private (app);
g_return_if_fail (GSM_IS_APP (app));
if (priv->registered != registered) {
priv->registered = registered;
g_object_notify (G_OBJECT (app), "registered");
}
}
gboolean
gsm_app_save_to_keyfile (GsmApp *app,
GKeyFile *keyfile,
GError **error)
{
GsmAppPrivate *priv = gsm_app_get_instance_private (app);
g_debug ("Saving app: %s", priv->id);
return GSM_APP_GET_CLASS (app)->impl_save_to_keyfile (app, keyfile, error);
}

119
gnome-session/gsm-app.h Normal file
View file

@ -0,0 +1,119 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2007 Novell, Inc.
* Copyright (C) 2008 Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GSM_APP_H__
#define __GSM_APP_H__
#include <glib-object.h>
#include <sys/types.h>
#include "gsm-manager.h"
#include "gsm-client.h"
G_BEGIN_DECLS
#define GSM_TYPE_APP (gsm_app_get_type ())
G_DECLARE_DERIVABLE_TYPE (GsmApp, gsm_app, GSM, APP, GObject)
struct _GsmAppClass
{
GObjectClass parent_class;
/* signals */
void (*exited) (GsmApp *app,
guchar exit_code);
void (*died) (GsmApp *app,
int signal);
/* virtual methods */
gboolean (*impl_start) (GsmApp *app,
GError **error);
gboolean (*impl_restart) (GsmApp *app,
GError **error);
gboolean (*impl_stop) (GsmApp *app,
GError **error);
gboolean (*impl_provides) (GsmApp *app,
const char *service);
char ** (*impl_get_provides) (GsmApp *app);
gboolean (*impl_has_autostart_condition) (GsmApp *app,
const char *service);
gboolean (*impl_is_running) (GsmApp *app);
gboolean (*impl_get_autorestart) (GsmApp *app);
const char *(*impl_get_app_id) (GsmApp *app);
gboolean (*impl_is_disabled) (GsmApp *app);
gboolean (*impl_is_conditionally_disabled) (GsmApp *app);
gboolean (*impl_save_to_keyfile) (GsmApp *app,
GKeyFile *keyfile,
GError **error);
};
typedef enum
{
GSM_APP_ERROR_GENERAL = 0,
GSM_APP_ERROR_RESTART_LIMIT,
GSM_APP_ERROR_START,
GSM_APP_ERROR_STOP,
GSM_APP_NUM_ERRORS
} GsmAppError;
#define GSM_APP_ERROR gsm_app_error_quark ()
GQuark gsm_app_error_quark (void);
gboolean gsm_app_peek_autorestart (GsmApp *app);
const char *gsm_app_peek_id (GsmApp *app);
const char *gsm_app_peek_app_id (GsmApp *app);
const char *gsm_app_peek_startup_id (GsmApp *app);
GsmManagerPhase gsm_app_peek_phase (GsmApp *app);
gboolean gsm_app_peek_is_disabled (GsmApp *app);
gboolean gsm_app_peek_is_conditionally_disabled (GsmApp *app);
gboolean gsm_app_start (GsmApp *app,
GError **error);
gboolean gsm_app_restart (GsmApp *app,
GError **error);
gboolean gsm_app_stop (GsmApp *app,
GError **error);
gboolean gsm_app_is_running (GsmApp *app);
void gsm_app_exited (GsmApp *app,
guchar exit_code);
void gsm_app_died (GsmApp *app,
int signal);
gboolean gsm_app_provides (GsmApp *app,
const char *service);
char **gsm_app_get_provides (GsmApp *app);
gboolean gsm_app_has_autostart_condition (GsmApp *app,
const char *condition);
gboolean gsm_app_get_registered (GsmApp *app);
void gsm_app_set_registered (GsmApp *app,
gboolean registered);
gboolean gsm_app_save_to_keyfile (GsmApp *app,
GKeyFile *keyfile,
GError **error);
G_END_DECLS
#endif /* __GSM_APP_H__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,59 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2007 Novell, Inc.
* Copyright (C) 2008 Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GSM_AUTOSTART_APP_H__
#define __GSM_AUTOSTART_APP_H__
#include "gsm-app.h"
G_BEGIN_DECLS
#define GSM_TYPE_AUTOSTART_APP (gsm_autostart_app_get_type ())
G_DECLARE_DERIVABLE_TYPE (GsmAutostartApp, gsm_autostart_app, GSM, AUTOSTART_APP, GsmApp)
struct _GsmAutostartAppClass
{
GsmAppClass parent_class;
/* signals */
void (*condition_changed) (GsmApp *app,
gboolean condition);
};
GsmApp *gsm_autostart_app_new (const char *desktop_file,
gboolean mask_systemd,
GError **error);
void gsm_autostart_app_add_provides (GsmAutostartApp *aapp,
const char *provides);
#define GSM_AUTOSTART_APP_SYSTEMD_KEY "X-GNOME-HiddenUnderSystemd"
#define GSM_AUTOSTART_APP_ENABLED_KEY "X-GNOME-Autostart-enabled"
#define GSM_AUTOSTART_APP_PHASE_KEY "X-GNOME-Autostart-Phase"
#define GSM_AUTOSTART_APP_PROVIDES_KEY "X-GNOME-Provides"
#define GSM_AUTOSTART_APP_STARTUP_ID_KEY "X-GNOME-Autostart-startup-id"
#define GSM_AUTOSTART_APP_AUTORESTART_KEY "X-GNOME-AutoRestart"
#define GSM_AUTOSTART_APP_DBUS_NAME_KEY "X-GNOME-DBus-Name"
#define GSM_AUTOSTART_APP_DBUS_PATH_KEY "X-GNOME-DBus-Path"
#define GSM_AUTOSTART_APP_DBUS_ARGS_KEY "X-GNOME-DBus-Start-Arguments"
#define GSM_AUTOSTART_APP_DISCARD_KEY "X-GNOME-Autostart-discard-exec"
G_END_DECLS
#endif /* __GSM_AUTOSTART_APP_H__ */

583
gnome-session/gsm-client.c Normal file
View file

@ -0,0 +1,583 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2007 Novell, Inc.
* Copyright (C) 2008 Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gsm-client.h"
#include "org.gnome.SessionManager.Client.h"
static guint32 client_serial = 1;
typedef struct
{
char *id;
char *startup_id;
char *app_id;
guint status;
GsmExportedClient *skeleton;
GDBusConnection *connection;
} GsmClientPrivate;
typedef enum {
PROP_STARTUP_ID = 1,
PROP_APP_ID,
PROP_STATUS,
} GsmClientProperty;
static GParamSpec *props[PROP_STATUS + 1] = { NULL, };
enum {
DISCONNECTED,
END_SESSION_RESPONSE,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GsmClient, gsm_client, G_TYPE_OBJECT)
#define GSM_CLIENT_DBUS_IFACE "org.gnome.SessionManager.Client"
static const GDBusErrorEntry gsm_client_error_entries[] = {
{ GSM_CLIENT_ERROR_GENERAL, GSM_CLIENT_DBUS_IFACE ".GeneralError" },
{ GSM_CLIENT_ERROR_NOT_REGISTERED, GSM_CLIENT_DBUS_IFACE ".NotRegistered" }
};
GQuark
gsm_client_error_quark (void)
{
static volatile gsize quark_volatile = 0;
g_dbus_error_register_error_domain ("gsm_client_error",
&quark_volatile,
gsm_client_error_entries,
G_N_ELEMENTS (gsm_client_error_entries));
return quark_volatile;
}
static guint32
get_next_client_serial (void)
{
guint32 serial;
serial = client_serial++;
if ((gint32)client_serial < 0) {
client_serial = 1;
}
return serial;
}
static gboolean
gsm_client_get_startup_id (GsmExportedClient *skeleton,
GDBusMethodInvocation *invocation,
GsmClient *client)
{
GsmClientPrivate *priv = gsm_client_get_instance_private (client);
gsm_exported_client_complete_get_startup_id (skeleton, invocation, priv->startup_id);
return TRUE;
}
static gboolean
gsm_client_get_app_id (GsmExportedClient *skeleton,
GDBusMethodInvocation *invocation,
GsmClient *client)
{
GsmClientPrivate *priv = gsm_client_get_instance_private (client);
gsm_exported_client_complete_get_app_id (skeleton, invocation, priv->app_id);
return TRUE;
}
static gboolean
gsm_client_get_restart_style_hint (GsmExportedClient *skeleton,
GDBusMethodInvocation *invocation,
GsmClient *client)
{
guint hint;
hint = GSM_CLIENT_GET_CLASS (client)->impl_get_restart_style_hint (client);
gsm_exported_client_complete_get_restart_style_hint (skeleton, invocation, hint);
return TRUE;
}
static gboolean
gsm_client_get_status (GsmExportedClient *skeleton,
GDBusMethodInvocation *invocation,
GsmClient *client)
{
GsmClientPrivate *priv = gsm_client_get_instance_private (client);
gsm_exported_client_complete_get_status (skeleton, invocation, priv->status);
return TRUE;
}
static gboolean
gsm_client_get_unix_process_id (GsmExportedClient *skeleton,
GDBusMethodInvocation *invocation,
GsmClient *client)
{
guint pid;
pid = GSM_CLIENT_GET_CLASS (client)->impl_get_unix_process_id (client);
gsm_exported_client_complete_get_unix_process_id (skeleton, invocation, pid);
return TRUE;
}
static gboolean
gsm_client_stop_dbus (GsmExportedClient *skeleton,
GDBusMethodInvocation *invocation,
GsmClient *client)
{
GError *error = NULL;
gsm_client_stop (client, &error);
if (error != NULL) {
g_dbus_method_invocation_take_error (invocation, error);
} else {
gsm_exported_client_complete_stop (skeleton, invocation);
}
return TRUE;
}
static gboolean
register_client (GsmClient *client)
{
GsmClientPrivate *priv = gsm_client_get_instance_private (client);
GError *error = NULL;
GsmExportedClient *skeleton;
priv->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
if (priv->connection == NULL) {
g_critical ("error getting session bus: %s", error->message);
g_error_free (error);
return FALSE;
}
skeleton = gsm_exported_client_skeleton_new ();
priv->skeleton = skeleton;
g_debug ("exporting client to object path: %s", priv->id);
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (skeleton),
priv->connection,
priv->id, &error);
if (error != NULL) {
g_critical ("error exporting client on session bus: %s", error->message);
g_error_free (error);
return FALSE;
}
g_signal_connect (skeleton, "handle-get-app-id",
G_CALLBACK (gsm_client_get_app_id), client);
g_signal_connect (skeleton, "handle-get-restart-style-hint",
G_CALLBACK (gsm_client_get_restart_style_hint), client);
g_signal_connect (skeleton, "handle-get-startup-id",
G_CALLBACK (gsm_client_get_startup_id), client);
g_signal_connect (skeleton, "handle-get-status",
G_CALLBACK (gsm_client_get_status), client);
g_signal_connect (skeleton, "handle-get-unix-process-id",
G_CALLBACK (gsm_client_get_unix_process_id), client);
g_signal_connect (skeleton, "handle-stop",
G_CALLBACK (gsm_client_stop_dbus), client);
return TRUE;
}
static GObject *
gsm_client_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
GsmClientPrivate *priv;
GsmClient *client;
gboolean res;
client = GSM_CLIENT (G_OBJECT_CLASS (gsm_client_parent_class)->constructor (type,
n_construct_properties,
construct_properties));
priv = gsm_client_get_instance_private (client);
g_free (priv->id);
priv->id = g_strdup_printf ("/org/gnome/SessionManager/Client%u", get_next_client_serial ());
res = register_client (client);
if (! res) {
g_warning ("Unable to register client with session bus");
}
return G_OBJECT (client);
}
static void
gsm_client_init (GsmClient *client)
{
}
static void
gsm_client_finalize (GObject *object)
{
GsmClient *client;
GsmClientPrivate *priv;
g_return_if_fail (object != NULL);
g_return_if_fail (GSM_IS_CLIENT (object));
client = GSM_CLIENT (object);
priv = gsm_client_get_instance_private (client);
g_return_if_fail (priv != NULL);
g_free (priv->id);
g_free (priv->startup_id);
g_free (priv->app_id);
if (priv->skeleton != NULL) {
g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (priv->skeleton),
priv->connection);
g_clear_object (&priv->skeleton);
}
g_clear_object (&priv->connection);
G_OBJECT_CLASS (gsm_client_parent_class)->finalize (object);
}
void
gsm_client_set_status (GsmClient *client,
guint status)
{
GsmClientPrivate *priv = gsm_client_get_instance_private (client);
g_return_if_fail (GSM_IS_CLIENT (client));
if (priv->status != status) {
priv->status = status;
g_object_notify_by_pspec (G_OBJECT (client), props[PROP_STATUS]);
}
}
static void
gsm_client_set_startup_id (GsmClient *client,
const char *startup_id)
{
GsmClientPrivate *priv = gsm_client_get_instance_private (client);
g_return_if_fail (GSM_IS_CLIENT (client));
g_free (priv->startup_id);
if (startup_id != NULL) {
priv->startup_id = g_strdup (startup_id);
} else {
priv->startup_id = g_strdup ("");
}
g_object_notify_by_pspec (G_OBJECT (client), props[PROP_STARTUP_ID]);
}
void
gsm_client_set_app_id (GsmClient *client,
const char *app_id)
{
GsmClientPrivate *priv = gsm_client_get_instance_private (client);
g_return_if_fail (GSM_IS_CLIENT (client));
g_free (priv->app_id);
if (app_id != NULL) {
priv->app_id = g_strdup (app_id);
} else {
priv->app_id = g_strdup ("");
}
g_object_notify_by_pspec (G_OBJECT (client), props[PROP_APP_ID]);
}
static void
gsm_client_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GsmClient *self;
self = GSM_CLIENT (object);
switch ((GsmClientProperty) prop_id) {
case PROP_STARTUP_ID:
gsm_client_set_startup_id (self, g_value_get_string (value));
break;
case PROP_APP_ID:
gsm_client_set_app_id (self, g_value_get_string (value));
break;
case PROP_STATUS:
gsm_client_set_status (self, g_value_get_uint (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gsm_client_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GsmClient *self = GSM_CLIENT (object);
GsmClientPrivate *priv = gsm_client_get_instance_private (self);
switch ((GsmClientProperty) prop_id) {
case PROP_STARTUP_ID:
g_value_set_string (value, priv->startup_id);
break;
case PROP_APP_ID:
g_value_set_string (value, priv->app_id);
break;
case PROP_STATUS:
g_value_set_uint (value, priv->status);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean
default_stop (GsmClient *client,
GError **error)
{
g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE);
g_warning ("Stop not implemented");
return TRUE;
}
static void
gsm_client_dispose (GObject *object)
{
GsmClient *client;
GsmClientPrivate *priv;
g_return_if_fail (object != NULL);
g_return_if_fail (GSM_IS_CLIENT (object));
client = GSM_CLIENT (object);
priv = gsm_client_get_instance_private (client);
g_debug ("GsmClient: disposing %s", priv->id);
G_OBJECT_CLASS (gsm_client_parent_class)->dispose (object);
}
static void
gsm_client_class_init (GsmClientClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = gsm_client_get_property;
object_class->set_property = gsm_client_set_property;
object_class->constructor = gsm_client_constructor;
object_class->finalize = gsm_client_finalize;
object_class->dispose = gsm_client_dispose;
klass->impl_stop = default_stop;
signals[DISCONNECTED] =
g_signal_new ("disconnected",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GsmClientClass, disconnected),
NULL, NULL, NULL,
G_TYPE_NONE,
0);
signals[END_SESSION_RESPONSE] =
g_signal_new ("end-session-response",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GsmClientClass, end_session_response),
NULL, NULL, NULL,
G_TYPE_NONE,
4, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_STRING);
props[PROP_STARTUP_ID] =
g_param_spec_string ("startup-id",
"startup-id",
"startup-id",
"",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
props[PROP_APP_ID] =
g_param_spec_string ("app-id",
"app-id",
"app-id",
"",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
props[PROP_STATUS] =
g_param_spec_uint ("status",
"status",
"status",
0,
G_MAXINT,
GSM_CLIENT_UNREGISTERED,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, G_N_ELEMENTS (props), props);
}
const char *
gsm_client_peek_id (GsmClient *client)
{
GsmClientPrivate *priv = gsm_client_get_instance_private (client);
g_return_val_if_fail (GSM_IS_CLIENT (client), NULL);
return priv->id;
}
/**
* gsm_client_peek_app_id:
* @client: a #GsmClient.
*
* Note that the application ID might not be known; this happens when for XSMP
* clients that we did not start ourselves, for instance.
*
* Returns: the application ID of the client, or %NULL if no such ID is
* known. The string is owned by @client.
**/
const char *
gsm_client_peek_app_id (GsmClient *client)
{
GsmClientPrivate *priv = gsm_client_get_instance_private (client);
g_return_val_if_fail (GSM_IS_CLIENT (client), NULL);
return priv->app_id;
}
const char *
gsm_client_peek_startup_id (GsmClient *client)
{
GsmClientPrivate *priv = gsm_client_get_instance_private (client);
g_return_val_if_fail (GSM_IS_CLIENT (client), NULL);
return priv->startup_id;
}
guint
gsm_client_peek_status (GsmClient *client)
{
GsmClientPrivate *priv = gsm_client_get_instance_private (client);
g_return_val_if_fail (GSM_IS_CLIENT (client), GSM_CLIENT_UNREGISTERED);
return priv->status;
}
guint
gsm_client_peek_restart_style_hint (GsmClient *client)
{
g_return_val_if_fail (GSM_IS_CLIENT (client), GSM_CLIENT_RESTART_NEVER);
return GSM_CLIENT_GET_CLASS (client)->impl_get_restart_style_hint (client);
}
/**
* gsm_client_get_app_name:
* @client: a #GsmClient.
*
* Returns: a copy of the application name of the client, or %NULL if no such
* name is known.
**/
char *
gsm_client_get_app_name (GsmClient *client)
{
g_return_val_if_fail (GSM_IS_CLIENT (client), NULL);
return GSM_CLIENT_GET_CLASS (client)->impl_get_app_name (client);
}
gboolean
gsm_client_cancel_end_session (GsmClient *client,
GError **error)
{
g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE);
return GSM_CLIENT_GET_CLASS (client)->impl_cancel_end_session (client, error);
}
gboolean
gsm_client_query_end_session (GsmClient *client,
GsmClientEndSessionFlag flags,
GError **error)
{
g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE);
return GSM_CLIENT_GET_CLASS (client)->impl_query_end_session (client, flags, error);
}
gboolean
gsm_client_end_session (GsmClient *client,
GsmClientEndSessionFlag flags,
GError **error)
{
g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE);
return GSM_CLIENT_GET_CLASS (client)->impl_end_session (client, flags, error);
}
gboolean
gsm_client_stop (GsmClient *client,
GError **error)
{
g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE);
return GSM_CLIENT_GET_CLASS (client)->impl_stop (client, error);
}
void
gsm_client_disconnected (GsmClient *client)
{
g_signal_emit (client, signals[DISCONNECTED], 0);
}
GKeyFile *
gsm_client_save (GsmClient *client,
GsmApp *app,
GError **error)
{
g_return_val_if_fail (GSM_IS_CLIENT (client), NULL);
return GSM_CLIENT_GET_CLASS (client)->impl_save (client, app, error);
}
void
gsm_client_end_session_response (GsmClient *client,
gboolean is_ok,
gboolean do_last,
gboolean cancel,
const char *reason)
{
g_signal_emit (client, signals[END_SESSION_RESPONSE], 0,
is_ok, do_last, cancel, reason);
}

140
gnome-session/gsm-client.h Normal file
View file

@ -0,0 +1,140 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2007 Novell, Inc.
* Copyright (C) 2008 Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GSM_CLIENT_H__
#define __GSM_CLIENT_H__
#include <glib.h>
#include <glib-object.h>
#include <sys/types.h>
G_BEGIN_DECLS
#define GSM_TYPE_CLIENT (gsm_client_get_type ())
G_DECLARE_DERIVABLE_TYPE (GsmClient, gsm_client, GSM, CLIENT, GObject)
typedef struct _GsmApp GsmApp;
typedef struct _GsmClient GsmClient;
typedef struct _GsmClientClass GsmClientClass;
typedef enum {
GSM_CLIENT_UNREGISTERED = 0,
GSM_CLIENT_REGISTERED,
GSM_CLIENT_FINISHED,
GSM_CLIENT_FAILED
} GsmClientStatus;
typedef enum {
GSM_CLIENT_RESTART_NEVER = 0,
GSM_CLIENT_RESTART_IF_RUNNING,
GSM_CLIENT_RESTART_ANYWAY,
GSM_CLIENT_RESTART_IMMEDIATELY
} GsmClientRestartStyle;
typedef enum {
GSM_CLIENT_END_SESSION_FLAG_FORCEFUL = 1 << 0,
GSM_CLIENT_END_SESSION_FLAG_SAVE = 1 << 1,
GSM_CLIENT_END_SESSION_FLAG_LAST = 1 << 2
} GsmClientEndSessionFlag;
struct _GsmClientClass
{
GObjectClass parent_class;
/* signals */
void (*disconnected) (GsmClient *client);
void (*end_session_response) (GsmClient *client,
gboolean ok,
gboolean do_last,
gboolean cancel,
const char *reason);
/* virtual methods */
char * (*impl_get_app_name) (GsmClient *client);
GsmClientRestartStyle (*impl_get_restart_style_hint) (GsmClient *client);
guint (*impl_get_unix_process_id) (GsmClient *client);
gboolean (*impl_query_end_session) (GsmClient *client,
GsmClientEndSessionFlag flags,
GError **error);
gboolean (*impl_end_session) (GsmClient *client,
GsmClientEndSessionFlag flags,
GError **error);
gboolean (*impl_cancel_end_session) (GsmClient *client,
GError **error);
gboolean (*impl_stop) (GsmClient *client,
GError **error);
GKeyFile * (*impl_save) (GsmClient *client,
GsmApp *app,
GError **error);
};
typedef enum
{
GSM_CLIENT_ERROR_GENERAL = 0,
GSM_CLIENT_ERROR_NOT_REGISTERED,
GSM_CLIENT_NUM_ERRORS
} GsmClientError;
#define GSM_CLIENT_ERROR gsm_client_error_quark ()
GQuark gsm_client_error_quark (void);
const char *gsm_client_peek_id (GsmClient *client);
const char * gsm_client_peek_startup_id (GsmClient *client);
const char * gsm_client_peek_app_id (GsmClient *client);
guint gsm_client_peek_restart_style_hint (GsmClient *client);
guint gsm_client_peek_status (GsmClient *client);
char *gsm_client_get_app_name (GsmClient *client);
void gsm_client_set_app_id (GsmClient *client,
const char *app_id);
void gsm_client_set_status (GsmClient *client,
guint status);
gboolean gsm_client_end_session (GsmClient *client,
guint flags,
GError **error);
gboolean gsm_client_query_end_session (GsmClient *client,
guint flags,
GError **error);
gboolean gsm_client_cancel_end_session (GsmClient *client,
GError **error);
void gsm_client_disconnected (GsmClient *client);
GKeyFile *gsm_client_save (GsmClient *client,
GsmApp *app,
GError **error);
gboolean gsm_client_stop (GsmClient *client,
GError **error);
/* private */
void gsm_client_end_session_response (GsmClient *client,
gboolean is_ok,
gboolean do_last,
gboolean cancel,
const char *reason);
G_END_DECLS
#endif /* __GSM_CLIENT_H__ */

View file

@ -0,0 +1,439 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2008 Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <gio/gio.h>
#include "org.gnome.SessionManager.ClientPrivate.h"
#include "gsm-dbus-client.h"
#include "gsm-manager.h"
#include "gsm-util.h"
#define SM_DBUS_NAME "org.gnome.SessionManager"
#define SM_DBUS_CLIENT_PRIVATE_INTERFACE "org.gnome.SessionManager.ClientPrivate"
struct _GsmDBusClient
{
GObject parent_instance;
char *bus_name;
GPid caller_pid;
GsmClientRestartStyle restart_style_hint;
GDBusConnection *connection;
GsmExportedClientPrivate *skeleton;
guint watch_id;
};
typedef enum {
PROP_BUS_NAME = 1,
} GsmDBusClientProperty;
static GParamSpec *props[PROP_BUS_NAME + 1] = { NULL, };
G_DEFINE_TYPE (GsmDBusClient, gsm_dbus_client, GSM_TYPE_CLIENT)
static gboolean
setup_connection (GsmDBusClient *client)
{
GError *error = NULL;
if (client->connection == NULL) {
client->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
if (error != NULL) {
g_debug ("GsmDbusClient: Couldn't connect to session bus: %s",
error->message);
g_error_free (error);
return FALSE;
}
}
return TRUE;
}
static gboolean
handle_end_session_response (GsmExportedClientPrivate *skeleton,
GDBusMethodInvocation *invocation,
gboolean is_ok,
const char *reason,
GsmDBusClient *client)
{
g_debug ("GsmDBusClient: got EndSessionResponse is-ok:%d reason=%s", is_ok, reason);
gsm_client_end_session_response (GSM_CLIENT (client),
is_ok, FALSE, FALSE, reason);
gsm_exported_client_private_complete_end_session_response (skeleton, invocation);
return TRUE;
}
static GObject *
gsm_dbus_client_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
GsmDBusClient *client;
GError *error = NULL;
GsmExportedClientPrivate *skeleton;
client = GSM_DBUS_CLIENT (G_OBJECT_CLASS (gsm_dbus_client_parent_class)->constructor (type,
n_construct_properties,
construct_properties));
if (! setup_connection (client)) {
g_object_unref (client);
return NULL;
}
skeleton = gsm_exported_client_private_skeleton_new ();
client->skeleton = skeleton;
g_debug ("exporting dbus client to object path: %s", gsm_client_peek_id (GSM_CLIENT (client)));
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (skeleton),
client->connection,
gsm_client_peek_id (GSM_CLIENT (client)),
&error);
if (error != NULL) {
g_critical ("error exporting client private on session bus: %s", error->message);
g_error_free (error);
g_object_unref (client);
return NULL;
}
g_signal_connect (skeleton, "handle-end-session-response",
G_CALLBACK (handle_end_session_response), client);
return G_OBJECT (client);
}
static void
gsm_dbus_client_init (GsmDBusClient *client)
{
}
/* adapted from PolicyKit */
static gboolean
get_caller_info (GsmDBusClient *client,
const char *sender,
uid_t *calling_uid_out,
pid_t *calling_pid_out)
{
g_autoptr(GDBusConnection) connection = NULL;
GError *error;
g_autoptr(GVariant) uid_variant = NULL;
g_autoptr(GVariant) pid_variant = NULL;
uid_t uid;
pid_t pid;
if (sender == NULL) {
return FALSE;
}
error = NULL;
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
if (error != NULL) {
g_warning ("error getting session bus: %s", error->message);
g_error_free (error);
return FALSE;
}
uid_variant = g_dbus_connection_call_sync (connection,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"GetConnectionUnixUser",
g_variant_new ("(s)", sender),
G_VARIANT_TYPE ("(u)"),
G_DBUS_CALL_FLAGS_NONE,
-1, NULL, &error);
if (error != NULL) {
g_debug ("GetConnectionUnixUser() failed: %s", error->message);
g_error_free (error);
return FALSE;
}
pid_variant = g_dbus_connection_call_sync (connection,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"GetConnectionUnixProcessID",
g_variant_new ("(s)", sender),
G_VARIANT_TYPE ("(u)"),
G_DBUS_CALL_FLAGS_NONE,
-1, NULL, &error);
if (error != NULL) {
g_debug ("GetConnectionUnixProcessID() failed: %s", error->message);
g_error_free (error);
return FALSE;
}
g_variant_get (uid_variant, "(u)", &uid);
g_variant_get (pid_variant, "(u)", &pid);
if (calling_uid_out != NULL) {
*calling_uid_out = uid;
}
if (calling_pid_out != NULL) {
*calling_pid_out = pid;
}
g_debug ("uid = %d", uid);
g_debug ("pid = %d", pid);
return TRUE;
}
static void
on_client_vanished (GDBusConnection *connection,
const char *name,
gpointer user_data)
{
GsmDBusClient *client = user_data;
g_bus_unwatch_name (client->watch_id);
client->watch_id = 0;
gsm_client_disconnected (GSM_CLIENT (client));
}
static void
gsm_dbus_client_set_bus_name (GsmDBusClient *client,
const char *bus_name)
{
g_return_if_fail (GSM_IS_DBUS_CLIENT (client));
g_free (client->bus_name);
client->bus_name = g_strdup (bus_name);
g_object_notify_by_pspec (G_OBJECT (client), props[PROP_BUS_NAME]);
if (!get_caller_info (client, bus_name, NULL, &client->caller_pid)) {
client->caller_pid = 0;
}
client->watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
bus_name,
G_BUS_NAME_WATCHER_FLAGS_NONE,
NULL,
on_client_vanished,
client,
NULL);
}
const char *
gsm_dbus_client_get_bus_name (GsmDBusClient *client)
{
g_return_val_if_fail (GSM_IS_DBUS_CLIENT (client), NULL);
return client->bus_name;
}
static void
gsm_dbus_client_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GsmDBusClient *self = GSM_DBUS_CLIENT (object);
switch ((GsmDBusClientProperty) prop_id) {
case PROP_BUS_NAME:
gsm_dbus_client_set_bus_name (self, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gsm_dbus_client_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GsmDBusClient *self = GSM_DBUS_CLIENT (object);
switch ((GsmDBusClientProperty) prop_id) {
case PROP_BUS_NAME:
g_value_set_string (value, self->bus_name);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gsm_dbus_client_finalize (GObject *object)
{
GsmDBusClient *client = (GsmDBusClient *) object;
g_free (client->bus_name);
if (client->skeleton != NULL) {
g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (client->skeleton),
client->connection);
g_clear_object (&client->skeleton);
}
g_clear_object (&client->connection);
if (client->watch_id != 0)
g_bus_unwatch_name (client->watch_id);
G_OBJECT_CLASS (gsm_dbus_client_parent_class)->finalize (object);
}
static GKeyFile *
dbus_client_save (GsmClient *client,
GsmApp *app,
GError **error)
{
g_debug ("GsmDBusClient: saving client with id %s",
gsm_client_peek_id (client));
/* FIXME: We still don't support client saving for D-Bus
* session clients */
return NULL;
}
static gboolean
dbus_client_stop (GsmClient *client,
GError **error)
{
GsmDBusClient *dbus_client = (GsmDBusClient *) client;
gsm_exported_client_private_emit_stop (dbus_client->skeleton);
return TRUE;
}
static char *
dbus_client_get_app_name (GsmClient *client)
{
/* Always use app-id instead */
return NULL;
}
static GsmClientRestartStyle
dbus_client_get_restart_style_hint (GsmClient *client)
{
return (GSM_DBUS_CLIENT (client)->restart_style_hint);
}
static guint
dbus_client_get_unix_process_id (GsmClient *client)
{
return (GSM_DBUS_CLIENT (client)->caller_pid);
}
static gboolean
dbus_client_query_end_session (GsmClient *client,
GsmClientEndSessionFlag flags,
GError **error)
{
GsmDBusClient *dbus_client = (GsmDBusClient *) client;
if (dbus_client->bus_name == NULL) {
g_set_error (error,
GSM_CLIENT_ERROR,
GSM_CLIENT_ERROR_NOT_REGISTERED,
"Client is not registered");
return FALSE;
}
g_debug ("GsmDBusClient: sending QueryEndSession signal to %s", dbus_client->bus_name);
gsm_exported_client_private_emit_query_end_session (dbus_client->skeleton, flags);
return TRUE;
}
static gboolean
dbus_client_end_session (GsmClient *client,
GsmClientEndSessionFlag flags,
GError **error)
{
GsmDBusClient *dbus_client = (GsmDBusClient *) client;
gsm_exported_client_private_emit_end_session (dbus_client->skeleton, flags);
return TRUE;
}
static gboolean
dbus_client_cancel_end_session (GsmClient *client,
GError **error)
{
GsmDBusClient *dbus_client = (GsmDBusClient *) client;
gsm_exported_client_private_emit_cancel_end_session (dbus_client->skeleton);
return TRUE;
}
static void
gsm_dbus_client_class_init (GsmDBusClientClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GsmClientClass *client_class = GSM_CLIENT_CLASS (klass);
object_class->finalize = gsm_dbus_client_finalize;
object_class->constructor = gsm_dbus_client_constructor;
object_class->get_property = gsm_dbus_client_get_property;
object_class->set_property = gsm_dbus_client_set_property;
client_class->impl_save = dbus_client_save;
client_class->impl_stop = dbus_client_stop;
client_class->impl_query_end_session = dbus_client_query_end_session;
client_class->impl_end_session = dbus_client_end_session;
client_class->impl_cancel_end_session = dbus_client_cancel_end_session;
client_class->impl_get_app_name = dbus_client_get_app_name;
client_class->impl_get_restart_style_hint = dbus_client_get_restart_style_hint;
client_class->impl_get_unix_process_id = dbus_client_get_unix_process_id;
props[PROP_BUS_NAME] =
g_param_spec_string ("bus-name",
"bus-name",
"bus-name",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, G_N_ELEMENTS (props), props);
}
GsmClient *
gsm_dbus_client_new (const char *startup_id,
const char *bus_name)
{
GsmDBusClient *client;
client = g_object_new (GSM_TYPE_DBUS_CLIENT,
"startup-id", startup_id,
"bus-name", bus_name,
NULL);
return GSM_CLIENT (client);
}

View file

@ -0,0 +1,35 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2008 Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GSM_DBUS_CLIENT_H__
#define __GSM_DBUS_CLIENT_H__
#include "gsm-client.h"
G_BEGIN_DECLS
#define GSM_TYPE_DBUS_CLIENT (gsm_dbus_client_get_type ())
G_DECLARE_FINAL_TYPE (GsmDBusClient, gsm_dbus_client, GSM, DBUS_CLIENT, GsmClient)
GsmClient * gsm_dbus_client_new (const char *startup_id,
const char *bus_name);
const char * gsm_dbus_client_get_bus_name (GsmDBusClient *client);
G_END_DECLS
#endif /* __GSM_DBUS_CLIENT_H__ */

View file

@ -0,0 +1,461 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2011 Red Hat, Inc.
* Copyright (C) 2019 Canonical Ltd.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Colin Walters <walters@verbum.org>
* Marco Trevisan <marco@ubuntu.com>
*/
#include <config.h>
#include <stdlib.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#ifdef GDK_WINDOWING_X11
#include <gtk/gtkx.h>
#endif
#include "gsm-fail-whale-dialog.h"
#include "gsm-icon-names.h"
struct _GsmFailWhaleDialog
{
GtkWindow parent;
gboolean debug_mode;
gboolean allow_logout;
gboolean extensions;
GdkMonitor *monitor;
GdkRectangle geometry;
};
G_DEFINE_TYPE (GsmFailWhaleDialog, gsm_fail_whale_dialog, GTK_TYPE_WINDOW);
/* derived from tomboy */
static void
_window_override_user_time (GsmFailWhaleDialog *window)
{
guint32 ev_time = gtk_get_current_event_time ();
GdkWindow *gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
#ifdef GDK_WINDOWING_X11
if (!GDK_IS_X11_WINDOW (gdk_window))
return;
if (ev_time == 0) {
gint ev_mask = gtk_widget_get_events (GTK_WIDGET (window));
if (!(ev_mask & GDK_PROPERTY_CHANGE_MASK)) {
gtk_widget_add_events (GTK_WIDGET (window),
GDK_PROPERTY_CHANGE_MASK);
}
/*
* NOTE: Last resort for D-BUS or other non-interactive
* openings. Causes roundtrip to server. Lame.
*/
ev_time = gdk_x11_get_server_time (gdk_window);
}
gdk_x11_window_set_user_time (gdk_window, ev_time);
#endif
}
static void
_window_move_resize_window (GsmFailWhaleDialog *window,
gboolean move,
gboolean resize)
{
if (window->debug_mode)
return;
g_debug ("Move and/or resize window x=%d y=%d w=%d h=%d",
window->geometry.x,
window->geometry.y,
window->geometry.width,
window->geometry.height);
if (resize) {
gtk_window_resize (GTK_WINDOW (window),
window->geometry.width,
window->geometry.height);
}
if (move) {
gtk_window_move (GTK_WINDOW (window),
window->geometry.x,
window->geometry.y);
}
}
static void
update_geometry (GsmFailWhaleDialog *fail_dialog)
{
gdk_monitor_get_geometry (fail_dialog->monitor, &fail_dialog->geometry);
}
static void
on_screen_size_changed (GdkScreen *screen,
GsmFailWhaleDialog *fail_dialog)
{
gtk_widget_queue_resize (GTK_WIDGET (fail_dialog));
}
static void
gsm_fail_whale_dialog_realize (GtkWidget *widget)
{
if (GTK_WIDGET_CLASS (gsm_fail_whale_dialog_parent_class)->realize) {
GTK_WIDGET_CLASS (gsm_fail_whale_dialog_parent_class)->realize (widget);
}
_window_override_user_time (GSM_FAIL_WHALE_DIALOG (widget));
update_geometry (GSM_FAIL_WHALE_DIALOG (widget));
_window_move_resize_window (GSM_FAIL_WHALE_DIALOG (widget), TRUE, TRUE);
g_signal_connect (gtk_window_get_screen (GTK_WINDOW (widget)),
"size_changed",
G_CALLBACK (on_screen_size_changed),
widget);
}
static void
gsm_fail_whale_dialog_unrealize (GtkWidget *widget)
{
g_signal_handlers_disconnect_by_func (gtk_window_get_screen (GTK_WINDOW (widget)),
on_screen_size_changed,
widget);
if (GTK_WIDGET_CLASS (gsm_fail_whale_dialog_parent_class)->unrealize) {
GTK_WIDGET_CLASS (gsm_fail_whale_dialog_parent_class)->unrealize (widget);
}
}
static void
gsm_fail_whale_dialog_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
GsmFailWhaleDialog *fail_dialog;
GdkRectangle old_geometry;
int position_changed = FALSE;
int size_changed = FALSE;
fail_dialog = GSM_FAIL_WHALE_DIALOG (widget);
old_geometry = fail_dialog->geometry;
update_geometry (fail_dialog);
requisition->width = fail_dialog->geometry.width;
requisition->height = fail_dialog->geometry.height;
if (!gtk_widget_get_realized (widget)) {
return;
}
if (old_geometry.width != fail_dialog->geometry.width ||
old_geometry.height != fail_dialog->geometry.height) {
size_changed = TRUE;
}
if (old_geometry.x != fail_dialog->geometry.x ||
old_geometry.y != fail_dialog->geometry.y) {
position_changed = TRUE;
}
_window_move_resize_window (fail_dialog,
position_changed, size_changed);
}
static void
gsm_fail_whale_dialog_get_preferred_width (GtkWidget *widget,
gint *minimal_width,
gint *natural_width)
{
GtkRequisition requisition;
gsm_fail_whale_dialog_size_request (widget, &requisition);
*minimal_width = *natural_width = requisition.width;
}
static void
gsm_fail_whale_dialog_get_preferred_width_for_height (GtkWidget *widget,
gint for_height,
gint *minimal_width,
gint *natural_width)
{
GtkRequisition requisition;
gsm_fail_whale_dialog_size_request (widget, &requisition);
*minimal_width = *natural_width = requisition.width;
}
static void
gsm_fail_whale_dialog_get_preferred_height (GtkWidget *widget,
gint *minimal_height,
gint *natural_height)
{
GtkRequisition requisition;
gsm_fail_whale_dialog_size_request (widget, &requisition);
*minimal_height = *natural_height = requisition.height;
}
static void
gsm_fail_whale_dialog_get_preferred_height_for_width (GtkWidget *widget,
gint for_width,
gint *minimal_height,
gint *natural_height)
{
GtkRequisition requisition;
gsm_fail_whale_dialog_size_request (widget, &requisition);
*minimal_height = *natural_height = requisition.height;
}
static void
gsm_fail_whale_dialog_class_init (GsmFailWhaleDialogClass *klass)
{
GtkWidgetClass *widget_class;
widget_class = GTK_WIDGET_CLASS (klass);
widget_class->realize = gsm_fail_whale_dialog_realize;
widget_class->unrealize = gsm_fail_whale_dialog_unrealize;
widget_class->get_preferred_width = gsm_fail_whale_dialog_get_preferred_width;
widget_class->get_preferred_height = gsm_fail_whale_dialog_get_preferred_height;
widget_class->get_preferred_width_for_height = gsm_fail_whale_dialog_get_preferred_width_for_height;
widget_class->get_preferred_height_for_width = gsm_fail_whale_dialog_get_preferred_height_for_width;
}
static void
on_logout_clicked (GtkWidget *button,
GsmFailWhaleDialog *fail_dialog)
{
if (!fail_dialog->debug_mode) {
g_spawn_command_line_async ("gnome-session-quit --force", NULL);
}
gtk_main_quit ();
}
static void
setup_window (GsmFailWhaleDialog *fail_dialog)
{
GtkWidget *box;
GtkWidget *image;
GtkWidget *label;
GtkWidget *message_label;
GtkWidget *button_box;
GtkWidget *button;
GdkPixbuf *fail_icon;
GdkDisplay *display;
char *markup;
int scale_factor;
int i;
gtk_window_set_title (GTK_WINDOW (fail_dialog), "");
gtk_window_set_icon_name (GTK_WINDOW (fail_dialog), GSM_ICON_COMPUTER_FAIL);
gtk_window_set_skip_taskbar_hint (GTK_WINDOW (fail_dialog), TRUE);
gtk_window_set_keep_above (GTK_WINDOW (fail_dialog), TRUE);
gtk_window_stick (GTK_WINDOW (fail_dialog));
gtk_window_set_position (GTK_WINDOW (fail_dialog), GTK_WIN_POS_CENTER_ALWAYS);
/* only works if there is a window manager which is unlikely */
display = gtk_widget_get_display (GTK_WIDGET (fail_dialog));
for (i = 0; i < gdk_display_get_n_monitors (display); i++) {
if (gdk_display_get_monitor (display, i) == fail_dialog->monitor) {
GdkScreen *screen;
screen = gtk_widget_get_screen (GTK_WIDGET (fail_dialog));
gtk_window_fullscreen_on_monitor (GTK_WINDOW (fail_dialog),
screen, i);
break;
}
}
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
gtk_widget_set_valign (box, GTK_ALIGN_CENTER);
gtk_widget_show (box);
gtk_container_add (GTK_CONTAINER (fail_dialog), box);
scale_factor = gdk_monitor_get_scale_factor (fail_dialog->monitor);
fail_icon = gtk_icon_theme_load_icon_for_scale (gtk_icon_theme_get_default (),
GSM_ICON_COMPUTER_FAIL,
128,
scale_factor,
0,
NULL);
if (fail_icon != NULL) {
image = gtk_image_new_from_pixbuf (fail_icon);
gtk_widget_show (image);
gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 0);
g_object_unref (fail_icon);
}
label = gtk_label_new (NULL);
markup = g_strdup_printf ("<b><big>%s</big></b>", _("Oh no! Something has gone wrong."));
gtk_label_set_markup (GTK_LABEL (label), markup);
g_free (markup);
gtk_widget_show (label);
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
if (!fail_dialog->allow_logout)
message_label = gtk_label_new (_("A problem has occurred and the system cant recover. Please contact a system administrator"));
else if (fail_dialog->extensions)
message_label = gtk_label_new (_("A problem has occurred and the system cant recover. All extensions have been disabled as a precaution."));
else
message_label = gtk_label_new (_("A problem has occurred and the system cant recover.\nPlease log out and try again."));
gtk_label_set_justify (GTK_LABEL (message_label), GTK_JUSTIFY_CENTER);
gtk_label_set_line_wrap (GTK_LABEL (message_label), TRUE);
gtk_widget_show (message_label);
gtk_box_pack_start (GTK_BOX (box),
message_label, FALSE, FALSE, 0);
button_box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
gtk_container_set_border_width (GTK_CONTAINER (button_box), 20);
gtk_widget_show (button_box);
gtk_box_pack_end (GTK_BOX (box),
button_box, FALSE, FALSE, 0);
if (fail_dialog->allow_logout) {
button = gtk_button_new_with_mnemonic (_("_Log Out"));
gtk_widget_show (button);
gtk_box_pack_end (GTK_BOX (button_box),
button, FALSE, FALSE, 0);
g_signal_connect (button, "clicked",
G_CALLBACK (on_logout_clicked), fail_dialog);
}
}
static void
gsm_fail_whale_dialog_init (GsmFailWhaleDialog *fail_dialog)
{
}
static gboolean debug_mode = FALSE;
static gboolean allow_logout = FALSE;
static gboolean extensions = FALSE;
static GList *dialogs = NULL;
static void
create_fail_dialog (GdkMonitor *monitor)
{
GsmFailWhaleDialog *fail_dialog;
fail_dialog = g_object_new (GSM_TYPE_FAIL_WHALE_DIALOG, NULL);
fail_dialog->debug_mode = debug_mode;
fail_dialog->allow_logout = allow_logout;
fail_dialog->extensions = extensions;
fail_dialog->monitor = monitor;
setup_window (fail_dialog);
g_signal_connect (fail_dialog, "destroy",
G_CALLBACK (gtk_main_quit), NULL);
gtk_widget_show (GTK_WIDGET (fail_dialog));
dialogs = g_list_prepend (dialogs, fail_dialog);
}
static void
on_monitor_added (GdkDisplay *display,
GdkMonitor *monitor)
{
create_fail_dialog (monitor);
}
static void
on_monitor_removed (GdkDisplay *display,
GdkMonitor *monitor)
{
GList *l;
for (l = dialogs; l;) {
GList *next = l->next;
GsmFailWhaleDialog *fail_dialog = l->data;
if (fail_dialog->monitor == monitor) {
dialogs = g_list_delete_link (dialogs, l);
gtk_widget_destroy (GTK_WIDGET (fail_dialog));
}
l = next;
}
}
int main (int argc, char *argv[])
{
GOptionEntry entries[] = {
{ "debug", 0, 0, G_OPTION_ARG_NONE, &debug_mode, N_("Enable debugging code"), NULL },
{ "allow-logout", 0, 0, G_OPTION_ARG_NONE, &allow_logout, N_("Allow logout"), NULL },
{ "extensions", 0, 0, G_OPTION_ARG_NONE, &extensions, N_("Show extension warning"), NULL },
{ NULL, 0, 0, 0, NULL, NULL, NULL }
};
GError *error = NULL;
GdkDisplay *display;
int i;
bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
if (!gtk_init_with_args (&argc, &argv, " - fail whale",
entries, GETTEXT_PACKAGE,
&error)) {
if (error != NULL) {
g_warning ("%s", error->message);
exit (1);
}
/* display server probably went away. Could be for legitimate reasons, could be for
* unexpected reasons. If it went away unexpectantly, that's logged elsewhere, so
* let's not add noise by logging here.
*/
return 0;
}
/* Force-off allow_logout when running inside GDM, this is needed
* because the systemd service always passes --allow-logout
*/
if (g_strcmp0 (g_getenv ("RUNNING_UNDER_GDM"), "true") == 0)
allow_logout = FALSE;
display = gdk_display_get_default ();
for (i = 0; i < gdk_display_get_n_monitors (display); i++) {
create_fail_dialog (gdk_display_get_monitor (display, i));
}
g_signal_connect (display, "monitor-added",
G_CALLBACK (on_monitor_added), &dialogs);
g_signal_connect (display, "monitor-removed",
G_CALLBACK (on_monitor_removed), &dialogs);
gtk_main ();
return 0;
}

View file

@ -0,0 +1,36 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2011 Red Hat, Inc.
* Copyright (C) 2019 Canonical Ltd.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Colin Walters <walters@verbum.org>
* Marco Trevisan <marco@ubuntu.com>
*/
#ifndef __GSM_FAIL_WHALE_DIALOG_H__
#define __GSM_FAIL_WHALE_DIALOG_H__
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define GSM_TYPE_FAIL_WHALE_DIALOG (gsm_fail_whale_dialog_get_type ())
G_DECLARE_FINAL_TYPE (GsmFailWhaleDialog, gsm_fail_whale_dialog, GSM, FAIL_WHALE_DIALOG, GtkWindow);
G_END_DECLS
#endif /* __GSM_FAIL_WHALE_DIALOG_H__ */

View file

@ -0,0 +1,66 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
* gsm-fail-whale.c
* Copyright (C) 2012 Red Hat, Inc
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <signal.h>
#include <stdlib.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include "gsm-fail-whale.h"
#include "gsm-util.h"
static void
on_fail_whale_failed (void)
{
raise (SIGTERM);
}
void
gsm_fail_whale_dialog_we_failed (gboolean debug_mode,
gboolean allow_logout,
GsmShellExtensions *extensions)
{
gint i;
gchar *argv[5];
static GPid pid = 0;
if (pid != 0) {
return;
}
i = 0;
argv[i++] = LIBEXECDIR "/gnome-session-failed";
if (debug_mode)
argv[i++] = "--debug";
if (allow_logout)
argv[i++] = "--allow-logout";
if (extensions != NULL && gsm_shell_extensions_n_extensions (extensions) > 0)
argv[i++] = "--extensions";
argv[i++] = NULL;
if (!g_spawn_async (NULL, argv, (char **) gsm_util_listenv (), G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL)) {
exit (1);
}
g_child_watch_add (pid,
(GChildWatchFunc)on_fail_whale_failed,
NULL);
}

View file

@ -0,0 +1,34 @@
/* gsm-fail-whale.h
* Copyright (C) 2012 Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GSM_FAIL_WHALE_H___
#define __GSM_FAIL_WHALE_H__
#include <glib.h>
#include "gsm-shell-extensions.h"
G_BEGIN_DECLS
void gsm_fail_whale_dialog_we_failed (gboolean debug_mode,
gboolean allow_logout,
GsmShellExtensions *extensions);
G_END_DECLS
#endif /* __GSM_FAIL_WHALE_H__ */

View file

@ -0,0 +1,28 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2011 Novell, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GSM_ICON_NAMES_H__
#define __GSM_ICON_NAMES_H__
#define GSM_ICON_COMPUTER_FAIL "computer-fail-symbolic"
#define GSM_ICON_INHIBITOR_DEFAULT "gnome-windows"
#define GSM_ICON_LOGOUT "system-log-out"
#define GSM_ICON_SHUTDOWN "system-shutdown"
#define GSM_ICON_XSMP_DEFAULT "system-run"
#endif /*__GSM_ICON_NAMES_H__ */

View file

@ -0,0 +1,36 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2008 Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GSM_INHIBITOR_FLAG_H__
#define __GSM_INHIBITOR_FLAG_H__
#include <glib-object.h>
G_BEGIN_DECLS
typedef enum {
GSM_INHIBITOR_FLAG_LOGOUT = 1 << 0,
GSM_INHIBITOR_FLAG_SWITCH_USER = 1 << 1,
GSM_INHIBITOR_FLAG_SUSPEND = 1 << 2,
GSM_INHIBITOR_FLAG_IDLE = 1 << 3,
GSM_INHIBITOR_FLAG_AUTOMOUNT = 1 << 4
} GsmInhibitorFlag;
G_END_DECLS
#endif /* __GSM_INHIBITOR_FLAG_H__ */

View file

@ -0,0 +1,649 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2008 Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "gsm-inhibitor.h"
#include "org.gnome.SessionManager.Inhibitor.h"
#include "gsm-util.h"
static guint32 inhibitor_serial = 1;
#define GSM_INHIBITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_INHIBITOR, GsmInhibitorPrivate))
struct GsmInhibitorPrivate
{
char *id;
char *bus_name;
char *app_id;
char *client_id;
char *reason;
guint flags;
guint toplevel_xid;
guint cookie;
GDBusConnection *connection;
GsmExportedInhibitor *skeleton;
guint watch_id;
};
enum {
PROP_0,
PROP_BUS_NAME,
PROP_REASON,
PROP_APP_ID,
PROP_CLIENT_ID,
PROP_FLAGS,
PROP_TOPLEVEL_XID,
PROP_COOKIE
};
enum {
VANISHED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE (GsmInhibitor, gsm_inhibitor, G_TYPE_OBJECT)
#define GSM_INHIBITOR_DBUS_IFACE "org.gnome.SessionManager.Inhibitor"
static const GDBusErrorEntry gsm_inhibitor_error_entries[] = {
{ GSM_INHIBITOR_ERROR_GENERAL, GSM_INHIBITOR_DBUS_IFACE ".GeneralError" },
{ GSM_INHIBITOR_ERROR_NOT_SET, GSM_INHIBITOR_DBUS_IFACE ".NotSet" }
};
GQuark
gsm_inhibitor_error_quark (void)
{
static volatile gsize quark_volatile = 0;
g_dbus_error_register_error_domain ("gsm_inhibitor_error",
&quark_volatile,
gsm_inhibitor_error_entries,
G_N_ELEMENTS (gsm_inhibitor_error_entries));
return quark_volatile;
}
static gboolean
gsm_inhibitor_get_app_id (GsmExportedInhibitor *skeleton,
GDBusMethodInvocation *invocation,
GsmInhibitor *inhibitor)
{
const gchar *id;
if (inhibitor->priv->app_id != NULL) {
id = inhibitor->priv->app_id;
} else {
id = "";
}
gsm_exported_inhibitor_complete_get_app_id (skeleton, invocation, id);
return TRUE;
}
static gboolean
gsm_inhibitor_get_client_id (GsmExportedInhibitor *skeleton,
GDBusMethodInvocation *invocation,
GsmInhibitor *inhibitor)
{
/* object paths are not allowed to be NULL or blank */
if (IS_STRING_EMPTY (inhibitor->priv->client_id)) {
g_dbus_method_invocation_return_error (invocation,
GSM_INHIBITOR_ERROR,
GSM_INHIBITOR_ERROR_NOT_SET,
"Value is not set");
return TRUE;
}
gsm_exported_inhibitor_complete_get_client_id (skeleton, invocation, inhibitor->priv->client_id);
g_debug ("GsmInhibitor: getting client-id = '%s'", inhibitor->priv->client_id);
return TRUE;
}
static gboolean
gsm_inhibitor_get_reason (GsmExportedInhibitor *skeleton,
GDBusMethodInvocation *invocation,
GsmInhibitor *inhibitor)
{
const gchar *reason;
if (inhibitor->priv->reason != NULL) {
reason = inhibitor->priv->reason;
} else {
reason = "";
}
gsm_exported_inhibitor_complete_get_reason (skeleton, invocation, reason);
return TRUE;
}
static gboolean
gsm_inhibitor_get_flags (GsmExportedInhibitor *skeleton,
GDBusMethodInvocation *invocation,
GsmInhibitor *inhibitor)
{
gsm_exported_inhibitor_complete_get_flags (skeleton, invocation, inhibitor->priv->flags);
return TRUE;
}
static gboolean
gsm_inhibitor_get_toplevel_xid (GsmExportedInhibitor *skeleton,
GDBusMethodInvocation *invocation,
GsmInhibitor *inhibitor)
{
gsm_exported_inhibitor_complete_get_toplevel_xid (skeleton, invocation, inhibitor->priv->toplevel_xid);
return TRUE;
}
static guint32
get_next_inhibitor_serial (void)
{
guint32 serial;
serial = inhibitor_serial++;
if ((gint32)inhibitor_serial < 0) {
inhibitor_serial = 1;
}
return serial;
}
static gboolean
register_inhibitor (GsmInhibitor *inhibitor)
{
GError *error;
GsmExportedInhibitor *skeleton;
error = NULL;
inhibitor->priv->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
if (error != NULL) {
g_critical ("error getting session bus: %s", error->message);
g_error_free (error);
return FALSE;
}
skeleton = gsm_exported_inhibitor_skeleton_new ();
inhibitor->priv->skeleton = skeleton;
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (skeleton),
inhibitor->priv->connection,
inhibitor->priv->id, &error);
if (error != NULL) {
g_critical ("error exporting inhibitor on session bus: %s", error->message);
g_error_free (error);
return FALSE;
}
g_signal_connect (skeleton, "handle-get-app-id",
G_CALLBACK (gsm_inhibitor_get_app_id), inhibitor);
g_signal_connect (skeleton, "handle-get-client-id",
G_CALLBACK (gsm_inhibitor_get_client_id), inhibitor);
g_signal_connect (skeleton, "handle-get-flags",
G_CALLBACK (gsm_inhibitor_get_flags), inhibitor);
g_signal_connect (skeleton, "handle-get-reason",
G_CALLBACK (gsm_inhibitor_get_reason), inhibitor);
g_signal_connect (skeleton, "handle-get-toplevel-xid",
G_CALLBACK (gsm_inhibitor_get_toplevel_xid), inhibitor);
return TRUE;
}
static GObject *
gsm_inhibitor_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
GsmInhibitor *inhibitor;
gboolean res;
inhibitor = GSM_INHIBITOR (G_OBJECT_CLASS (gsm_inhibitor_parent_class)->constructor (type,
n_construct_properties,
construct_properties));
g_free (inhibitor->priv->id);
inhibitor->priv->id = g_strdup_printf ("/org/gnome/SessionManager/Inhibitor%u", get_next_inhibitor_serial ());
res = register_inhibitor (inhibitor);
if (! res) {
g_warning ("Unable to register inhibitor with session bus");
}
return G_OBJECT (inhibitor);
}
static void
gsm_inhibitor_init (GsmInhibitor *inhibitor)
{
inhibitor->priv = GSM_INHIBITOR_GET_PRIVATE (inhibitor);
}
static void
on_inhibitor_vanished (GDBusConnection *connection,
const char *name,
gpointer user_data)
{
GsmInhibitor *inhibitor = user_data;
g_bus_unwatch_name (inhibitor->priv->watch_id);
inhibitor->priv->watch_id = 0;
g_signal_emit (inhibitor, signals[VANISHED], 0);
}
static void
gsm_inhibitor_set_bus_name (GsmInhibitor *inhibitor,
const char *bus_name)
{
g_return_if_fail (GSM_IS_INHIBITOR (inhibitor));
g_free (inhibitor->priv->bus_name);
if (bus_name != NULL) {
inhibitor->priv->bus_name = g_strdup (bus_name);
inhibitor->priv->watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
bus_name,
G_BUS_NAME_WATCHER_FLAGS_NONE,
NULL,
on_inhibitor_vanished,
inhibitor,
NULL);
} else {
inhibitor->priv->bus_name = g_strdup ("");
}
g_object_notify (G_OBJECT (inhibitor), "bus-name");
}
static void
gsm_inhibitor_set_app_id (GsmInhibitor *inhibitor,
const char *app_id)
{
g_return_if_fail (GSM_IS_INHIBITOR (inhibitor));
g_free (inhibitor->priv->app_id);
inhibitor->priv->app_id = g_strdup (app_id);
g_object_notify (G_OBJECT (inhibitor), "app-id");
}
static void
gsm_inhibitor_set_client_id (GsmInhibitor *inhibitor,
const char *client_id)
{
g_return_if_fail (GSM_IS_INHIBITOR (inhibitor));
g_free (inhibitor->priv->client_id);
g_debug ("GsmInhibitor: setting client-id = %s", client_id);
if (client_id != NULL) {
inhibitor->priv->client_id = g_strdup (client_id);
} else {
inhibitor->priv->client_id = g_strdup ("");
}
g_object_notify (G_OBJECT (inhibitor), "client-id");
}
static void
gsm_inhibitor_set_reason (GsmInhibitor *inhibitor,
const char *reason)
{
g_return_if_fail (GSM_IS_INHIBITOR (inhibitor));
g_free (inhibitor->priv->reason);
if (reason != NULL) {
inhibitor->priv->reason = g_strdup (reason);
} else {
inhibitor->priv->reason = g_strdup ("");
}
g_object_notify (G_OBJECT (inhibitor), "reason");
}
static void
gsm_inhibitor_set_cookie (GsmInhibitor *inhibitor,
guint cookie)
{
g_return_if_fail (GSM_IS_INHIBITOR (inhibitor));
if (inhibitor->priv->cookie != cookie) {
inhibitor->priv->cookie = cookie;
g_object_notify (G_OBJECT (inhibitor), "cookie");
}
}
static void
gsm_inhibitor_set_flags (GsmInhibitor *inhibitor,
guint flags)
{
g_return_if_fail (GSM_IS_INHIBITOR (inhibitor));
if (inhibitor->priv->flags != flags) {
inhibitor->priv->flags = flags;
g_object_notify (G_OBJECT (inhibitor), "flags");
}
}
static void
gsm_inhibitor_set_toplevel_xid (GsmInhibitor *inhibitor,
guint xid)
{
g_return_if_fail (GSM_IS_INHIBITOR (inhibitor));
if (inhibitor->priv->toplevel_xid != xid) {
inhibitor->priv->toplevel_xid = xid;
g_object_notify (G_OBJECT (inhibitor), "toplevel-xid");
}
}
const char *
gsm_inhibitor_peek_bus_name (GsmInhibitor *inhibitor)
{
g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), NULL);
return inhibitor->priv->bus_name;
}
const char *
gsm_inhibitor_peek_id (GsmInhibitor *inhibitor)
{
g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), NULL);
return inhibitor->priv->id;
}
const char *
gsm_inhibitor_peek_app_id (GsmInhibitor *inhibitor)
{
g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), NULL);
return inhibitor->priv->app_id;
}
const char *
gsm_inhibitor_peek_client_id (GsmInhibitor *inhibitor)
{
g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), NULL);
return inhibitor->priv->client_id;
}
const char *
gsm_inhibitor_peek_reason (GsmInhibitor *inhibitor)
{
g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), NULL);
return inhibitor->priv->reason;
}
guint
gsm_inhibitor_peek_flags (GsmInhibitor *inhibitor)
{
g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), 0);
return inhibitor->priv->flags;
}
guint
gsm_inhibitor_peek_toplevel_xid (GsmInhibitor *inhibitor)
{
g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), 0);
return inhibitor->priv->toplevel_xid;
}
guint
gsm_inhibitor_peek_cookie (GsmInhibitor *inhibitor)
{
g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), 0);
return inhibitor->priv->cookie;
}
static void
gsm_inhibitor_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GsmInhibitor *self;
self = GSM_INHIBITOR (object);
switch (prop_id) {
case PROP_BUS_NAME:
gsm_inhibitor_set_bus_name (self, g_value_get_string (value));
break;
case PROP_APP_ID:
gsm_inhibitor_set_app_id (self, g_value_get_string (value));
break;
case PROP_CLIENT_ID:
gsm_inhibitor_set_client_id (self, g_value_get_string (value));
break;
case PROP_REASON:
gsm_inhibitor_set_reason (self, g_value_get_string (value));
break;
case PROP_FLAGS:
gsm_inhibitor_set_flags (self, g_value_get_uint (value));
break;
case PROP_COOKIE:
gsm_inhibitor_set_cookie (self, g_value_get_uint (value));
break;
case PROP_TOPLEVEL_XID:
gsm_inhibitor_set_toplevel_xid (self, g_value_get_uint (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gsm_inhibitor_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GsmInhibitor *self;
self = GSM_INHIBITOR (object);
switch (prop_id) {
case PROP_BUS_NAME:
g_value_set_string (value, self->priv->bus_name);
break;
case PROP_APP_ID:
g_value_set_string (value, self->priv->app_id);
break;
case PROP_CLIENT_ID:
g_value_set_string (value, self->priv->client_id);
break;
case PROP_REASON:
g_value_set_string (value, self->priv->reason);
break;
case PROP_FLAGS:
g_value_set_uint (value, self->priv->flags);
break;
case PROP_COOKIE:
g_value_set_uint (value, self->priv->cookie);
break;
case PROP_TOPLEVEL_XID:
g_value_set_uint (value, self->priv->toplevel_xid);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gsm_inhibitor_finalize (GObject *object)
{
GsmInhibitor *inhibitor = (GsmInhibitor *) object;
g_free (inhibitor->priv->id);
g_free (inhibitor->priv->bus_name);
g_free (inhibitor->priv->app_id);
g_free (inhibitor->priv->client_id);
g_free (inhibitor->priv->reason);
if (inhibitor->priv->skeleton != NULL) {
g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (inhibitor->priv->skeleton),
inhibitor->priv->connection);
g_clear_object (&inhibitor->priv->skeleton);
}
if (inhibitor->priv->watch_id != 0) {
g_bus_unwatch_name (inhibitor->priv->watch_id);
}
g_clear_object (&inhibitor->priv->connection);
G_OBJECT_CLASS (gsm_inhibitor_parent_class)->finalize (object);
}
static void
gsm_inhibitor_class_init (GsmInhibitorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gsm_inhibitor_finalize;
object_class->constructor = gsm_inhibitor_constructor;
object_class->get_property = gsm_inhibitor_get_property;
object_class->set_property = gsm_inhibitor_set_property;
signals[VANISHED] =
g_signal_new ("vanished",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL,
G_TYPE_NONE,
0);
g_object_class_install_property (object_class,
PROP_BUS_NAME,
g_param_spec_string ("bus-name",
"bus-name",
"bus-name",
"",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_APP_ID,
g_param_spec_string ("app-id",
"app-id",
"app-id",
"",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_CLIENT_ID,
g_param_spec_string ("client-id",
"client-id",
"client-id",
"",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_REASON,
g_param_spec_string ("reason",
"reason",
"reason",
"",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_FLAGS,
g_param_spec_uint ("flags",
"flags",
"flags",
0,
G_MAXINT,
0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_TOPLEVEL_XID,
g_param_spec_uint ("toplevel-xid",
"toplevel-xid",
"toplevel-xid",
0,
G_MAXINT,
0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_COOKIE,
g_param_spec_uint ("cookie",
"cookie",
"cookie",
0,
G_MAXINT,
0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_type_class_add_private (klass, sizeof (GsmInhibitorPrivate));
}
GsmInhibitor *
gsm_inhibitor_new (const char *app_id,
guint toplevel_xid,
guint flags,
const char *reason,
const char *bus_name,
guint cookie)
{
GsmInhibitor *inhibitor;
inhibitor = g_object_new (GSM_TYPE_INHIBITOR,
"app-id", app_id,
"reason", reason,
"bus-name", bus_name,
"flags", flags,
"toplevel-xid", toplevel_xid,
"cookie", cookie,
NULL);
return inhibitor;
}
GsmInhibitor *
gsm_inhibitor_new_for_client (const char *client_id,
const char *app_id,
guint flags,
const char *reason,
const char *bus_name,
guint cookie)
{
GsmInhibitor *inhibitor;
inhibitor = g_object_new (GSM_TYPE_INHIBITOR,
"client-id", client_id,
"app-id", app_id,
"reason", reason,
"bus-name", bus_name,
"flags", flags,
"cookie", cookie,
NULL);
return inhibitor;
}

View file

@ -0,0 +1,88 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2008 Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GSM_INHIBITOR_H__
#define __GSM_INHIBITOR_H__
#include <glib-object.h>
#include <sys/types.h>
#include "gsm-inhibitor-flag.h"
G_BEGIN_DECLS
#define GSM_TYPE_INHIBITOR (gsm_inhibitor_get_type ())
#define GSM_INHIBITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_INHIBITOR, GsmInhibitor))
#define GSM_INHIBITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_INHIBITOR, GsmInhibitorClass))
#define GSM_IS_INHIBITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_INHIBITOR))
#define GSM_IS_INHIBITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_INHIBITOR))
#define GSM_INHIBITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_INHIBITOR, GsmInhibitorClass))
typedef struct _GsmInhibitor GsmInhibitor;
typedef struct _GsmInhibitorClass GsmInhibitorClass;
typedef struct GsmInhibitorPrivate GsmInhibitorPrivate;
struct _GsmInhibitor
{
GObject parent;
GsmInhibitorPrivate *priv;
};
struct _GsmInhibitorClass
{
GObjectClass parent_class;
};
typedef enum
{
GSM_INHIBITOR_ERROR_GENERAL = 0,
GSM_INHIBITOR_ERROR_NOT_SET,
GSM_INHIBITOR_NUM_ERRORS
} GsmInhibitorError;
#define GSM_INHIBITOR_ERROR gsm_inhibitor_error_quark ()
GQuark gsm_inhibitor_error_quark (void);
GType gsm_inhibitor_get_type (void) G_GNUC_CONST;
GsmInhibitor * gsm_inhibitor_new (const char *app_id,
guint toplevel_xid,
guint flags,
const char *reason,
const char *bus_name,
guint cookie);
GsmInhibitor * gsm_inhibitor_new_for_client (const char *client_id,
const char *app_id,
guint flags,
const char *reason,
const char *bus_name,
guint cookie);
const char * gsm_inhibitor_peek_id (GsmInhibitor *inhibitor);
const char * gsm_inhibitor_peek_app_id (GsmInhibitor *inhibitor);
const char * gsm_inhibitor_peek_client_id (GsmInhibitor *inhibitor);
const char * gsm_inhibitor_peek_reason (GsmInhibitor *inhibitor);
const char * gsm_inhibitor_peek_bus_name (GsmInhibitor *inhibitor);
guint gsm_inhibitor_peek_cookie (GsmInhibitor *inhibitor);
guint gsm_inhibitor_peek_flags (GsmInhibitor *inhibitor);
guint gsm_inhibitor_peek_toplevel_xid (GsmInhibitor *inhibitor);
G_END_DECLS
#endif /* __GSM_INHIBITOR_H__ */

View file

@ -0,0 +1,34 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2008 William Jon McCann <jmccann@redhat.com>
*
* 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef __GSM_MANAGER_LOGOUT_MODE_H
#define __GSM_MANAGER_LOGOUT_MODE_H
G_BEGIN_DECLS
typedef enum {
GSM_MANAGER_LOGOUT_MODE_NORMAL = 0,
GSM_MANAGER_LOGOUT_MODE_NO_CONFIRMATION,
GSM_MANAGER_LOGOUT_MODE_FORCE
} GsmManagerLogoutMode;
G_END_DECLS
#endif /* __GSM_MANAGER_LOGOUT_MODE_H */

3941
gnome-session/gsm-manager.c Normal file

File diff suppressed because it is too large Load diff

123
gnome-session/gsm-manager.h Normal file
View file

@ -0,0 +1,123 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2008 William Jon McCann <jmccann@redhat.com>
*
* 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef __GSM_MANAGER_H
#define __GSM_MANAGER_H
#include <glib-object.h>
#include "gsm-store.h"
#include "gsm-manager-logout-mode.h"
G_BEGIN_DECLS
#define GSM_TYPE_MANAGER (gsm_manager_get_type ())
G_DECLARE_DERIVABLE_TYPE (GsmManager, gsm_manager, GSM, MANAGER, GObject)
struct _GsmManagerClass
{
GObjectClass parent_class;
void (* phase_changed) (GsmManager *manager,
const char *phase);
};
typedef enum {
/* gsm's own startup/initialization phase */
GSM_MANAGER_PHASE_STARTUP = 0,
/* gnome-initial-setup */
GSM_MANAGER_PHASE_EARLY_INITIALIZATION,
/* gnome-keyring-daemon */
GSM_MANAGER_PHASE_PRE_DISPLAY_SERVER,
/* wayland compositor and XWayland */
GSM_MANAGER_PHASE_DISPLAY_SERVER,
/* xrandr setup, gnome-settings-daemon, etc */
GSM_MANAGER_PHASE_INITIALIZATION,
/* window/compositing managers */
GSM_MANAGER_PHASE_WINDOW_MANAGER,
/* apps that will create _NET_WM_WINDOW_TYPE_PANEL windows */
GSM_MANAGER_PHASE_PANEL,
/* apps that will create _NET_WM_WINDOW_TYPE_DESKTOP windows */
GSM_MANAGER_PHASE_DESKTOP,
/* everything else */
GSM_MANAGER_PHASE_APPLICATION,
/* done launching */
GSM_MANAGER_PHASE_RUNNING,
/* shutting down */
GSM_MANAGER_PHASE_QUERY_END_SESSION,
GSM_MANAGER_PHASE_END_SESSION,
GSM_MANAGER_PHASE_EXIT
} GsmManagerPhase;
typedef enum
{
GSM_MANAGER_ERROR_GENERAL = 0,
GSM_MANAGER_ERROR_NOT_IN_INITIALIZATION,
GSM_MANAGER_ERROR_NOT_IN_RUNNING,
GSM_MANAGER_ERROR_ALREADY_REGISTERED,
GSM_MANAGER_ERROR_NOT_REGISTERED,
GSM_MANAGER_ERROR_INVALID_OPTION,
GSM_MANAGER_ERROR_LOCKED_DOWN,
GSM_MANAGER_NUM_ERRORS
} GsmManagerError;
#define GSM_MANAGER_ERROR gsm_manager_error_quark ()
GQuark gsm_manager_error_quark (void);
GsmManager * gsm_manager_new (GsmStore *client_store,
gboolean failsafe,
gboolean systemd_managed);
GsmManager * gsm_manager_get (void);
gboolean gsm_manager_get_failsafe (GsmManager *manager);
gboolean gsm_manager_get_dbus_disconnected (GsmManager *manager);
gboolean gsm_manager_get_systemd_managed (GsmManager *manager);
gboolean gsm_manager_add_autostart_app (GsmManager *manager,
const char *path);
gboolean gsm_manager_add_required_app (GsmManager *manager,
const char *path);
gboolean gsm_manager_add_autostart_apps_from_dir (GsmManager *manager,
const char *path);
gboolean gsm_manager_add_legacy_session_apps (GsmManager *manager,
const char *path);
void gsm_manager_start (GsmManager *manager);
char * _gsm_manager_get_default_session (GsmManager *manager);
void _gsm_manager_set_active_session (GsmManager *manager,
const char *session_name,
gboolean is_fallback);
void _gsm_manager_set_renderer (GsmManager *manager,
const char *renderer);
gboolean gsm_manager_logout (GsmManager *manager,
guint logout_mode,
GError **error);
gboolean gsm_manager_set_phase (GsmManager *manager,
GsmManagerPhase phase);
G_END_DECLS
#endif /* __GSM_MANAGER_H */

View file

@ -0,0 +1,33 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2009 Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GSM_PRESENCE_FLAG_H__
#define __GSM_PRESENCE_FLAG_H__
G_BEGIN_DECLS
typedef enum {
GSM_PRESENCE_STATUS_AVAILABLE = 0,
GSM_PRESENCE_STATUS_INVISIBLE,
GSM_PRESENCE_STATUS_BUSY,
GSM_PRESENCE_STATUS_IDLE,
} GsmPresenceStatus;
G_END_DECLS
#endif /* __GSM_PRESENCE_FLAG_H__ */

View file

@ -0,0 +1,542 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2009 Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define GNOME_DESKTOP_USE_UNSTABLE_API
#include <libgnome-desktop/gnome-idle-monitor.h>
#include "gsm-presence.h"
#include "org.gnome.SessionManager.Presence.h"
#define GSM_PRESENCE_DBUS_IFACE "org.gnome.SessionManager.Presence"
#define GSM_PRESENCE_DBUS_PATH "/org/gnome/SessionManager/Presence"
#define GS_NAME "org.gnome.ScreenSaver"
#define GS_PATH "/org/gnome/ScreenSaver"
#define GS_INTERFACE "org.gnome.ScreenSaver"
#define MAX_STATUS_TEXT 140
#define GSM_PRESENCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_PRESENCE, GsmPresencePrivate))
struct GsmPresencePrivate
{
guint status;
guint saved_status;
char *status_text;
gboolean idle_enabled;
GnomeIdleMonitor *idle_monitor;
guint idle_watch_id;
guint idle_timeout;
gboolean screensaver_active;
GDBusConnection *connection;
GDBusProxy *screensaver_proxy;
GsmExportedPresence *skeleton;
};
enum {
PROP_0,
PROP_IDLE_ENABLED,
PROP_IDLE_TIMEOUT,
};
enum {
STATUS_CHANGED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0, };
G_DEFINE_TYPE (GsmPresence, gsm_presence, G_TYPE_OBJECT)
static const GDBusErrorEntry gsm_presence_error_entries[] = {
{ GSM_PRESENCE_ERROR_GENERAL, GSM_PRESENCE_DBUS_IFACE ".GeneralError" }
};
GQuark
gsm_presence_error_quark (void)
{
static volatile gsize quark_volatile = 0;
g_dbus_error_register_error_domain ("gsm_presence_error",
&quark_volatile,
gsm_presence_error_entries,
G_N_ELEMENTS (gsm_presence_error_entries));
return quark_volatile;
}
static void idle_became_active_cb (GnomeIdleMonitor *idle_monitor,
guint id,
gpointer user_data);
static void
gsm_presence_set_status (GsmPresence *presence,
guint status)
{
if (status != presence->priv->status) {
presence->priv->status = status;
gsm_exported_presence_set_status (presence->priv->skeleton, status);
gsm_exported_presence_emit_status_changed (presence->priv->skeleton, presence->priv->status);
g_signal_emit (presence, signals[STATUS_CHANGED], 0, presence->priv->status);
}
}
static gboolean
gsm_presence_set_status_text (GsmPresence *presence,
const char *status_text,
GError **error)
{
g_return_val_if_fail (GSM_IS_PRESENCE (presence), FALSE);
g_free (presence->priv->status_text);
presence->priv->status_text = NULL;
/* check length */
if (status_text != NULL && strlen (status_text) > MAX_STATUS_TEXT) {
g_set_error (error,
GSM_PRESENCE_ERROR,
GSM_PRESENCE_ERROR_GENERAL,
"Status text too long");
return FALSE;
}
if (status_text != NULL) {
presence->priv->status_text = g_strdup (status_text);
} else {
presence->priv->status_text = g_strdup ("");
}
gsm_exported_presence_set_status_text (presence->priv->skeleton, presence->priv->status_text);
gsm_exported_presence_emit_status_text_changed (presence->priv->skeleton, presence->priv->status_text);
return TRUE;
}
static void
set_session_idle (GsmPresence *presence,
gboolean is_idle)
{
g_debug ("GsmPresence: setting idle: %d", is_idle);
if (is_idle) {
if (presence->priv->status == GSM_PRESENCE_STATUS_IDLE) {
g_debug ("GsmPresence: already idle, ignoring");
return;
}
/* save current status */
presence->priv->saved_status = presence->priv->status;
gsm_presence_set_status (presence, GSM_PRESENCE_STATUS_IDLE);
gnome_idle_monitor_add_user_active_watch (presence->priv->idle_monitor,
idle_became_active_cb,
presence,
NULL);
} else {
if (presence->priv->status != GSM_PRESENCE_STATUS_IDLE) {
g_debug ("GsmPresence: already not idle, ignoring");
return;
}
/* restore saved status */
gsm_presence_set_status (presence, presence->priv->saved_status);
g_debug ("GsmPresence: setting non-idle status %d", presence->priv->saved_status);
presence->priv->saved_status = GSM_PRESENCE_STATUS_AVAILABLE;
}
}
static void
idle_became_idle_cb (GnomeIdleMonitor *idle_monitor,
guint id,
gpointer user_data)
{
GsmPresence *presence = user_data;
set_session_idle (presence, TRUE);
}
static void
idle_became_active_cb (GnomeIdleMonitor *idle_monitor,
guint id,
gpointer user_data)
{
GsmPresence *presence = user_data;
set_session_idle (presence, FALSE);
}
static void
reset_idle_watch (GsmPresence *presence)
{
if (presence->priv->idle_watch_id > 0) {
g_debug ("GsmPresence: removing idle watch (%i)", presence->priv->idle_watch_id);
gnome_idle_monitor_remove_watch (presence->priv->idle_monitor,
presence->priv->idle_watch_id);
presence->priv->idle_watch_id = 0;
}
if (presence->priv->idle_enabled
&& presence->priv->idle_timeout > 0) {
presence->priv->idle_watch_id = gnome_idle_monitor_add_idle_watch (presence->priv->idle_monitor,
presence->priv->idle_timeout,
idle_became_idle_cb,
presence,
NULL);
g_debug ("GsmPresence: adding idle watch (%i) for %d secs",
presence->priv->idle_watch_id,
presence->priv->idle_timeout / 1000);
}
}
static void
on_screensaver_dbus_signal (GDBusProxy *proxy,
gchar *sender_name,
gchar *signal_name,
GVariant *parameters,
GsmPresence *presence)
{
gboolean is_active;
if (g_strcmp0 (signal_name, "ActiveChanged") != 0) {
return;
}
g_variant_get (parameters, "(b)", &is_active);
if (presence->priv->screensaver_active != is_active) {
presence->priv->screensaver_active = is_active;
set_session_idle (presence, is_active);
}
}
static void
screensaver_get_active_cb (GDBusProxy *screensaver_proxy,
GAsyncResult *res,
GsmPresence *presence)
{
g_autoptr(GVariant) data = NULL;
g_autoptr(GError) error = NULL;
gboolean is_active;
data = g_dbus_proxy_call_finish (screensaver_proxy, res, &error);
if (!data) {
if (error) {
g_warning ("Could not retrieve current screensaver active state: %s",
error->message);
} else {
g_warning ("Could not retrieve current screensaver active state!");
}
return;
}
g_variant_get (data, "(b)", &is_active);
if (presence->priv->screensaver_active != is_active) {
presence->priv->screensaver_active = is_active;
set_session_idle (presence, is_active);
}
}
static void
on_screensaver_name_owner_changed (GDBusProxy *screensaver_proxy,
GParamSpec *pspec,
GsmPresence *presence)
{
gchar *name_owner;
name_owner = g_dbus_proxy_get_name_owner (screensaver_proxy);
if (name_owner == NULL) {
g_debug ("Detected that screensaver has left the bus");
presence->priv->screensaver_active = FALSE;
set_session_idle (presence, FALSE);
} else {
g_debug ("Detected that screensaver has aquired the bus");
g_dbus_proxy_call (presence->priv->screensaver_proxy,
"GetActive",
NULL,
G_DBUS_CALL_FLAGS_NO_AUTO_START,
1000,
NULL,
(GAsyncReadyCallback) screensaver_get_active_cb,
presence);
}
g_free (name_owner);
}
static gboolean
gsm_presence_set_status_text_dbus (GsmExportedPresence *skeleton,
GDBusMethodInvocation *invocation,
gchar *status_text,
GsmPresence *presence)
{
GError *error = NULL;
if (gsm_presence_set_status_text (presence, status_text, &error)) {
gsm_exported_presence_complete_set_status_text (skeleton, invocation);
} else {
g_dbus_method_invocation_take_error (invocation, error);
}
return TRUE;
}
static gboolean
gsm_presence_set_status_dbus (GsmExportedPresence *skeleton,
GDBusMethodInvocation *invocation,
guint status,
GsmPresence *presence)
{
gsm_presence_set_status (presence, status);
gsm_exported_presence_complete_set_status (skeleton, invocation);
return TRUE;
}
static gboolean
register_presence (GsmPresence *presence)
{
GError *error;
GsmExportedPresence *skeleton;
error = NULL;
presence->priv->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
if (error != NULL) {
g_critical ("error getting session bus: %s", error->message);
g_error_free (error);
return FALSE;
}
skeleton = gsm_exported_presence_skeleton_new ();
presence->priv->skeleton = skeleton;
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (skeleton),
presence->priv->connection,
GSM_PRESENCE_DBUS_PATH, &error);
if (error != NULL) {
g_critical ("error registering presence object on session bus: %s", error->message);
g_error_free (error);
return FALSE;
}
g_signal_connect (skeleton, "handle-set-status",
G_CALLBACK (gsm_presence_set_status_dbus), presence);
g_signal_connect (skeleton, "handle-set-status-text",
G_CALLBACK (gsm_presence_set_status_text_dbus), presence);
return TRUE;
}
static GObject *
gsm_presence_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
GsmPresence *presence;
gboolean res;
GError *error = NULL;
presence = GSM_PRESENCE (G_OBJECT_CLASS (gsm_presence_parent_class)->constructor (type,
n_construct_properties,
construct_properties));
res = register_presence (presence);
if (! res) {
g_warning ("Unable to register presence with session bus");
}
/* This only connects to signals and resolves the current name owner
* synchronously. It is important to not auto-start the service!
*/
presence->priv->screensaver_proxy = g_dbus_proxy_new_sync (presence->priv->connection,
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
NULL,
GS_NAME,
GS_PATH,
GS_INTERFACE,
NULL, &error);
if (error != NULL) {
g_critical ("Unable to create a DBus proxy for GnomeScreensaver: %s",
error->message);
g_error_free (error);
} else {
g_signal_connect (presence->priv->screensaver_proxy, "notify::g-name-owner",
G_CALLBACK (on_screensaver_name_owner_changed), presence);
g_signal_connect (presence->priv->screensaver_proxy, "g-signal",
G_CALLBACK (on_screensaver_dbus_signal), presence);
}
return G_OBJECT (presence);
}
static void
gsm_presence_init (GsmPresence *presence)
{
presence->priv = GSM_PRESENCE_GET_PRIVATE (presence);
presence->priv->idle_monitor = gnome_idle_monitor_new ();
}
void
gsm_presence_set_idle_enabled (GsmPresence *presence,
gboolean enabled)
{
g_return_if_fail (GSM_IS_PRESENCE (presence));
if (presence->priv->idle_enabled != enabled) {
presence->priv->idle_enabled = enabled;
reset_idle_watch (presence);
g_object_notify (G_OBJECT (presence), "idle-enabled");
}
}
void
gsm_presence_set_idle_timeout (GsmPresence *presence,
guint timeout)
{
g_return_if_fail (GSM_IS_PRESENCE (presence));
if (timeout != presence->priv->idle_timeout) {
presence->priv->idle_timeout = timeout;
reset_idle_watch (presence);
g_object_notify (G_OBJECT (presence), "idle-timeout");
}
}
static void
gsm_presence_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GsmPresence *self;
self = GSM_PRESENCE (object);
switch (prop_id) {
case PROP_IDLE_ENABLED:
gsm_presence_set_idle_enabled (self, g_value_get_boolean (value));
break;
case PROP_IDLE_TIMEOUT:
gsm_presence_set_idle_timeout (self, g_value_get_uint (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gsm_presence_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GsmPresence *self;
self = GSM_PRESENCE (object);
switch (prop_id) {
case PROP_IDLE_ENABLED:
g_value_set_boolean (value, self->priv->idle_enabled);
break;
case PROP_IDLE_TIMEOUT:
g_value_set_uint (value, self->priv->idle_timeout);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gsm_presence_finalize (GObject *object)
{
GsmPresence *presence = (GsmPresence *) object;
if (presence->priv->idle_watch_id > 0) {
gnome_idle_monitor_remove_watch (presence->priv->idle_monitor,
presence->priv->idle_watch_id);
presence->priv->idle_watch_id = 0;
}
g_clear_pointer (&presence->priv->status_text, g_free);
g_clear_object (&presence->priv->idle_monitor);
g_clear_object (&presence->priv->screensaver_proxy);
G_OBJECT_CLASS (gsm_presence_parent_class)->finalize (object);
}
static void
gsm_presence_class_init (GsmPresenceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gsm_presence_finalize;
object_class->constructor = gsm_presence_constructor;
object_class->get_property = gsm_presence_get_property;
object_class->set_property = gsm_presence_set_property;
signals [STATUS_CHANGED] =
g_signal_new ("status-changed",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GsmPresenceClass, status_changed),
NULL,
NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE,
1, G_TYPE_UINT);
g_object_class_install_property (object_class,
PROP_IDLE_ENABLED,
g_param_spec_boolean ("idle-enabled",
NULL,
NULL,
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_IDLE_TIMEOUT,
g_param_spec_uint ("idle-timeout",
"idle timeout",
"idle timeout",
0,
G_MAXINT,
120000,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_type_class_add_private (klass, sizeof (GsmPresencePrivate));
}
GsmPresence *
gsm_presence_new (void)
{
GsmPresence *presence;
presence = g_object_new (GSM_TYPE_PRESENCE,
NULL);
return presence;
}

View file

@ -0,0 +1,75 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2009 Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GSM_PRESENCE_H__
#define __GSM_PRESENCE_H__
#include <glib-object.h>
#include <sys/types.h>
#include "gsm-presence-flag.h"
G_BEGIN_DECLS
#define GSM_TYPE_PRESENCE (gsm_presence_get_type ())
#define GSM_PRESENCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_PRESENCE, GsmPresence))
#define GSM_PRESENCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_PRESENCE, GsmPresenceClass))
#define GSM_IS_PRESENCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_PRESENCE))
#define GSM_IS_PRESENCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_PRESENCE))
#define GSM_PRESENCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_PRESENCE, GsmPresenceClass))
typedef struct _GsmPresence GsmPresence;
typedef struct _GsmPresenceClass GsmPresenceClass;
typedef struct GsmPresencePrivate GsmPresencePrivate;
struct _GsmPresence
{
GObject parent;
GsmPresencePrivate *priv;
};
struct _GsmPresenceClass
{
GObjectClass parent_class;
void (* status_changed) (GsmPresence *presence,
guint status);
};
typedef enum
{
GSM_PRESENCE_ERROR_GENERAL = 0,
GSM_PRESENCE_NUM_ERRORS
} GsmPresenceError;
#define GSM_PRESENCE_ERROR gsm_presence_error_quark ()
GQuark gsm_presence_error_quark (void);
GType gsm_presence_get_type (void) G_GNUC_CONST;
GsmPresence * gsm_presence_new (void);
void gsm_presence_set_idle_enabled (GsmPresence *presence,
gboolean enabled);
void gsm_presence_set_idle_timeout (GsmPresence *presence,
guint n_seconds);
G_END_DECLS
#endif /* __GSM_PRESENCE_H__ */

View file

@ -0,0 +1,113 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2010 Novell, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <gio/gio.h>
#include <glib/gi18n.h>
#include "gsm-process-helper.h"
typedef struct {
gboolean done;
GSubprocess *process;
gboolean caught_error;
GError **error;
GMainContext *maincontext;
GSource *timeout_source;
} GsmProcessHelper;
static void
on_child_exited (GObject *source,
GAsyncResult *result,
gpointer data)
{
GsmProcessHelper *helper = data;
helper->done = TRUE;
if (!g_subprocess_wait_check_finish ((GSubprocess*)source, result,
helper->caught_error ? NULL : helper->error))
helper->caught_error = TRUE;
g_clear_pointer (&helper->timeout_source, g_source_destroy);
g_main_context_wakeup (helper->maincontext);
}
static gboolean
on_child_timeout (gpointer data)
{
GsmProcessHelper *helper = data;
g_assert (!helper->done);
g_subprocess_force_exit (helper->process);
g_set_error_literal (helper->error,
G_IO_CHANNEL_ERROR,
G_IO_CHANNEL_ERROR_FAILED,
"Timed out");
helper->timeout_source = NULL;
return FALSE;
}
gboolean
gsm_process_helper (const char *command_line,
unsigned int timeout,
GError **error)
{
gboolean ret = FALSE;
GsmProcessHelper helper = { 0, };
gchar **argv = NULL;
GMainContext *subcontext = NULL;
if (!g_shell_parse_argv (command_line, NULL, &argv, error))
goto out;
helper.error = error;
subcontext = g_main_context_new ();
g_main_context_push_thread_default (subcontext);
helper.process = g_subprocess_newv ((const char*const*)argv, 0, error);
if (!helper.process)
goto out;
g_subprocess_wait_async (helper.process, NULL, on_child_exited, &helper);
helper.timeout_source = g_timeout_source_new (timeout);
g_source_set_callback (helper.timeout_source, on_child_timeout, &helper, NULL);
g_source_attach (helper.timeout_source, subcontext);
while (!helper.done)
g_main_context_iteration (subcontext, TRUE);
ret = helper.caught_error;
out:
g_strfreev (argv);
if (subcontext) {
g_main_context_pop_thread_default (subcontext);
g_main_context_unref (subcontext);
}
g_clear_object (&helper.process);
return ret;
}

View file

@ -0,0 +1,32 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2010 Novell, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GSM_PROCESS_HELPER_H
#define __GSM_PROCESS_HELPER_H
#include <glib.h>
G_BEGIN_DECLS
int gsm_process_helper (const char *command_line,
unsigned int timeout,
GError **error);
G_END_DECLS
#endif /* __GSM_PROCESS_HELPER_H */

View file

@ -0,0 +1,334 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2006, 2010 Novell, Inc.
* Copyright (C) 2008 Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include "gsm-session-fill.h"
#include "gsm-system.h"
#include "gsm-manager.h"
#include "gsm-process-helper.h"
#include "gsm-util.h"
#define GSM_KEYFILE_SESSION_GROUP "GNOME Session"
#define GSM_KEYFILE_RUNNABLE_KEY "IsRunnableHelper"
#define GSM_KEYFILE_FALLBACK_KEY "FallbackSession"
#define GSM_KEYFILE_REQUIRED_COMPONENTS_KEY "RequiredComponents"
/* See https://bugzilla.gnome.org/show_bug.cgi?id=641992 for discussion */
#define GSM_RUNNABLE_HELPER_TIMEOUT 3000 /* ms */
typedef void (*GsmFillHandleComponent) (const char *component,
const char *app_path,
gpointer user_data);
static void
handle_required_components (GKeyFile *keyfile,
gboolean look_in_saved_session,
GsmFillHandleComponent callback,
gpointer user_data)
{
char **required_components;
int i;
g_assert (keyfile != NULL);
g_assert (callback != NULL);
required_components = g_key_file_get_string_list (keyfile,
GSM_KEYFILE_SESSION_GROUP,
GSM_KEYFILE_REQUIRED_COMPONENTS_KEY,
NULL, NULL);
if (!required_components)
return;
for (i = 0; required_components[i] != NULL; i++) {
char *app_path;
app_path = gsm_util_find_desktop_file_for_app_name (required_components[i],
look_in_saved_session, TRUE);
callback (required_components[i], app_path, user_data);
g_free (app_path);
}
g_strfreev (required_components);
}
static void
check_required_components_helper (const char *component,
const char *app_path,
gpointer user_data)
{
gboolean *error = user_data;
if (app_path == NULL) {
g_warning ("Unable to find required component '%s'", component);
*error = TRUE;
}
}
static gboolean
check_required (GKeyFile *keyfile)
{
gboolean error = FALSE;
g_debug ("fill: *** Checking required components");
handle_required_components (keyfile, FALSE,
check_required_components_helper, &error);
g_debug ("fill: *** Done checking required components");
return !error;
}
static void
maybe_load_saved_session_apps (GsmManager *manager)
{
GsmSystem *system;
gboolean is_login;
system = gsm_get_system ();
is_login = gsm_system_is_login_session (system);
g_object_unref (system);
if (is_login)
return;
gsm_manager_add_autostart_apps_from_dir (manager, gsm_util_get_saved_session_dir ());
}
static void
append_required_components_helper (const char *component,
const char *app_path,
gpointer user_data)
{
GsmManager *manager = user_data;
if (app_path == NULL)
g_warning ("Unable to find required component '%s'", component);
else
gsm_manager_add_required_app (manager, app_path);
}
static void
load_standard_apps (GsmManager *manager,
GKeyFile *keyfile)
{
/* Note that saving/restoring sessions is not really possible on systemd, as
* XSMP clients cannot be reliably mapped to .desktop files. */
g_debug ("fill: *** Adding required components");
handle_required_components (keyfile,
!gsm_manager_get_failsafe (manager) && !gsm_manager_get_systemd_managed (manager),
append_required_components_helper, manager);
g_debug ("fill: *** Done adding required components");
if (!gsm_manager_get_failsafe (manager)) {
char **autostart_dirs;
int i;
autostart_dirs = gsm_util_get_autostart_dirs ();
if (!gsm_manager_get_systemd_managed (manager))
maybe_load_saved_session_apps (manager);
for (i = 0; autostart_dirs[i]; i++) {
gsm_manager_add_autostart_apps_from_dir (manager,
autostart_dirs[i]);
}
g_strfreev (autostart_dirs);
}
}
static GKeyFile *
get_session_keyfile_if_valid (const char *path)
{
GKeyFile *keyfile;
gsize len;
char **list;
g_debug ("fill: *** Looking if %s is a valid session file", path);
keyfile = g_key_file_new ();
if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) {
g_debug ("Cannot use session '%s': non-existing or invalid file.", path);
goto error;
}
if (!g_key_file_has_group (keyfile, GSM_KEYFILE_SESSION_GROUP)) {
g_warning ("Cannot use session '%s': no '%s' group.", path, GSM_KEYFILE_SESSION_GROUP);
goto error;
}
list = g_key_file_get_string_list (keyfile,
GSM_KEYFILE_SESSION_GROUP,
GSM_KEYFILE_REQUIRED_COMPONENTS_KEY,
&len, NULL);
if (list)
g_strfreev (list);
if (len == 0)
g_warning ("Session '%s': no component in the session.", path);
return keyfile;
error:
g_key_file_free (keyfile);
return NULL;
}
/**
* find_valid_session_keyfile:
* @session: name of session
*
* We look for the session file in XDG_CONFIG_HOME, XDG_CONFIG_DIRS and
* XDG_DATA_DIRS. This enables users and sysadmins to override a specific
* session that is shipped in XDG_DATA_DIRS.
*/
static GKeyFile *
find_valid_session_keyfile (const char *session)
{
GPtrArray *dirs;
const char * const *system_config_dirs;
const char * const *system_data_dirs;
int i;
GKeyFile *keyfile;
char *basename;
dirs = g_ptr_array_new ();
g_ptr_array_add (dirs, (gpointer) g_get_user_config_dir ());
system_config_dirs = g_get_system_config_dirs ();
for (i = 0; system_config_dirs[i]; i++)
g_ptr_array_add (dirs, (gpointer) system_config_dirs[i]);
system_data_dirs = g_get_system_data_dirs ();
for (i = 0; system_data_dirs[i]; i++)
g_ptr_array_add (dirs, (gpointer) system_data_dirs[i]);
keyfile = NULL;
basename = g_strdup_printf ("%s.session", session);
for (i = 0; i < dirs->len; i++) {
g_autofree gchar *path = g_build_filename (dirs->pdata[i], "gnome-session", "sessions", basename, NULL);
keyfile = get_session_keyfile_if_valid (path);
if (keyfile != NULL)
break;
}
if (dirs)
g_ptr_array_free (dirs, TRUE);
if (basename)
g_free (basename);
return keyfile;
}
static GKeyFile *
get_session_keyfile (const char *session,
char **actual_session,
gboolean *is_fallback)
{
GKeyFile *keyfile;
gboolean session_runnable;
char *value;
GError *error = NULL;
*actual_session = NULL;
g_debug ("fill: *** Getting session '%s'", session);
keyfile = find_valid_session_keyfile (session);
if (!keyfile)
return NULL;
session_runnable = TRUE;
value = g_key_file_get_string (keyfile,
GSM_KEYFILE_SESSION_GROUP, GSM_KEYFILE_RUNNABLE_KEY,
NULL);
if (!IS_STRING_EMPTY (value)) {
g_debug ("fill: *** Launching helper '%s' to know if session is runnable", value);
session_runnable = gsm_process_helper (value, GSM_RUNNABLE_HELPER_TIMEOUT, &error);
if (!session_runnable) {
g_warning ("Session '%s' runnable check failed: %s", session,
error->message);
g_clear_error (&error);
}
}
g_free (value);
if (session_runnable) {
session_runnable = check_required (keyfile);
}
if (session_runnable) {
*actual_session = g_strdup (session);
if (is_fallback)
*is_fallback = FALSE;
return keyfile;
}
g_debug ("fill: *** Session is not runnable");
/* We can't run this session, so try to use the fallback */
value = g_key_file_get_string (keyfile,
GSM_KEYFILE_SESSION_GROUP, GSM_KEYFILE_FALLBACK_KEY,
NULL);
g_key_file_free (keyfile);
keyfile = NULL;
if (!IS_STRING_EMPTY (value)) {
if (is_fallback)
*is_fallback = TRUE;
keyfile = get_session_keyfile (value, actual_session, NULL);
}
g_free (value);
return keyfile;
}
gboolean
gsm_session_fill (GsmManager *manager,
const char *session)
{
GKeyFile *keyfile;
gboolean is_fallback;
char *actual_session;
keyfile = get_session_keyfile (session, &actual_session, &is_fallback);
if (!keyfile)
return FALSE;
_gsm_manager_set_active_session (manager, actual_session, is_fallback);
g_free (actual_session);
load_standard_apps (manager, keyfile);
g_key_file_free (keyfile);
return TRUE;
}

View file

@ -0,0 +1,32 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2006, 2010 Novell, Inc.
* Copyright (C) 2008 Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GSM_SESSION_FILL_H
#define __GSM_SESSION_FILL_H
#include "gsm-manager.h"
G_BEGIN_DECLS
gboolean gsm_session_fill (GsmManager *manager,
const char *session);
G_END_DECLS
#endif /* __GSM_SESSION_FILL_H */

View file

@ -0,0 +1,293 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
* gsm-session-save.c
* Copyright (C) 2008 Lucas Rocha.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include "gsm-app.h"
#include "gsm-util.h"
#include "gsm-autostart-app.h"
#include "gsm-client.h"
#include "gsm-session-save.h"
#define GSM_MANAGER_SCHEMA "org.gnome.SessionManager"
#define KEY_AUTOSAVE_ONE_SHOT "auto-save-session-one-shot"
static gboolean gsm_session_clear_saved_session (const char *directory,
GHashTable *discard_hash);
typedef struct {
const char *dir;
GHashTable *discard_hash;
GsmStore *app_store;
GError **error;
} SessionSaveData;
static gboolean
_app_has_app_id (const char *id,
GsmApp *app,
const char *app_id_a)
{
const char *app_id_b;
app_id_b = gsm_app_peek_app_id (app);
return g_strcmp0 (app_id_a, app_id_b) == 0;
}
static gboolean
save_one_client (char *id,
GObject *object,
SessionSaveData *data)
{
GsmClient *client;
GKeyFile *keyfile;
GsmApp *app = NULL;
const char *app_id;
char *path = NULL;
char *filename = NULL;
char *contents = NULL;
gsize length = 0;
char *discard_exec;
g_autoptr(GError) local_error = NULL;
client = GSM_CLIENT (object);
app_id = gsm_client_peek_app_id (client);
if (!IS_STRING_EMPTY (app_id)) {
if (g_str_has_suffix (app_id, ".desktop"))
filename = g_strdup (app_id);
else
filename = g_strdup_printf ("%s.desktop", app_id);
path = g_build_filename (data->dir, filename, NULL);
app = (GsmApp *)gsm_store_find (data->app_store,
(GsmStoreFunc)_app_has_app_id,
(char *)app_id);
}
keyfile = gsm_client_save (client, app, &local_error);
if (keyfile == NULL || local_error) {
goto out;
}
contents = g_key_file_to_data (keyfile, &length, &local_error);
if (local_error) {
goto out;
}
if (!path || g_file_test (path, G_FILE_TEST_EXISTS)) {
if (filename)
g_free (filename);
if (path)
g_free (path);
filename = g_strdup_printf ("%s.desktop",
gsm_client_peek_startup_id (client));
path = g_build_filename (data->dir, filename, NULL);
}
g_file_set_contents (path,
contents,
length,
&local_error);
if (local_error) {
goto out;
}
discard_exec = g_key_file_get_string (keyfile,
G_KEY_FILE_DESKTOP_GROUP,
GSM_AUTOSTART_APP_DISCARD_KEY,
NULL);
if (discard_exec) {
g_hash_table_insert (data->discard_hash,
discard_exec, discard_exec);
}
g_debug ("GsmSessionSave: saved client %s to %s", id, filename);
out:
if (keyfile != NULL) {
g_key_file_free (keyfile);
}
g_free (contents);
g_free (filename);
g_free (path);
/* in case of any error, stop saving session */
if (local_error) {
g_propagate_error (data->error, g_steal_pointer (&local_error));
return TRUE;
}
return FALSE;
}
void
gsm_session_save (GsmStore *client_store,
GsmStore *app_store,
GError **error)
{
GSettings *settings;
const char *save_dir;
SessionSaveData data;
g_debug ("GsmSessionSave: Saving session");
/* Clear one shot key autosave in the event its set (so that it's actually
* one shot only)
*/
settings = g_settings_new (GSM_MANAGER_SCHEMA);
g_settings_set_boolean (settings, KEY_AUTOSAVE_ONE_SHOT, FALSE);
g_object_unref (settings);
save_dir = gsm_util_get_saved_session_dir ();
if (save_dir == NULL) {
g_warning ("GsmSessionSave: cannot create saved session directory");
return;
}
data.dir = save_dir;
data.discard_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
data.app_store = app_store;
/* remove old saved session */
gsm_session_clear_saved_session (save_dir, data.discard_hash);
data.error = error;
gsm_store_foreach (client_store,
(GsmStoreFunc) save_one_client,
&data);
g_hash_table_destroy (data.discard_hash);
}
static gboolean
gsm_session_clear_one_client (const char *filename,
GHashTable *discard_hash)
{
gboolean result = TRUE;
GKeyFile *key_file;
char *discard_exec = NULL;
char **envp;
g_debug ("GsmSessionSave: removing '%s' from saved session", filename);
envp = (char **) gsm_util_listenv ();
key_file = g_key_file_new ();
if (g_key_file_load_from_file (key_file, filename,
G_KEY_FILE_NONE, NULL)) {
char **argv;
int argc;
discard_exec = g_key_file_get_string (key_file,
G_KEY_FILE_DESKTOP_GROUP,
GSM_AUTOSTART_APP_DISCARD_KEY,
NULL);
if (!discard_exec)
goto out;
if (discard_hash && g_hash_table_lookup (discard_hash, discard_exec))
goto out;
if (!g_shell_parse_argv (discard_exec, &argc, &argv, NULL))
goto out;
result = g_spawn_async (NULL, argv, envp, G_SPAWN_SEARCH_PATH,
NULL, NULL, NULL, NULL) && result;
g_strfreev (argv);
} else {
result = FALSE;
}
out:
if (key_file)
g_key_file_free (key_file);
if (discard_exec)
g_free (discard_exec);
result = (g_unlink (filename) == 0) && result;
return result;
}
static gboolean
gsm_session_clear_saved_session (const char *directory,
GHashTable *discard_hash)
{
GDir *dir;
const char *filename;
gboolean result = TRUE;
GError *error;
g_debug ("GsmSessionSave: clearing currently saved session at %s",
directory);
if (directory == NULL) {
return FALSE;
}
error = NULL;
dir = g_dir_open (directory, 0, &error);
if (error) {
g_warning ("GsmSessionSave: error loading saved session directory: %s", error->message);
g_error_free (error);
return FALSE;
}
while ((filename = g_dir_read_name (dir))) {
char *path = g_build_filename (directory,
filename, NULL);
result = gsm_session_clear_one_client (path, discard_hash)
&& result;
g_free (path);
}
g_dir_close (dir);
return result;
}
void
gsm_session_save_clear (void)
{
const char *save_dir;
g_debug ("GsmSessionSave: Clearing saved session");
save_dir = gsm_util_get_saved_session_dir ();
if (save_dir == NULL) {
g_warning ("GsmSessionSave: cannot create saved session directory");
return;
}
gsm_session_clear_saved_session (save_dir, NULL);
}

View file

@ -0,0 +1,34 @@
/* gsm-session-save.h
* Copyright (C) 2008 Lucas Rocha.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GSM_SESSION_SAVE_H__
#define __GSM_SESSION_SAVE_H__
#include <glib.h>
#include "gsm-store.h"
G_BEGIN_DECLS
void gsm_session_save (GsmStore *client_store,
GsmStore *app_store,
GError **error);
void gsm_session_save_clear (void);
G_END_DECLS
#endif /* __GSM_SESSION_SAVE_H__ */

View file

@ -0,0 +1,199 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2011 Red Hat, Inc
*
* 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, 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, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Jasper St. Pierre <jstpierre@mecheye.net>
*/
#include <config.h>
#include <glib.h>
#include <gio/gio.h>
#include <json-glib/json-glib.h>
#include "gsm-shell-extensions.h"
#define SHELL_SCHEMA "org.gnome.shell"
#define DISABLE_EXTENSIONS_KEY "disable-user-extensions"
#define SHELL_EXTENSIONS_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_SHELL_EXTENSIONS, GsmShellExtensionsPrivate))
struct _GsmShellExtensionsPrivate
{
GSettings *settings;
guint num_extensions;
};
G_DEFINE_TYPE (GsmShellExtensions, gsm_shell_extensions, G_TYPE_OBJECT);
/**
* gsm_shell_extensions_finalize:
* @object: (in): A #GsmShellExtensions.
*
* Finalizer for a #GsmShellExtensions instance. Frees any resources held by
* the instance.
*/
static void
gsm_shell_extensions_finalize (GObject *object)
{
GsmShellExtensions *extensions = GSM_SHELL_EXTENSIONS (object);
GsmShellExtensionsPrivate *priv = extensions->priv;
g_clear_object (&priv->settings);
G_OBJECT_CLASS (gsm_shell_extensions_parent_class)->finalize (object);
}
/**
* gsm_shell_extensions_class_init:
* @klass: (in): A #GsmShellExtensionsClass.
*
* Initializes the #GsmShellExtensionsClass and prepares the vtable.
*/
static void
gsm_shell_extensions_class_init (GsmShellExtensionsClass *klass)
{
GObjectClass *object_class;
object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gsm_shell_extensions_finalize;
g_type_class_add_private (object_class, sizeof (GsmShellExtensionsPrivate));
}
static void
gsm_shell_extensions_scan_dir (GsmShellExtensions *self,
GFile *dir)
{
GFileEnumerator *enumerator;
GFileInfo *info;
JsonParser *metadata_parser;
metadata_parser = json_parser_new ();
enumerator = g_file_enumerate_children (dir,
"standard::*",
G_FILE_QUERY_INFO_NONE,
NULL,
NULL);
if (enumerator == NULL)
return;
while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)) != NULL)
{
gchar *metadata_filename;
const gchar *metadata_uuid;
gchar *dir_uuid;
JsonObject *metadata_root;
dir_uuid = (char *) g_file_info_get_name (info);
metadata_filename = g_build_filename (g_file_get_path (dir),
dir_uuid,
"metadata.json",
NULL);
if (!json_parser_load_from_file (metadata_parser, metadata_filename, NULL))
continue;
g_free (metadata_filename);
metadata_root = json_node_get_object (json_parser_get_root (metadata_parser));
metadata_uuid = json_object_get_string_member (metadata_root, "uuid");
if (!g_str_equal (metadata_uuid, dir_uuid))
{
g_warning ("Extension with dirname '%s' does not match metadata's UUID of '%s'. Skipping.",
dir_uuid, metadata_uuid);
continue;
}
self->priv->num_extensions++;
}
}
static void
gsm_shell_extensions_scan (GsmShellExtensions *self)
{
gchar *dirname;
GFile *dir;
const gchar * const * system_data_dirs;
int i;
/* User data dir first. */
dirname = g_build_filename (g_get_user_data_dir (), "gnome-shell", "extensions", NULL);
dir = g_file_new_for_path (dirname);
g_free (dirname);
gsm_shell_extensions_scan_dir (self, dir);
g_object_unref (dir);
system_data_dirs = g_get_system_data_dirs ();
for (i = 0; system_data_dirs[i]; i++)
{
dirname = g_build_filename (system_data_dirs[i], "gnome-shell", "extensions", NULL);
dir = g_file_new_for_path (dirname);
g_free (dirname);
gsm_shell_extensions_scan_dir (self, dir);
g_object_unref (dir);
}
}
/**
* gsm_shell_extensions_init:
* @self: (in): A #GsmShellExtensions.
*
* Initializes the newly created #GsmShellExtensions instance.
*/
static void
gsm_shell_extensions_init (GsmShellExtensions *self)
{
GSettingsSchemaSource *source;
GSettingsSchema *schema;
self->priv = SHELL_EXTENSIONS_PRIVATE (self);
source = g_settings_schema_source_get_default ();
schema = g_settings_schema_source_lookup (source, SHELL_SCHEMA, TRUE);
if (schema != NULL)
{
self->priv->settings = g_settings_new_full (schema, NULL, NULL);
g_settings_schema_unref (schema);
}
if (self->priv->settings != NULL)
gsm_shell_extensions_scan (self);
}
gboolean
gsm_shell_extensions_disable_all (GsmShellExtensions *self)
{
return g_settings_set_boolean (self->priv->settings,
DISABLE_EXTENSIONS_KEY,
TRUE);
}
guint
gsm_shell_extensions_n_extensions (GsmShellExtensions *self)
{
if (self->priv->settings == NULL)
return 0;
return self->priv->num_extensions;
}

View file

@ -0,0 +1,61 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2011 Red Hat, Inc
*
* 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, 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, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Jasper St. Pierre <jstpierre@mecheye.net>
*/
#ifndef __GSM_SHELL_EXTENSIONS_H
#define __GSM_SHELL_EXTENSIONS_H
#include <glib-object.h>
G_BEGIN_DECLS
#define GSM_TYPE_SHELL_EXTENSIONS (gsm_shell_extensions_get_type ())
#define GSM_SHELL_EXTENSIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_SHELL_EXTENSIONS, GsmShellExtensions))
#define GSM_SHELL_EXTENSIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_SHELL_EXTENSIONS, GsmShellExtensionsClass))
#define GSM_IS_SHELL_EXTENSIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_SHELL_EXTENSIONS))
#define GSM_IS_SHELL_EXTENSIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_SHELL_EXTENSIONS))
#define GSM_SHELL_EXTENSIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_SHELL_EXTENSIONS, GsmShellExtensionsClass))
typedef struct _GsmShellExtensions GsmShellExtensions;
typedef struct _GsmShellExtensionsClass GsmShellExtensionsClass;
typedef struct _GsmShellExtensionsPrivate GsmShellExtensionsPrivate;
struct _GsmShellExtensions
{
GObject parent;
/*< private >*/
GsmShellExtensionsPrivate *priv;
};
struct _GsmShellExtensionsClass
{
GObjectClass parent_class;
};
GType gsm_shell_extensions_get_type (void) G_GNUC_CONST;
gboolean gsm_shell_extensions_disable_all (GsmShellExtensions *self);
guint gsm_shell_extensions_n_extensions (GsmShellExtensions *self);
G_END_DECLS
#endif /* __GSM_SHELL_EXTENSIONS_H */

507
gnome-session/gsm-shell.c Normal file
View file

@ -0,0 +1,507 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2010 Red Hat, Inc
*
* 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, 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <glib.h>
#include <glib-object.h>
#include <glib/gi18n.h>
#include <gio/gio.h>
#include "gsm-inhibitor.h"
#include "gsm-shell.h"
#define SHELL_NAME "org.gnome.Shell"
#define SHELL_PATH "/org/gnome/Shell"
#define SHELL_INTERFACE "org.gnome.Shell"
#define SHELL_END_SESSION_DIALOG_PATH "/org/gnome/SessionManager/EndSessionDialog"
#define SHELL_END_SESSION_DIALOG_INTERFACE "org.gnome.SessionManager.EndSessionDialog"
#define AUTOMATIC_ACTION_TIMEOUT 60
#define GSM_SHELL_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_SHELL, GsmShellPrivate))
struct _GsmShellPrivate
{
GDBusProxy *end_session_dialog_proxy;
GsmStore *inhibitors;
guint32 is_running : 1;
gboolean dialog_is_open;
GsmShellEndSessionDialogType end_session_dialog_type;
guint update_idle_id;
guint watch_id;
};
enum {
PROP_0,
PROP_IS_RUNNING
};
enum {
END_SESSION_DIALOG_OPENED = 0,
END_SESSION_DIALOG_OPEN_FAILED,
END_SESSION_DIALOG_CLOSED,
END_SESSION_DIALOG_CANCELED,
END_SESSION_DIALOG_CONFIRMED_LOGOUT,
END_SESSION_DIALOG_CONFIRMED_SHUTDOWN,
END_SESSION_DIALOG_CONFIRMED_REBOOT,
NUMBER_OF_SIGNALS
};
static guint signals[NUMBER_OF_SIGNALS] = { 0 };
static void gsm_shell_class_init (GsmShellClass *klass);
static void gsm_shell_init (GsmShell *ck);
static void gsm_shell_finalize (GObject *object);
static void queue_end_session_dialog_update (GsmShell *shell);
G_DEFINE_TYPE (GsmShell, gsm_shell, G_TYPE_OBJECT);
static void
gsm_shell_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GsmShell *shell = GSM_SHELL (object);
switch (prop_id) {
case PROP_IS_RUNNING:
g_value_set_boolean (value,
shell->priv->is_running);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
prop_id,
pspec);
}
}
static void
gsm_shell_class_init (GsmShellClass *shell_class)
{
GObjectClass *object_class;
GParamSpec *param_spec;
object_class = G_OBJECT_CLASS (shell_class);
object_class->finalize = gsm_shell_finalize;
object_class->get_property = gsm_shell_get_property;
param_spec = g_param_spec_boolean ("is-running",
"Is running",
"Whether GNOME Shell is running in the session",
FALSE,
G_PARAM_READABLE);
g_object_class_install_property (object_class, PROP_IS_RUNNING,
param_spec);
signals [END_SESSION_DIALOG_OPENED] =
g_signal_new ("end-session-dialog-opened",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GsmShellClass, end_session_dialog_opened),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
signals [END_SESSION_DIALOG_OPEN_FAILED] =
g_signal_new ("end-session-dialog-open-failed",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GsmShellClass, end_session_dialog_open_failed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
signals [END_SESSION_DIALOG_CLOSED] =
g_signal_new ("end-session-dialog-closed",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GsmShellClass, end_session_dialog_closed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
signals [END_SESSION_DIALOG_CANCELED] =
g_signal_new ("end-session-dialog-canceled",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GsmShellClass, end_session_dialog_canceled),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
signals [END_SESSION_DIALOG_CONFIRMED_LOGOUT] =
g_signal_new ("end-session-dialog-confirmed-logout",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GsmShellClass, end_session_dialog_confirmed_logout),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
signals [END_SESSION_DIALOG_CONFIRMED_SHUTDOWN] =
g_signal_new ("end-session-dialog-confirmed-shutdown",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GsmShellClass, end_session_dialog_confirmed_shutdown),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
signals [END_SESSION_DIALOG_CONFIRMED_REBOOT] =
g_signal_new ("end-session-dialog-confirmed-reboot",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GsmShellClass, end_session_dialog_confirmed_reboot),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
g_type_class_add_private (shell_class, sizeof (GsmShellPrivate));
}
static void
on_shell_name_vanished (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
GsmShell *shell = user_data;
shell->priv->is_running = FALSE;
}
static void
on_shell_name_appeared (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
gpointer user_data)
{
GsmShell *shell = user_data;
shell->priv->is_running = TRUE;
}
static void
gsm_shell_ensure_connection (GsmShell *shell)
{
if (shell->priv->watch_id != 0) {
return;
}
shell->priv->watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
SHELL_NAME,
G_BUS_NAME_WATCHER_FLAGS_NONE,
on_shell_name_appeared,
on_shell_name_vanished,
shell, NULL);
}
static void
gsm_shell_init (GsmShell *shell)
{
shell->priv = GSM_SHELL_GET_PRIVATE (shell);
gsm_shell_ensure_connection (shell);
}
static void
gsm_shell_finalize (GObject *object)
{
GsmShell *shell;
GObjectClass *parent_class;
shell = GSM_SHELL (object);
parent_class = G_OBJECT_CLASS (gsm_shell_parent_class);
g_object_unref (shell->priv->inhibitors);
if (shell->priv->watch_id != 0) {
g_bus_unwatch_name (shell->priv->watch_id);
shell->priv->watch_id = 0;
}
if (parent_class->finalize != NULL) {
parent_class->finalize (object);
}
}
GsmShell *
gsm_shell_new (void)
{
GsmShell *shell;
shell = g_object_new (GSM_TYPE_SHELL, NULL);
return shell;
}
GsmShell *
gsm_get_shell (void)
{
static GsmShell *shell = NULL;
if (shell == NULL) {
shell = gsm_shell_new ();
}
return g_object_ref (shell);
}
gboolean
gsm_shell_is_running (GsmShell *shell)
{
gsm_shell_ensure_connection (shell);
return shell->priv->is_running;
}
static gboolean
add_inhibitor_to_array (const char *id,
GsmInhibitor *inhibitor,
GVariantBuilder *builder)
{
g_variant_builder_add (builder, "o", gsm_inhibitor_peek_id (inhibitor));
return FALSE;
}
static GVariant *
get_array_from_store (GsmStore *inhibitors)
{
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("ao"));
gsm_store_foreach (inhibitors,
(GsmStoreFunc) add_inhibitor_to_array,
&builder);
return g_variant_builder_end (&builder);
}
static void
on_open_finished (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GsmShell *shell = user_data;
GError *error;
if (shell->priv->update_idle_id != 0) {
g_source_remove (shell->priv->update_idle_id);
shell->priv->update_idle_id = 0;
}
shell->priv->dialog_is_open = FALSE;
error = NULL;
g_dbus_proxy_call_finish (G_DBUS_PROXY (source), result, &error);
if (error != NULL) {
g_warning ("Unable to open shell end session dialog: %s", error->message);
g_error_free (error);
g_signal_emit (G_OBJECT (shell), signals[END_SESSION_DIALOG_OPEN_FAILED], 0);
return;
}
g_signal_emit (G_OBJECT (shell), signals[END_SESSION_DIALOG_OPENED], 0);
}
static void
on_end_session_dialog_dbus_signal (GDBusProxy *proxy,
gchar *sender_name,
gchar *signal_name,
GVariant *parameters,
GsmShell *shell)
{
struct {
const char *name;
int index;
} signal_map[] = {
{ "Closed", END_SESSION_DIALOG_CLOSED },
{ "Canceled", END_SESSION_DIALOG_CANCELED },
{ "ConfirmedLogout", END_SESSION_DIALOG_CONFIRMED_LOGOUT },
{ "ConfirmedReboot", END_SESSION_DIALOG_CONFIRMED_REBOOT },
{ "ConfirmedShutdown", END_SESSION_DIALOG_CONFIRMED_SHUTDOWN },
{ NULL, -1 }
};
int signal_index = -1;
int i;
for (i = 0; signal_map[i].name != NULL; i++) {
if (g_strcmp0 (signal_map[i].name, signal_name) == 0) {
signal_index = signal_map[i].index;
break;
}
}
if (signal_index == -1)
return;
shell->priv->dialog_is_open = FALSE;
if (shell->priv->update_idle_id != 0) {
g_source_remove (shell->priv->update_idle_id);
shell->priv->update_idle_id = 0;
}
g_signal_handlers_disconnect_by_func (shell->priv->inhibitors,
G_CALLBACK (queue_end_session_dialog_update),
shell);
g_signal_emit (G_OBJECT (shell), signals[signal_index], 0);
}
static void
on_end_session_dialog_name_owner_changed (GDBusProxy *proxy,
GParamSpec *pspec,
GsmShell *shell)
{
gchar *name_owner;
name_owner = g_dbus_proxy_get_name_owner (proxy);
if (name_owner == NULL) {
g_clear_object (&shell->priv->end_session_dialog_proxy);
}
g_free (name_owner);
}
static gboolean
on_need_end_session_dialog_update (GsmShell *shell)
{
/* No longer need an update */
if (shell->priv->update_idle_id == 0)
return FALSE;
shell->priv->update_idle_id = 0;
gsm_shell_open_end_session_dialog (shell,
shell->priv->end_session_dialog_type,
shell->priv->inhibitors);
return FALSE;
}
static void
queue_end_session_dialog_update (GsmShell *shell)
{
if (shell->priv->update_idle_id != 0)
return;
shell->priv->update_idle_id = g_idle_add ((GSourceFunc) on_need_end_session_dialog_update,
shell);
}
gboolean
gsm_shell_open_end_session_dialog (GsmShell *shell,
GsmShellEndSessionDialogType type,
GsmStore *inhibitors)
{
GDBusProxy *proxy;
GError *error;
error = NULL;
if (shell->priv->dialog_is_open) {
g_return_val_if_fail (shell->priv->end_session_dialog_type == type,
FALSE);
return TRUE;
}
if (shell->priv->end_session_dialog_proxy == NULL) {
proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
SHELL_NAME,
SHELL_END_SESSION_DIALOG_PATH,
SHELL_END_SESSION_DIALOG_INTERFACE,
NULL, &error);
if (error != NULL) {
g_critical ("Could not connect to the shell: %s",
error->message);
g_error_free (error);
return FALSE;
}
shell->priv->end_session_dialog_proxy = proxy;
g_signal_connect (proxy, "notify::g-name-owner",
G_CALLBACK (on_end_session_dialog_name_owner_changed),
shell);
g_signal_connect (proxy, "g-signal",
G_CALLBACK (on_end_session_dialog_dbus_signal),
shell);
}
g_dbus_proxy_call (shell->priv->end_session_dialog_proxy,
"Open",
g_variant_new ("(uuu@ao)",
type,
0,
AUTOMATIC_ACTION_TIMEOUT,
get_array_from_store (inhibitors)),
G_DBUS_CALL_FLAGS_NONE,
G_MAXINT, NULL,
on_open_finished, shell);
g_object_ref (inhibitors);
if (shell->priv->inhibitors != NULL) {
g_signal_handlers_disconnect_by_func (shell->priv->inhibitors,
G_CALLBACK (queue_end_session_dialog_update),
shell);
g_object_unref (shell->priv->inhibitors);
}
shell->priv->inhibitors = inhibitors;
g_signal_connect_swapped (inhibitors, "added",
G_CALLBACK (queue_end_session_dialog_update),
shell);
g_signal_connect_swapped (inhibitors, "removed",
G_CALLBACK (queue_end_session_dialog_update),
shell);
shell->priv->dialog_is_open = TRUE;
shell->priv->end_session_dialog_type = type;
return TRUE;
}
void
gsm_shell_close_end_session_dialog (GsmShell *shell)
{
if (!shell->priv->end_session_dialog_proxy)
return;
shell->priv->dialog_is_open = FALSE;
g_dbus_proxy_call (shell->priv->end_session_dialog_proxy,
"Close",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1, NULL, NULL, NULL);
}

87
gnome-session/gsm-shell.h Normal file
View file

@ -0,0 +1,87 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2010 Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Ray Strode <rstrode@redhat.com>
*/
#ifndef __GSM_SHELL_H__
#define __GSM_SHELL_H__
#include <glib.h>
#include <glib-object.h>
#include "gsm-store.h"
G_BEGIN_DECLS
#define GSM_TYPE_SHELL (gsm_shell_get_type ())
#define GSM_SHELL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_SHELL, GsmShell))
#define GSM_SHELL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_SHELL, GsmShellClass))
#define GSM_IS_SHELL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_SHELL))
#define GSM_IS_SHELL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_SHELL))
#define GSM_SHELL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GSM_TYPE_SHELL, GsmShellClass))
#define GSM_SHELL_ERROR (gsm_shell_error_quark ())
typedef struct _GsmShell GsmShell;
typedef struct _GsmShellClass GsmShellClass;
typedef struct _GsmShellPrivate GsmShellPrivate;
typedef enum
{
GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT = 0,
GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN,
GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART,
} GsmShellEndSessionDialogType;
struct _GsmShell
{
GObject parent;
GsmShellPrivate *priv;
};
struct _GsmShellClass
{
GObjectClass parent_class;
void (* end_session_dialog_opened) (GsmShell *shell);
void (* end_session_dialog_open_failed) (GsmShell *shell);
void (* end_session_dialog_closed) (GsmShell *shell);
void (* end_session_dialog_canceled) (GsmShell *shell);
void (* end_session_dialog_confirmed_logout) (GsmShell *shell);
void (* end_session_dialog_confirmed_shutdown) (GsmShell *shell);
void (* end_session_dialog_confirmed_reboot) (GsmShell *shell);
};
GType gsm_shell_get_type (void);
GsmShell *gsm_shell_new (void);
GsmShell *gsm_get_shell (void);
gboolean gsm_shell_is_running (GsmShell *shell);
gboolean gsm_shell_open_end_session_dialog (GsmShell *shell,
GsmShellEndSessionDialogType type,
GsmStore *inhibitors);
void gsm_shell_close_end_session_dialog (GsmShell *shell);
G_END_DECLS
#endif /* __GSM_SHELL_H__ */

408
gnome-session/gsm-store.c Normal file
View file

@ -0,0 +1,408 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2007-2008 William Jon McCann <mccann@jhu.edu>
*
* 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <glib-object.h>
#include "gsm-store.h"
#define GSM_STORE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_STORE, GsmStorePrivate))
struct GsmStorePrivate
{
GHashTable *objects;
gboolean locked;
};
enum {
ADDED,
REMOVED,
LAST_SIGNAL
};
enum {
PROP_0,
PROP_LOCKED
};
static guint signals [LAST_SIGNAL] = { 0 };
static void gsm_store_class_init (GsmStoreClass *klass);
static void gsm_store_init (GsmStore *store);
static void gsm_store_finalize (GObject *object);
G_DEFINE_TYPE (GsmStore, gsm_store, G_TYPE_OBJECT)
GQuark
gsm_store_error_quark (void)
{
static GQuark ret = 0;
if (ret == 0) {
ret = g_quark_from_static_string ("gsm_store_error");
}
return ret;
}
guint
gsm_store_size (GsmStore *store)
{
return g_hash_table_size (store->priv->objects);
}
gboolean
gsm_store_remove (GsmStore *store,
const char *id)
{
GObject *found;
gboolean removed;
char *id_copy;
g_return_val_if_fail (store != NULL, FALSE);
found = g_hash_table_lookup (store->priv->objects, id);
if (found == NULL) {
return FALSE;
}
id_copy = g_strdup (id);
g_object_ref (found);
removed = g_hash_table_remove (store->priv->objects, id_copy);
g_assert (removed);
g_signal_emit (store, signals [REMOVED], 0, id_copy);
g_object_unref (found);
g_free (id_copy);
return TRUE;
}
void
gsm_store_foreach (GsmStore *store,
GsmStoreFunc func,
gpointer user_data)
{
g_return_if_fail (store != NULL);
g_return_if_fail (func != NULL);
g_hash_table_find (store->priv->objects,
(GHRFunc)func,
user_data);
}
GObject *
gsm_store_find (GsmStore *store,
GsmStoreFunc predicate,
gpointer user_data)
{
GObject *object;
g_return_val_if_fail (store != NULL, NULL);
g_return_val_if_fail (predicate != NULL, NULL);
object = g_hash_table_find (store->priv->objects,
(GHRFunc)predicate,
user_data);
return object;
}
GObject *
gsm_store_lookup (GsmStore *store,
const char *id)
{
GObject *object;
g_return_val_if_fail (store != NULL, NULL);
g_return_val_if_fail (id != NULL, NULL);
object = g_hash_table_lookup (store->priv->objects, id);
return object;
}
typedef struct
{
GsmStoreFunc func;
gpointer user_data;
GsmStore *store;
GList *removed;
} WrapperData;
static gboolean
foreach_remove_wrapper (const char *id,
GObject *object,
WrapperData *data)
{
gboolean res;
res = (data->func) (id, object, data->user_data);
if (res) {
data->removed = g_list_prepend (data->removed, g_strdup (id));
}
return res;
}
guint
gsm_store_foreach_remove (GsmStore *store,
GsmStoreFunc func,
gpointer user_data)
{
guint ret;
WrapperData data;
g_return_val_if_fail (store != NULL, 0);
g_return_val_if_fail (func != NULL, 0);
data.store = store;
data.user_data = user_data;
data.func = func;
data.removed = NULL;
ret = g_hash_table_foreach_remove (store->priv->objects,
(GHRFunc)foreach_remove_wrapper,
&data);
while (data.removed != NULL) {
char *id;
id = data.removed->data;
g_debug ("GsmStore: emitting removed for %s", id);
g_signal_emit (store, signals [REMOVED], 0, id);
g_free (data.removed->data);
data.removed->data = NULL;
data.removed = g_list_delete_link (data.removed, data.removed);
}
return ret;
}
static gboolean
_remove_all (const char *id,
GObject *object,
gpointer data)
{
return TRUE;
}
void
gsm_store_clear (GsmStore *store)
{
g_return_if_fail (store != NULL);
g_debug ("GsmStore: Clearing object store");
gsm_store_foreach_remove (store,
_remove_all,
NULL);
}
gboolean
gsm_store_add (GsmStore *store,
const char *id,
GObject *object)
{
g_return_val_if_fail (store != NULL, FALSE);
g_return_val_if_fail (id != NULL, FALSE);
g_return_val_if_fail (object != NULL, FALSE);
/* If we're locked, we don't accept any new session
objects. */
if (store->priv->locked) {
return FALSE;
}
g_debug ("GsmStore: Adding object id %s to store", id);
g_hash_table_insert (store->priv->objects,
g_strdup (id),
g_object_ref (object));
g_signal_emit (store, signals [ADDED], 0, id);
return TRUE;
}
void
gsm_store_set_locked (GsmStore *store,
gboolean locked)
{
g_return_if_fail (GSM_IS_STORE (store));
store->priv->locked = locked;
}
gboolean
gsm_store_get_locked (GsmStore *store)
{
g_return_val_if_fail (GSM_IS_STORE (store), FALSE);
return store->priv->locked;
}
static void
gsm_store_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GsmStore *self;
self = GSM_STORE (object);
switch (prop_id) {
case PROP_LOCKED:
gsm_store_set_locked (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gsm_store_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GsmStore *self;
self = GSM_STORE (object);
switch (prop_id) {
case PROP_LOCKED:
g_value_set_boolean (value, self->priv->locked);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gsm_store_dispose (GObject *object)
{
GsmStore *store;
g_return_if_fail (object != NULL);
g_return_if_fail (GSM_IS_STORE (object));
store = GSM_STORE (object);
gsm_store_clear (store);
G_OBJECT_CLASS (gsm_store_parent_class)->dispose (object);
}
static void
gsm_store_class_init (GsmStoreClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = gsm_store_get_property;
object_class->set_property = gsm_store_set_property;
object_class->finalize = gsm_store_finalize;
object_class->dispose = gsm_store_dispose;
signals [ADDED] =
g_signal_new ("added",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GsmStoreClass, added),
NULL, NULL, NULL,
G_TYPE_NONE,
1, G_TYPE_STRING);
signals [REMOVED] =
g_signal_new ("removed",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GsmStoreClass, removed),
NULL, NULL, NULL,
G_TYPE_NONE,
1, G_TYPE_STRING);
g_object_class_install_property (object_class,
PROP_LOCKED,
g_param_spec_boolean ("locked",
NULL,
NULL,
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_type_class_add_private (klass, sizeof (GsmStorePrivate));
}
static void
_destroy_object (GObject *object)
{
g_debug ("GsmStore: Unreffing object: %p", object);
g_object_unref (object);
}
static void
gsm_store_init (GsmStore *store)
{
store->priv = GSM_STORE_GET_PRIVATE (store);
store->priv->objects = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
(GDestroyNotify) _destroy_object);
}
static void
gsm_store_finalize (GObject *object)
{
GsmStore *store;
g_return_if_fail (object != NULL);
g_return_if_fail (GSM_IS_STORE (object));
store = GSM_STORE (object);
g_return_if_fail (store->priv != NULL);
g_hash_table_destroy (store->priv->objects);
G_OBJECT_CLASS (gsm_store_parent_class)->finalize (object);
}
GsmStore *
gsm_store_new (void)
{
GObject *object;
object = g_object_new (GSM_TYPE_STORE,
NULL);
return GSM_STORE (object);
}

96
gnome-session/gsm-store.h Normal file
View file

@ -0,0 +1,96 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2007-2008 William Jon McCann <mccann@jhu.edu>
*
* 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef __GSM_STORE_H
#define __GSM_STORE_H
#include <glib-object.h>
G_BEGIN_DECLS
#define GSM_TYPE_STORE (gsm_store_get_type ())
#define GSM_STORE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSM_TYPE_STORE, GsmStore))
#define GSM_STORE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSM_TYPE_STORE, GsmStoreClass))
#define GSM_IS_STORE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSM_TYPE_STORE))
#define GSM_IS_STORE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSM_TYPE_STORE))
#define GSM_STORE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSM_TYPE_STORE, GsmStoreClass))
typedef struct GsmStorePrivate GsmStorePrivate;
typedef struct
{
GObject parent;
GsmStorePrivate *priv;
} GsmStore;
typedef struct
{
GObjectClass parent_class;
void (* added) (GsmStore *store,
const char *id);
void (* removed) (GsmStore *store,
const char *id);
} GsmStoreClass;
typedef enum
{
GSM_STORE_ERROR_GENERAL
} GsmStoreError;
#define GSM_STORE_ERROR gsm_store_error_quark ()
typedef gboolean (*GsmStoreFunc) (const char *id,
GObject *object,
gpointer user_data);
GQuark gsm_store_error_quark (void);
GType gsm_store_get_type (void);
GsmStore * gsm_store_new (void);
gboolean gsm_store_get_locked (GsmStore *store);
void gsm_store_set_locked (GsmStore *store,
gboolean locked);
guint gsm_store_size (GsmStore *store);
gboolean gsm_store_add (GsmStore *store,
const char *id,
GObject *object);
void gsm_store_clear (GsmStore *store);
gboolean gsm_store_remove (GsmStore *store,
const char *id);
void gsm_store_foreach (GsmStore *store,
GsmStoreFunc func,
gpointer user_data);
guint gsm_store_foreach_remove (GsmStore *store,
GsmStoreFunc func,
gpointer user_data);
GObject * gsm_store_find (GsmStore *store,
GsmStoreFunc predicate,
gpointer user_data);
GObject * gsm_store_lookup (GsmStore *store,
const char *id);
G_END_DECLS
#endif /* __GSM_STORE_H */

276
gnome-session/gsm-system.c Normal file
View file

@ -0,0 +1,276 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2012 Red Hat, Inc.
*
* 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, 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <glib-object.h>
#include <glib/gi18n.h>
#include "gsm-system.h"
#include "gsm-systemd.h"
enum {
REQUEST_COMPLETED,
SHUTDOWN_PREPARED,
LAST_SIGNAL
};
enum {
PROP_0,
PROP_ACTIVE
};
static guint signals[LAST_SIGNAL] = { 0 };
G_DEFINE_INTERFACE (GsmSystem, gsm_system, G_TYPE_OBJECT)
static void
gsm_system_default_init (GsmSystemInterface *iface)
{
GParamSpec *pspec;
signals [REQUEST_COMPLETED] =
g_signal_new ("request-completed",
GSM_TYPE_SYSTEM,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GsmSystemInterface, request_completed),
NULL, NULL, NULL,
G_TYPE_NONE,
1, G_TYPE_POINTER);
signals[SHUTDOWN_PREPARED] =
g_signal_new ("shutdown-prepared",
GSM_TYPE_SYSTEM,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GsmSystemInterface, shutdown_prepared),
NULL, NULL, NULL,
G_TYPE_NONE,
1, G_TYPE_BOOLEAN);
pspec = g_param_spec_boolean ("active",
"Active",
"Whether or not session is active",
TRUE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
g_object_interface_install_property (iface, pspec);
}
typedef GObject GsmSystemNull;
typedef GObjectClass GsmSystemNullClass;
static void do_nothing (void) { }
static gboolean return_true (void) { return TRUE; }
static gboolean return_false (void) { return FALSE; }
static void
gsm_system_null_init_iface (GsmSystemInterface *iface)
{
iface->can_switch_user = (void *) return_false;
iface->can_stop = (void *) return_false;
iface->can_restart = (void *) return_false;
iface->can_restart_to_firmware_setup = (void *) return_false;
iface->set_restart_to_firmware_setup = (void *) do_nothing;
iface->can_suspend = (void *) return_false;
iface->can_hibernate = (void *) return_false;
iface->attempt_stop = (void *) do_nothing;
iface->attempt_restart = (void *) do_nothing;
iface->suspend = (void *) do_nothing;
iface->hibernate = (void *) do_nothing;
iface->set_session_idle = (void *) do_nothing;
iface->is_login_session = (void *) return_true;
iface->set_inhibitors = (void *) do_nothing;
iface->prepare_shutdown = (void *) do_nothing;
iface->complete_shutdown = (void *) do_nothing;
iface->is_last_session_for_user = (void *) return_false;
}
static void
gsm_system_null_init (GsmSystemNull *gsn)
{
}
static void
gsm_system_null_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
g_value_set_boolean (value, TRUE);
}
static void
gsm_system_null_class_init (GsmSystemNullClass *class)
{
class->get_property = gsm_system_null_get_property;
class->set_property = (void *) do_nothing;
g_object_class_override_property (class, 1, "active");
}
static GType gsm_system_null_get_type (void);
G_DEFINE_TYPE_WITH_CODE (GsmSystemNull, gsm_system_null, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GSM_TYPE_SYSTEM, gsm_system_null_init_iface))
GQuark
gsm_system_error_quark (void)
{
static GQuark error_quark = 0;
if (error_quark == 0) {
error_quark = g_quark_from_static_string ("gsm-system-error");
}
return error_quark;
}
gboolean
gsm_system_can_switch_user (GsmSystem *system)
{
return GSM_SYSTEM_GET_IFACE (system)->can_switch_user (system);
}
gboolean
gsm_system_can_stop (GsmSystem *system)
{
return GSM_SYSTEM_GET_IFACE (system)->can_stop (system);
}
gboolean
gsm_system_can_restart (GsmSystem *system)
{
return GSM_SYSTEM_GET_IFACE (system)->can_restart (system);
}
gboolean
gsm_system_can_restart_to_firmware_setup (GsmSystem *system)
{
return GSM_SYSTEM_GET_IFACE (system)->can_restart_to_firmware_setup (system);
}
void
gsm_system_set_restart_to_firmware_setup (GsmSystem *system,
gboolean enable)
{
GSM_SYSTEM_GET_IFACE (system)->set_restart_to_firmware_setup (system, enable);
}
gboolean
gsm_system_can_suspend (GsmSystem *system)
{
return GSM_SYSTEM_GET_IFACE (system)->can_suspend (system);
}
gboolean
gsm_system_can_hibernate (GsmSystem *system)
{
return GSM_SYSTEM_GET_IFACE (system)->can_hibernate (system);
}
void
gsm_system_attempt_stop (GsmSystem *system)
{
GSM_SYSTEM_GET_IFACE (system)->attempt_stop (system);
}
void
gsm_system_attempt_restart (GsmSystem *system)
{
GSM_SYSTEM_GET_IFACE (system)->attempt_restart (system);
}
void
gsm_system_suspend (GsmSystem *system)
{
GSM_SYSTEM_GET_IFACE (system)->suspend (system);
}
void
gsm_system_hibernate (GsmSystem *system)
{
GSM_SYSTEM_GET_IFACE (system)->hibernate (system);
}
void
gsm_system_set_session_idle (GsmSystem *system,
gboolean is_idle)
{
GSM_SYSTEM_GET_IFACE (system)->set_session_idle (system, is_idle);
}
void
gsm_system_set_inhibitors (GsmSystem *system,
GsmInhibitorFlag flags)
{
GSM_SYSTEM_GET_IFACE (system)->set_inhibitors (system, flags);
}
gboolean
gsm_system_is_login_session (GsmSystem *system)
{
return GSM_SYSTEM_GET_IFACE (system)->is_login_session (system);
}
gboolean
gsm_system_is_last_session_for_user (GsmSystem *system)
{
return GSM_SYSTEM_GET_IFACE (system)->is_last_session_for_user (system);
}
/**
* gsm_system_is_active:
*
* Returns: %TRUE if the current session is in the foreground
* Since: 3.8
*/
gboolean
gsm_system_is_active (GsmSystem *system)
{
gboolean is_active;
g_object_get ((GObject*)system, "active", &is_active, NULL);
return is_active;
}
void
gsm_system_prepare_shutdown (GsmSystem *system,
gboolean restart)
{
GSM_SYSTEM_GET_IFACE (system)->prepare_shutdown (system, restart);
}
void
gsm_system_complete_shutdown (GsmSystem *system)
{
GSM_SYSTEM_GET_IFACE (system)->complete_shutdown (system);
}
GsmSystem *
gsm_get_system (void)
{
static GsmSystem *system = NULL;
if (system == NULL) {
system = GSM_SYSTEM (gsm_systemd_new ());
if (system != NULL) {
g_debug ("Using systemd for session tracking");
}
}
if (system == NULL) {
system = g_object_new (gsm_system_null_get_type (), NULL);
g_warning ("Using null backend for session tracking");
}
return g_object_ref (system);
}

130
gnome-session/gsm-system.h Normal file
View file

@ -0,0 +1,130 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2008 Jon McCann <jmccann@redhat.com>
*
* 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, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Jon McCann <jmccann@redhat.com>
*/
#ifndef __GSM_SYSTEM_H__
#define __GSM_SYSTEM_H__
#include <glib.h>
#include <glib-object.h>
#include "gsm-inhibitor.h"
G_BEGIN_DECLS
#define GSM_TYPE_SYSTEM (gsm_system_get_type ())
#define GSM_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_SYSTEM, GsmSystem))
#define GSM_SYSTEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_SYSTEM, GsmSystemInterface))
#define GSM_IS_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_SYSTEM))
#define GSM_SYSTEM_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), GSM_TYPE_SYSTEM, GsmSystemInterface))
#define GSM_SYSTEM_ERROR (gsm_system_error_quark ())
typedef struct _GsmSystem GsmSystem;
typedef struct _GsmSystemInterface GsmSystemInterface;
typedef enum _GsmSystemError GsmSystemError;
struct _GsmSystemInterface
{
GTypeInterface base_interface;
void (* request_completed) (GsmSystem *system,
GError *error);
void (* shutdown_prepared) (GsmSystem *system,
gboolean success);
gboolean (* can_switch_user) (GsmSystem *system);
gboolean (* can_stop) (GsmSystem *system);
gboolean (* can_restart) (GsmSystem *system);
gboolean (* can_restart_to_firmware_setup) (GsmSystem *system);
void (* set_restart_to_firmware_setup) (GsmSystem *system,
gboolean enable);
gboolean (* can_suspend) (GsmSystem *system);
gboolean (* can_hibernate) (GsmSystem *system);
void (* attempt_stop) (GsmSystem *system);
void (* attempt_restart) (GsmSystem *system);
void (* suspend) (GsmSystem *system);
void (* hibernate) (GsmSystem *system);
void (* set_session_idle) (GsmSystem *system,
gboolean is_idle);
gboolean (* is_login_session) (GsmSystem *system);
void (* set_inhibitors) (GsmSystem *system,
GsmInhibitorFlag flags);
void (* prepare_shutdown) (GsmSystem *system,
gboolean restart);
void (* complete_shutdown)(GsmSystem *system);
gboolean (* is_last_session_for_user) (GsmSystem *system);
};
enum _GsmSystemError {
GSM_SYSTEM_ERROR_RESTARTING = 0,
GSM_SYSTEM_ERROR_STOPPING
};
GType gsm_system_get_type (void);
GQuark gsm_system_error_quark (void);
GsmSystem *gsm_get_system (void);
gboolean gsm_system_can_switch_user (GsmSystem *system);
gboolean gsm_system_can_stop (GsmSystem *system);
gboolean gsm_system_can_restart (GsmSystem *system);
gboolean gsm_system_can_restart_to_firmware_setup (GsmSystem *system);
void gsm_system_set_restart_to_firmware_setup (GsmSystem *system,
gboolean enable);
gboolean gsm_system_can_suspend (GsmSystem *system);
gboolean gsm_system_can_hibernate (GsmSystem *system);
void gsm_system_attempt_stop (GsmSystem *system);
void gsm_system_attempt_restart (GsmSystem *system);
void gsm_system_suspend (GsmSystem *system);
void gsm_system_hibernate (GsmSystem *system);
void gsm_system_set_session_idle (GsmSystem *system,
gboolean is_idle);
gboolean gsm_system_is_login_session (GsmSystem *system);
gboolean gsm_system_is_last_session_for_user (GsmSystem *system);
gboolean gsm_system_is_active (GsmSystem *system);
void gsm_system_set_inhibitors (GsmSystem *system,
GsmInhibitorFlag flags);
void gsm_system_prepare_shutdown (GsmSystem *system,
gboolean restart);
void gsm_system_complete_shutdown (GsmSystem *system);
G_END_DECLS
#endif /* __GSM_SYSTEM_H__ */

1197
gnome-session/gsm-systemd.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,59 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2012 Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Matthias Clasen <mclasen@redhat.com>
*/
#ifndef __GSM_SYSTEMD_H__
#define __GSM_SYSTEMD_H__
#include <glib.h>
#include <glib-object.h>
G_BEGIN_DECLS
#define GSM_TYPE_SYSTEMD (gsm_systemd_get_type ())
#define GSM_SYSTEMD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_SYSTEMD, GsmSystemd))
#define GSM_SYSTEMD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_SYSTEMD, GsmSystemdClass))
#define GSM_IS_SYSTEMD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_SYSTEMD))
#define GSM_IS_SYSTEMD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_SYSTEMD))
#define GSM_SYSTEMD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GSM_TYPE_SYSTEMD, GsmSystemdClass))
typedef struct _GsmSystemd GsmSystemd;
typedef struct _GsmSystemdClass GsmSystemdClass;
typedef struct _GsmSystemdPrivate GsmSystemdPrivate;
struct _GsmSystemd
{
GObject parent;
GsmSystemdPrivate *priv;
};
struct _GsmSystemdClass
{
GObjectClass parent_class;
};
GType gsm_systemd_get_type (void);
GsmSystemd *gsm_systemd_new (void) G_GNUC_MALLOC;
G_END_DECLS
#endif /* __GSM_SYSTEMD_H__ */

929
gnome-session/gsm-util.c Normal file
View file

@ -0,0 +1,929 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
* gsm-util.c
* Copyright (C) 2008 Lucas Rocha.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include "gsm-util.h"
static gchar *_saved_session_dir = NULL;
static gchar **child_environment;
/* These are variables that will not be passed on to subprocesses
* (either directly, via systemd or DBus).
* Some of these are blacklisted as they might end up in the wrong session
* (e.g. XDG_VTNR), others because they simply must never be passed on
* (NOTIFY_SOCKET).
*/
static const char * const variable_blacklist[] = {
"NOTIFY_SOCKET",
"XDG_SEAT",
"XDG_SESSION_ID",
"XDG_VTNR",
NULL
};
/* The following is copied from GDMs spawn_session function.
*
* Environment variables listed here will be copied into the user's service
* environments if they are set in gnome-session's environment. If they are
* not set in gnome-session's environment, they will be removed from the
* service environments. This is to protect against environment variables
* leaking from previous sessions (e.g. when switching from classic to
* default GNOME $GNOME_SHELL_SESSION_MODE will become unset).
*/
static const char * const variable_unsetlist[] = {
"DISPLAY",
"XAUTHORITY",
"WAYLAND_DISPLAY",
"WAYLAND_SOCKET",
"GNOME_SHELL_SESSION_MODE",
"GNOME_SETUP_DISPLAY",
/* None of the LC_* variables should survive a logout/login */
"LC_CTYPE",
"LC_NUMERIC",
"LC_TIME",
"LC_COLLATE",
"LC_MONETARY",
"LC_MESSAGES",
"LC_PAPER",
"LC_NAME",
"LC_ADDRESS",
"LC_TELEPHONE",
"LC_MEASUREMENT",
"LC_IDENTIFICATION",
"LC_ALL",
NULL
};
char *
gsm_util_find_desktop_file_for_app_name (const char *name,
gboolean look_in_saved_session,
gboolean autostart_first)
{
char *app_path;
char **app_dirs;
GKeyFile *key_file;
char *desktop_file;
int i;
app_path = NULL;
app_dirs = gsm_util_get_desktop_dirs (look_in_saved_session, autostart_first);
key_file = g_key_file_new ();
desktop_file = g_strdup_printf ("%s.desktop", name);
g_debug ("GsmUtil: Looking for file '%s'", desktop_file);
for (i = 0; app_dirs[i] != NULL; i++) {
g_debug ("GsmUtil: Looking in '%s'", app_dirs[i]);
}
g_key_file_load_from_dirs (key_file,
desktop_file,
(const char **) app_dirs,
&app_path,
G_KEY_FILE_NONE,
NULL);
if (app_path != NULL) {
g_debug ("GsmUtil: found in XDG dirs: '%s'", app_path);
}
/* look for gnome vendor prefix */
if (app_path == NULL) {
g_free (desktop_file);
desktop_file = g_strdup_printf ("gnome-%s.desktop", name);
g_key_file_load_from_dirs (key_file,
desktop_file,
(const char **) app_dirs,
&app_path,
G_KEY_FILE_NONE,
NULL);
if (app_path != NULL) {
g_debug ("GsmUtil: found in XDG dirs: '%s'", app_path);
}
}
g_free (desktop_file);
g_key_file_free (key_file);
g_strfreev (app_dirs);
return app_path;
}
static gboolean
ensure_dir_exists (const char *dir)
{
if (g_mkdir_with_parents (dir, 0700) == 0)
return TRUE;
g_warning ("GsmSessionSave: Failed to create directory %s: %s", dir, strerror (errno));
return FALSE;
}
gchar *
gsm_util_get_empty_tmp_session_dir (void)
{
char *tmp;
gboolean exists;
tmp = g_build_filename (g_get_user_config_dir (),
"gnome-session",
"saved-session.new",
NULL);
exists = ensure_dir_exists (tmp);
if (G_UNLIKELY (!exists)) {
g_warning ("GsmSessionSave: could not create directory for saved session: %s", tmp);
g_free (tmp);
return NULL;
} else {
/* make sure it's empty */
GDir *dir;
const char *filename;
dir = g_dir_open (tmp, 0, NULL);
if (dir) {
while ((filename = g_dir_read_name (dir))) {
char *path = g_build_filename (tmp, filename,
NULL);
g_unlink (path);
}
g_dir_close (dir);
}
}
return tmp;
}
const gchar *
gsm_util_get_saved_session_dir (void)
{
if (_saved_session_dir == NULL) {
gboolean exists;
_saved_session_dir =
g_build_filename (g_get_user_config_dir (),
"gnome-session",
"saved-session",
NULL);
exists = ensure_dir_exists (_saved_session_dir);
if (G_UNLIKELY (!exists)) {
static gboolean printed_warning = FALSE;
if (!printed_warning) {
g_warning ("GsmSessionSave: could not create directory for saved session: %s", _saved_session_dir);
printed_warning = TRUE;
}
_saved_session_dir = NULL;
return NULL;
}
}
return _saved_session_dir;
}
static char ** autostart_dirs;
void
gsm_util_set_autostart_dirs (char ** dirs)
{
autostart_dirs = g_strdupv (dirs);
}
static char **
gsm_util_get_standard_autostart_dirs (void)
{
GPtrArray *dirs;
const char * const *system_config_dirs;
const char * const *system_data_dirs;
int i;
dirs = g_ptr_array_new ();
g_ptr_array_add (dirs,
g_build_filename (g_get_user_config_dir (),
"autostart", NULL));
system_data_dirs = g_get_system_data_dirs ();
for (i = 0; system_data_dirs[i]; i++) {
g_ptr_array_add (dirs,
g_build_filename (system_data_dirs[i],
"gnome", "autostart", NULL));
}
system_config_dirs = g_get_system_config_dirs ();
for (i = 0; system_config_dirs[i]; i++) {
g_ptr_array_add (dirs,
g_build_filename (system_config_dirs[i],
"autostart", NULL));
}
g_ptr_array_add (dirs, NULL);
return (char **) g_ptr_array_free (dirs, FALSE);
}
char **
gsm_util_get_autostart_dirs ()
{
if (autostart_dirs) {
return g_strdupv ((char **)autostart_dirs);
}
return gsm_util_get_standard_autostart_dirs ();
}
char **
gsm_util_get_app_dirs ()
{
GPtrArray *dirs;
const char * const *system_data_dirs;
int i;
dirs = g_ptr_array_new ();
g_ptr_array_add (dirs,
g_build_filename (g_get_user_data_dir (),
"applications",
NULL));
system_data_dirs = g_get_system_data_dirs ();
for (i = 0; system_data_dirs[i]; i++) {
g_ptr_array_add (dirs,
g_build_filename (system_data_dirs[i],
"applications",
NULL));
}
g_ptr_array_add (dirs, NULL);
return (char **) g_ptr_array_free (dirs, FALSE);
}
char **
gsm_util_get_desktop_dirs (gboolean include_saved_session,
gboolean autostart_first)
{
char **apps;
char **autostart;
char **standard_autostart;
char **result;
int size;
int i;
apps = gsm_util_get_app_dirs ();
autostart = gsm_util_get_autostart_dirs ();
/* Still, check the standard autostart dirs for things like fulfilling session reqs,
* if using a non-standard autostart dir for autostarting */
if (autostart_dirs != NULL)
standard_autostart = gsm_util_get_standard_autostart_dirs ();
else
standard_autostart = NULL;
size = 0;
for (i = 0; apps[i] != NULL; i++) { size++; }
for (i = 0; autostart[i] != NULL; i++) { size++; }
if (standard_autostart != NULL)
for (i = 0; standard_autostart[i] != NULL; i++) { size++; }
if (include_saved_session)
size += 1;
result = g_new (char *, size + 1); /* including last NULL */
size = 0;
if (autostart_first) {
if (include_saved_session)
result[size++] = g_strdup (gsm_util_get_saved_session_dir ());
for (i = 0; autostart[i] != NULL; i++, size++) {
result[size] = autostart[i];
}
if (standard_autostart != NULL) {
for (i = 0; standard_autostart[i] != NULL; i++, size++) {
result[size] = standard_autostart[i];
}
}
for (i = 0; apps[i] != NULL; i++, size++) {
result[size] = apps[i];
}
} else {
for (i = 0; apps[i] != NULL; i++, size++) {
result[size] = apps[i];
}
if (standard_autostart != NULL) {
for (i = 0; standard_autostart[i] != NULL; i++, size++) {
result[size] = standard_autostart[i];
}
}
for (i = 0; autostart[i] != NULL; i++, size++) {
result[size] = autostart[i];
}
if (include_saved_session)
result[size++] = g_strdup (gsm_util_get_saved_session_dir ());
}
g_free (apps);
g_free (autostart);
g_free (standard_autostart);
result[size] = NULL;
return result;
}
gboolean
gsm_util_text_is_blank (const char *str)
{
if (str == NULL) {
return TRUE;
}
while (*str) {
if (!isspace(*str)) {
return FALSE;
}
str++;
}
return TRUE;
}
/**
* gsm_util_init_error:
* @fatal: whether or not the error is fatal to the login session
* @format: printf-style error message format
* @...: error message args
*
* Displays the error message to the user. If @fatal is %TRUE, gsm
* will exit after displaying the message.
*
* This should be called for major errors that occur before the
* session is up and running. (Notably, it positions the dialog box
* itself, since no window manager will be running yet.)
**/
void
gsm_util_init_error (gboolean fatal,
const char *format, ...)
{
char *msg;
va_list args;
gchar *argv[13];
va_start (args, format);
msg = g_strdup_vprintf (format, args);
va_end (args);
argv[0] = "zenity";
argv[1] = "--error";
argv[2] = "--class";
argv[3] = "mutter-dialog";
argv[4] = "--title";
argv[5] = "\"\"";
argv[6] = "--text";
argv[7] = msg;
argv[8] = "--icon-name";
argv[9] = "face-sad-symbolic";
argv[10] = "--ok-label";
argv[11] = _("_Log out");
argv[12] = NULL;
g_spawn_sync (NULL, argv, child_environment, 0, NULL, NULL, NULL, NULL, NULL, NULL);
g_free (msg);
if (fatal) {
exit (1);
}
}
/**
* gsm_util_generate_startup_id:
*
* Generates a new SM client ID.
*
* Return value: an SM client ID.
**/
char *
gsm_util_generate_startup_id (void)
{
static int sequence = -1;
static guint rand1 = 0;
static guint rand2 = 0;
static pid_t pid = 0;
struct timeval tv;
/* The XSMP spec defines the ID as:
*
* Version: "1"
* Address type and address:
* "1" + an IPv4 address as 8 hex digits
* "2" + a DECNET address as 12 hex digits
* "6" + an IPv6 address as 32 hex digits
* Time stamp: milliseconds since UNIX epoch as 13 decimal digits
* Process-ID type and process-ID:
* "1" + POSIX PID as 10 decimal digits
* Sequence number as 4 decimal digits
*
* XSMP client IDs are supposed to be globally unique: if
* SmsGenerateClientID() is unable to determine a network
* address for the machine, it gives up and returns %NULL.
* GNOME and KDE have traditionally used a fourth address
* format in this case:
* "0" + 16 random hex digits
*
* We don't even bother trying SmsGenerateClientID(), since the
* user's IP address is probably "192.168.1.*" anyway, so a random
* number is actually more likely to be globally unique.
*/
if (!rand1) {
rand1 = g_random_int ();
rand2 = g_random_int ();
pid = getpid ();
}
sequence = (sequence + 1) % 10000;
gettimeofday (&tv, NULL);
return g_strdup_printf ("10%.04x%.04x%.10lu%.3u%.10lu%.4d",
rand1,
rand2,
(unsigned long) tv.tv_sec,
(unsigned) tv.tv_usec,
(unsigned long) pid,
sequence);
}
static gboolean
gsm_util_update_activation_environment (const char *variable,
const char *value,
GError **error)
{
GDBusConnection *connection;
gboolean environment_updated;
GVariantBuilder builder;
GVariant *reply;
GError *bus_error = NULL;
environment_updated = FALSE;
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
if (connection == NULL) {
return FALSE;
}
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}"));
g_variant_builder_add (&builder, "{ss}", variable, value);
reply = g_dbus_connection_call_sync (connection,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"UpdateActivationEnvironment",
g_variant_new ("(@a{ss})",
g_variant_builder_end (&builder)),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1, NULL, &bus_error);
if (bus_error != NULL) {
g_propagate_error (error, bus_error);
} else {
environment_updated = TRUE;
g_variant_unref (reply);
}
g_clear_object (&connection);
return environment_updated;
}
gboolean
gsm_util_export_activation_environment (GError **error)
{
GDBusConnection *connection;
gboolean environment_updated = FALSE;
char **entry_names;
int i = 0;
GVariantBuilder builder;
GRegex *name_regex, *value_regex;
GVariant *reply;
GError *bus_error = NULL;
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
if (connection == NULL) {
return FALSE;
}
name_regex = g_regex_new ("^[a-zA-Z_][a-zA-Z0-9_]*$", G_REGEX_OPTIMIZE, 0, error);
if (name_regex == NULL) {
return FALSE;
}
value_regex = g_regex_new ("^(?:[ \t\n]|[^[:cntrl:]])*$", G_REGEX_OPTIMIZE, 0, error);
if (value_regex == NULL) {
return FALSE;
}
if (child_environment == NULL) {
child_environment = g_listenv ();
}
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}"));
for (entry_names = g_listenv (); entry_names[i] != NULL; i++) {
const char *entry_name = entry_names[i];
const char *entry_value = g_getenv (entry_name);
if (g_strv_contains (variable_blacklist, entry_name))
continue;
if (!g_utf8_validate (entry_name, -1, NULL) ||
!g_regex_match (name_regex, entry_name, 0, NULL) ||
!g_utf8_validate (entry_value, -1, NULL) ||
!g_regex_match (value_regex, entry_value, 0, NULL)) {
g_message ("Environment variable is unsafe to export to dbus: %s", entry_name);
continue;
}
child_environment = g_environ_setenv (child_environment,
entry_name, entry_value,
TRUE);
g_variant_builder_add (&builder, "{ss}", entry_name, entry_value);
}
g_regex_unref (name_regex);
g_regex_unref (value_regex);
g_strfreev (entry_names);
reply = g_dbus_connection_call_sync (connection,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"UpdateActivationEnvironment",
g_variant_new ("(@a{ss})",
g_variant_builder_end (&builder)),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1, NULL, &bus_error);
if (bus_error != NULL) {
g_propagate_error (error, bus_error);
} else {
environment_updated = TRUE;
g_variant_unref (reply);
}
g_clear_object (&connection);
return environment_updated;
}
gboolean
gsm_util_export_user_environment (GError **error)
{
GDBusConnection *connection;
gboolean environment_updated = FALSE;
char **entries;
int i = 0;
GVariantBuilder builder;
GRegex *regex;
GVariant *reply;
GError *bus_error = NULL;
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
if (connection == NULL) {
return FALSE;
}
regex = g_regex_new ("^[a-zA-Z_][a-zA-Z0-9_]*=(?:[ \t\n]|[^[:cntrl:]])*$", G_REGEX_OPTIMIZE, 0, error);
if (regex == NULL) {
return FALSE;
}
entries = g_get_environ ();
for (i = 0; variable_blacklist[i] != NULL; i++)
entries = g_environ_unsetenv (entries, variable_blacklist[i]);
g_variant_builder_init (&builder, G_VARIANT_TYPE ("(asas)"));
g_variant_builder_open (&builder, G_VARIANT_TYPE ("as"));
for (i = 0; variable_unsetlist[i] != NULL; i++)
g_variant_builder_add (&builder, "s", variable_unsetlist[i]);
for (i = 0; variable_blacklist[i] != NULL; i++)
g_variant_builder_add (&builder, "s", variable_blacklist[i]);
g_variant_builder_close (&builder);
g_variant_builder_open (&builder, G_VARIANT_TYPE ("as"));
for (i = 0; entries[i] != NULL; i++) {
const char *entry = entries[i];
if (!g_utf8_validate (entry, -1, NULL) ||
!g_regex_match (regex, entry, 0, NULL)) {
g_message ("Environment entry is unsafe to upload into user environment: %s", entry);
continue;
}
g_variant_builder_add (&builder, "s", entry);
}
g_variant_builder_close (&builder);
g_regex_unref (regex);
g_strfreev (entries);
reply = g_dbus_connection_call_sync (connection,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"UnsetAndSetEnvironment",
g_variant_builder_end (&builder),
NULL,
G_DBUS_CALL_FLAGS_NO_AUTO_START,
-1, NULL, &bus_error);
if (bus_error != NULL) {
g_propagate_error (error, bus_error);
} else {
environment_updated = TRUE;
g_variant_unref (reply);
}
g_clear_object (&connection);
return environment_updated;
}
static gboolean
gsm_util_update_user_environment (const char *variable,
const char *value,
GError **error)
{
GDBusConnection *connection;
gboolean environment_updated;
char *entry;
GVariantBuilder builder;
GVariant *reply;
GError *bus_error = NULL;
environment_updated = FALSE;
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
if (connection == NULL) {
return FALSE;
}
g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
entry = g_strdup_printf ("%s=%s", variable, value);
g_variant_builder_add (&builder, "s", entry);
g_free (entry);
reply = g_dbus_connection_call_sync (connection,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"SetEnvironment",
g_variant_new ("(@as)",
g_variant_builder_end (&builder)),
NULL,
G_DBUS_CALL_FLAGS_NO_AUTO_START,
-1, NULL, &bus_error);
if (bus_error != NULL) {
g_propagate_error (error, bus_error);
} else {
environment_updated = TRUE;
g_variant_unref (reply);
}
g_clear_object (&connection);
return environment_updated;
}
gboolean
gsm_util_systemd_unit_is_active (const char *unit,
GError **error)
{
g_autoptr(GDBusProxy) proxy = NULL;
g_autoptr(GVariant) result = NULL;
g_autofree gchar *object_path = NULL;
g_autofree gchar *active_state = NULL;
g_autoptr(GDBusProxy) unit_proxy = NULL;
proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
NULL,
error);
if (proxy == NULL) {
return FALSE;
}
result = g_dbus_proxy_call_sync (proxy,
"GetUnit",
g_variant_new ("(s)", unit),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
error);
if (result == NULL) {
if (error && *error) {
g_autofree char *remote_error = g_dbus_error_get_remote_error (*error);
if (g_strcmp0 (remote_error, "org.freedesktop.systemd1.NoSuchUnit") == 0) {
g_clear_error (error);
}
}
return FALSE;
}
g_variant_get (result, "(o)", &object_path);
g_clear_pointer (&result, g_variant_unref);
unit_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
"org.freedesktop.systemd1",
object_path,
"org.freedesktop.systemd1.Unit",
NULL,
error);
if (unit_proxy == NULL) {
return FALSE;
}
result = g_dbus_proxy_get_cached_property (unit_proxy, "ActiveState");
if (result == NULL) {
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY, "Error getting ActiveState property");
return FALSE;
}
g_variant_get (result, "s", &active_state);
return g_str_equal (active_state, "active");
}
gboolean
gsm_util_start_systemd_unit (const char *unit,
const char *mode,
GError **error)
{
g_autoptr(GDBusConnection) connection = NULL;
g_autoptr(GVariant) reply = NULL;
GError *bus_error = NULL;
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
if (connection == NULL)
return FALSE;
reply = g_dbus_connection_call_sync (connection,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartUnit",
g_variant_new ("(ss)",
unit, mode),
NULL,
G_DBUS_CALL_FLAGS_NO_AUTO_START,
-1, NULL, &bus_error);
if (bus_error != NULL) {
g_propagate_error (error, bus_error);
return FALSE;
}
return TRUE;
}
gboolean
gsm_util_systemd_reset_failed (GError **error)
{
g_autoptr(GDBusConnection) connection = NULL;
g_autoptr(GVariant) reply = NULL;
GError *bus_error = NULL;
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
if (connection == NULL)
return FALSE;
reply = g_dbus_connection_call_sync (connection,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"ResetFailed",
NULL,
NULL,
G_DBUS_CALL_FLAGS_NO_AUTO_START,
-1, NULL, &bus_error);
if (bus_error != NULL) {
g_propagate_error (error, bus_error);
return FALSE;
}
return TRUE;
}
void
gsm_util_setenv (const char *variable,
const char *value)
{
GError *error = NULL;
if (child_environment == NULL)
child_environment = g_listenv ();
if (!value)
child_environment = g_environ_unsetenv (child_environment, variable);
else
child_environment = g_environ_setenv (child_environment, variable, value, TRUE);
/* If this fails it isn't fatal, it means some things like session
* management and keyring won't work in activated clients.
*/
if (!gsm_util_update_activation_environment (variable, value, &error)) {
g_warning ("Could not make bus activated clients aware of %s=%s environment variable: %s", variable, value, error->message);
g_clear_error (&error);
}
/* If this fails, the system user session won't get the updated environment
*/
if (!gsm_util_update_user_environment (variable, value, &error)) {
g_debug ("Could not make systemd aware of %s=%s environment variable: %s", variable, value, error->message);
g_clear_error (&error);
}
}
const char * const *
gsm_util_listenv (void)
{
return (const char * const *) child_environment;
}
const char * const *
gsm_util_get_variable_blacklist (void)
{
return variable_blacklist;
}

69
gnome-session/gsm-util.h Normal file
View file

@ -0,0 +1,69 @@
/* gsm-util.h
* Copyright (C) 2008 Lucas Rocha.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GSM_UTIL_H__
#define __GSM_UTIL_H__
#include <glib.h>
G_BEGIN_DECLS
#define IS_STRING_EMPTY(x) ((x)==NULL||(x)[0]=='\0')
char * gsm_util_find_desktop_file_for_app_name (const char *app_name,
gboolean look_in_saved_session,
gboolean autostart_first);
gchar *gsm_util_get_empty_tmp_session_dir (void);
const char *gsm_util_get_saved_session_dir (void);
gchar** gsm_util_get_app_dirs (void);
gchar** gsm_util_get_autostart_dirs (void);
void gsm_util_set_autostart_dirs (char **dirs);
gchar ** gsm_util_get_desktop_dirs (gboolean include_saved_session,
gboolean autostart_first);
gboolean gsm_util_text_is_blank (const char *str);
void gsm_util_init_error (gboolean fatal,
const char *format, ...) G_GNUC_PRINTF (2, 3);
char * gsm_util_generate_startup_id (void);
void gsm_util_setenv (const char *variable,
const char *value);
const char * const * gsm_util_listenv (void);
const char * const * gsm_util_get_variable_blacklist(void);
gboolean gsm_util_export_activation_environment (GError **error);
gboolean gsm_util_export_user_environment (GError **error);
gboolean gsm_util_systemd_unit_is_active (const char *unit,
GError **error);
gboolean gsm_util_start_systemd_unit (const char *unit,
const char *mode,
GError **error);
gboolean gsm_util_systemd_reset_failed (GError **error);
void gsm_quit (void);
G_END_DECLS
#endif /* __GSM_UTIL_H__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,89 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2007 Novell, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GSM_XSMP_CLIENT_H__
#define __GSM_XSMP_CLIENT_H__
#include "gsm-client.h"
#include <X11/SM/SMlib.h>
G_BEGIN_DECLS
#define GSM_TYPE_XSMP_CLIENT (gsm_xsmp_client_get_type ())
#define GSM_XSMP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_XSMP_CLIENT, GsmXSMPClient))
#define GSM_XSMP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_XSMP_CLIENT, GsmXSMPClientClass))
#define GSM_IS_XSMP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_XSMP_CLIENT))
#define GSM_IS_XSMP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_XSMP_CLIENT))
#define GSM_XSMP_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_XSMP_CLIENT, GsmXSMPClientClass))
typedef struct _GsmXSMPClient GsmXSMPClient;
typedef struct _GsmXSMPClientClass GsmXSMPClientClass;
typedef struct GsmXSMPClientPrivate GsmXSMPClientPrivate;
struct _GsmXSMPClient
{
GsmClient parent;
GsmXSMPClientPrivate *priv;
};
struct _GsmXSMPClientClass
{
GsmClientClass parent_class;
/* signals */
gboolean (*register_request) (GsmXSMPClient *client,
char **client_id);
void (*register_confirmed) (GsmXSMPClient *client,
const char *client_id);
gboolean (*logout_request) (GsmXSMPClient *client,
gboolean prompt);
void (*saved_state) (GsmXSMPClient *client);
void (*request_phase2) (GsmXSMPClient *client);
void (*request_interaction) (GsmXSMPClient *client);
void (*interaction_done) (GsmXSMPClient *client,
gboolean cancel_shutdown);
void (*save_yourself_done) (GsmXSMPClient *client);
};
GType gsm_xsmp_client_get_type (void) G_GNUC_CONST;
GsmClient *gsm_xsmp_client_new (IceConn ice_conn);
void gsm_xsmp_client_connect (GsmXSMPClient *client,
SmsConn conn,
unsigned long *mask_ret,
SmsCallbacks *callbacks_ret);
void gsm_xsmp_client_save_state (GsmXSMPClient *client);
void gsm_xsmp_client_save_yourself (GsmXSMPClient *client,
gboolean save_state);
void gsm_xsmp_client_save_yourself_phase2 (GsmXSMPClient *client);
void gsm_xsmp_client_interact (GsmXSMPClient *client);
void gsm_xsmp_client_shutdown_cancelled (GsmXSMPClient *client);
G_END_DECLS
#endif /* __GSM_XSMP_CLIENT_H__ */

Some files were not shown because too many files have changed in this diff Show more