├── .dir-locals.el ├── .gitignore ├── .mailmap ├── CODING_STYLE ├── LICENSE ├── README ├── TODO ├── man ├── .gitignore ├── meson.build └── systemadm.xml ├── meson.build ├── meson.version ├── meson_options.txt └── src ├── .gitignore ├── gnome-ask-password-agent.vala ├── meson.build ├── systemadm.appdata.xml ├── systemadm.desktop ├── systemadm.vala ├── systemd-gnome-ask-password-agent.path ├── systemd-gnome-ask-password-agent.rules ├── systemd-gnome-ask-password-agent.service └── systemd-interfaces.vala /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ; Sets emacs variables based on mode. 2 | ; A list of (major-mode . ((var1 . value1) (var2 . value2))) 3 | ; Mode can be nil, which gives default values. 4 | 5 | ((nil . ((indent-tabs-mode . nil) 6 | (tab-width . 8))) 7 | ) 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .deps/ 3 | .libs/ 4 | .dirstamp 5 | Makefile.in 6 | /aclocal.m4 7 | *.cache 8 | /compile 9 | /config.guess 10 | /config.h 11 | /config.h.in 12 | /config.log 13 | /config.status 14 | /config.sub 15 | /configure 16 | /depcomp 17 | /install-sh 18 | /missing 19 | /stamp-* 20 | /*.stamp 21 | Makefile 22 | /*.tar.xz 23 | /systemadm 24 | /systemd-gnome-ask-password-agent 25 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Kay Sievers 2 | Robert Gerus Robert "arachnist" Gerus 3 | Zbigniew Jędrzejewski-Szmek Zbyszek Szmek 4 | Fabiano Fidêncio Fabiano Fidencio 5 | -------------------------------------------------------------------------------- /CODING_STYLE: -------------------------------------------------------------------------------- 1 | 2 | - 8ch indent, no tabs 3 | 4 | - structs in MixedCase, variables, functions in lower_case 5 | 6 | - the destructors always unregister the object from the next bigger 7 | object, not the other way around 8 | 9 | - to minimize strict aliasing violations we prefer unions over casting 10 | 11 | - for robustness reasons destructors should be able to destruct 12 | half-initialized objects, too 13 | 14 | - error codes are returned as negative Exxx. i.e. return -EINVAL. There 15 | are some exceptions: for constructors its is OK to return NULL on 16 | OOM. For lookup functions NULL is fine too for "not found". 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | systemd System and Service Manager UI 2 | 3 | DETAILS: 4 | http://0pointer.de/blog/projects/systemd.html 5 | 6 | WEB SITE: 7 | http://www.freedesktop.org/wiki/Software/systemd 8 | 9 | GIT: 10 | git://anongit.freedesktop.org/systemd/systemd-ui 11 | ssh://git.freedesktop.org/git/systemd/systemd-ui 12 | 13 | GITWEB: 14 | http://cgit.freedesktop.org/systemd/systemd-ui 15 | 16 | MAILING LIST: 17 | http://lists.freedesktop.org/mailman/listinfo/systemd-devel 18 | http://lists.freedesktop.org/mailman/listinfo/systemd-commits 19 | 20 | IRC: 21 | #systemd on irc.freenode.org 22 | 23 | BUG REPORTS: 24 | https://bugs.freedesktop.org/enter_bug.cgi?product=systemd 25 | 26 | AUTHOR: 27 | Lennart Poettering with major support from Kay Sievers 28 | 29 | LICENSE: 30 | GPLv2+ for all code. 31 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/systemd/systemd-ui/d051a327ad61190a24a1668f0002acdc4154f4be/TODO -------------------------------------------------------------------------------- /man/.gitignore: -------------------------------------------------------------------------------- 1 | /*.1 2 | -------------------------------------------------------------------------------- /man/meson.build: -------------------------------------------------------------------------------- 1 | xsltproc_flags = ['--nonet', 2 | '--stringparam', 'funcsynopsis.style', 'ansi', 3 | ] 4 | 5 | xsl_file = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl' 6 | xsltproc_cmdline = [xsltproc, 7 | '-o', '@OUTPUT@', 8 | xsltproc_flags, 9 | xsl_file, 10 | '@INPUT@'] 11 | 12 | foreach manpage : [['systemadm', '1']] 13 | name = manpage[0] + '.' + manpage[1] 14 | custom_target(name, 15 | input: manpage[0] + '.xml', 16 | output: name, 17 | command: xsltproc_cmdline, 18 | install: true, 19 | install_dir : join_paths(get_option('mandir'), 'man' + manpage[1])) 20 | endforeach 21 | -------------------------------------------------------------------------------- /man/systemadm.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 23 | 24 | 25 | 26 | 27 | systemadm 28 | systemd 29 | 30 | 31 | 32 | Developer 33 | Lennart 34 | Poettering 35 | lennart@poettering.net 36 | 37 | 38 | 39 | 40 | 41 | systemadm 42 | 1 43 | 44 | 45 | 46 | systemadm 47 | Graphical frontend for the systemd system 48 | and service manager 49 | 50 | 51 | 52 | 53 | systemadm OPTIONS 54 | 55 | 56 | 57 | 58 | Description 59 | 60 | systemadm is a graphical 61 | frontend for the systemd system and service manager 62 | and allows introspection and control of 63 | systemd. 64 | 65 | 66 | 67 | Options 68 | 69 | The following options are understood: 70 | 71 | 72 | 73 | 74 | 75 | 76 | Prints a short help 77 | text and exits. 78 | 79 | 80 | 81 | 82 | 83 | 84 | Connect to the systemd 85 | system 86 | manager. (Default) 87 | 88 | 89 | 90 | 91 | 92 | Connect to the systemd 93 | manager of the calling 94 | user. 95 | 96 | 97 | 98 | 99 | In addition to this a number of parameters 100 | common to all Gtk+ programs are supported. 101 | 102 | 103 | 104 | See Also 105 | 106 | systemd1, 107 | systemctl1 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: LGPL-2.1-or-later 2 | 3 | project('systemd-ui', 'vala', 'c', 4 | version: files('meson.version'), 5 | license : 'LGPLv2+', 6 | default_options: [ 7 | 'c_std=gnu11', 8 | 'prefix=/usr', 9 | 'warning_level=2', 10 | ], 11 | meson_version : '>= 0.60.0', 12 | ) 13 | 14 | project_major_version = meson.project_version().split('.')[0].split('~')[0] 15 | if meson.project_version().contains('.') 16 | project_minor_version = meson.project_version().split('.')[-1].split('~')[0] 17 | else 18 | project_minor_version = '0' 19 | endif 20 | 21 | prefixdir = get_option('prefix') 22 | datadir = get_option('datadir') 23 | applicationsdir = datadir / 'applications' 24 | appdatadir = datadir / 'appdata' 25 | userunitdir = prefixdir / 'lib/systemd/user' 26 | 27 | polkitrulesdir = datadir / 'polkit-1/rules.d' 28 | 29 | docdir = get_option('docdir') 30 | if docdir == '' 31 | docdir = datadir / 'doc/systemd-ui' 32 | endif 33 | 34 | ##################################################################### 35 | 36 | cc = meson.get_compiler('c') 37 | userspace_c_args = [] 38 | userspace_c_ld_args = [] 39 | 40 | # Those generate many false positives, and we do not want to change the code to 41 | # avoid them. 42 | basic_disabled_warnings = [ 43 | '-Wno-inline', 44 | '-Wno-long-long', 45 | '-Wno-missing-field-initializers', 46 | '-Wno-overlength-strings', 47 | '-Wno-unused-but-set-variable', 48 | '-Wno-unused-parameter', 49 | '-Wno-unused-result', 50 | ] 51 | 52 | possible_common_cc_flags = [ 53 | '-W', 54 | '-Wvla', 55 | '-Wundef', 56 | '-Wformat=2', 57 | '-Wlogical-op', 58 | '-Wsign-compare', 59 | '-Wformat-security', 60 | '-Wmissing-include-dirs', 61 | '-Wformat-nonliteral', 62 | '-Wold-style-definition', 63 | '-Wpointer-arith', 64 | '-Winit-self', 65 | '-Wdeclaration-after-statement', 66 | '-Wfloat-equal', 67 | '-Wmissing-prototypes', 68 | '-Wmissing-declarations', 69 | '-Wmissing-noreturn', 70 | '-Wshadow', 71 | '-Wendif-labels', 72 | '-Wcast-align', 73 | '-Wstrict-aliasing=2', 74 | '-Wwrite-strings', 75 | '-Werror=overflow', 76 | 77 | '-fno-common', 78 | '-fdiagnostics-show-option', 79 | ] 80 | 81 | possible_common_link_flags = [ 82 | '-fstack-protector', 83 | ] 84 | 85 | # --as-needed and --no-undefined are provided by meson by default, 86 | # run 'meson configure' to see what is enabled 87 | possible_link_flags = [ 88 | '-Wl,--fatal-warnings', 89 | '-Wl,-z,now', 90 | '-Wl,-z,relro', 91 | '-Wl,--gc-sections', 92 | ] 93 | 94 | if get_option('b_sanitize') == 'none' 95 | possible_link_flags += '-Wl,--warn-common' 96 | endif 97 | 98 | if cc.get_id() == 'clang' 99 | possible_common_cc_flags += [ 100 | '-Wno-typedef-redefinition', 101 | '-Wno-gnu-variable-sized-type-not-at-end', 102 | ] 103 | endif 104 | 105 | possible_cc_flags = [ 106 | '-fno-strict-aliasing', 107 | '-fstrict-flex-arrays=1', 108 | '-fvisibility=hidden', 109 | '-ffunction-sections', 110 | '-fdata-sections', 111 | ] 112 | 113 | add_project_arguments( 114 | cc.get_supported_arguments( 115 | basic_disabled_warnings, 116 | possible_common_cc_flags 117 | ), 118 | language : 'c') 119 | 120 | add_project_link_arguments( 121 | cc.get_supported_link_arguments(possible_common_link_flags), 122 | language : 'c') 123 | 124 | userspace_c_args += cc.get_supported_arguments(possible_cc_flags) 125 | userspace_c_ld_args += cc.get_supported_link_arguments(possible_link_flags) 126 | 127 | ##################################################################### 128 | # compilation result tests 129 | 130 | have_clock_gettime = cc.has_function('clock_gettime', prefix : '''#include ''', args : '-D_GNU_SOURCE') 131 | if not have_clock_gettime 132 | error('`clock_gettime` not found') 133 | endif 134 | 135 | ##################################################################### 136 | 137 | xsltproc = find_program('xsltproc', required: get_option('man').allowed()) 138 | 139 | ##################################################################### 140 | 141 | dbus = dependency('dbus-1', version: '>= 1.3.2') 142 | glib = dependency('glib-2.0', version: '> 2.26') 143 | gio_unix = dependency('gio-unix-2.0') 144 | gee = dependency('gee-0.8') 145 | gtk3 = dependency('gtk+-3.0') 146 | libnotify = dependency('libnotify') 147 | posix = meson.get_compiler('vala').find_library('posix') 148 | 149 | ##################################################################### 150 | 151 | common_flags = declare_dependency( 152 | compile_args : userspace_c_args, 153 | link_args : userspace_c_ld_args, 154 | ) 155 | 156 | ##################################################################### 157 | 158 | subdir('src') 159 | 160 | ##################################################################### 161 | 162 | if xsltproc.found() 163 | subdir('man') 164 | endif 165 | 166 | install_data('LICENSE', 167 | 'CODING_STYLE', 168 | install_dir : docdir) 169 | -------------------------------------------------------------------------------- /meson.version: -------------------------------------------------------------------------------- 1 | 3 2 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | # -*- mode: meson -*- 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | option('man', type : 'feature', 5 | value : 'disabled', 6 | description : 'build and install man pages') 7 | 8 | option('docdir', type : 'string', 9 | description : 'documentation directory') 10 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | gnome-ask-password-agent.c 3 | systemadm.c 4 | systemd-interfaces.c 5 | wraplabel.c 6 | -------------------------------------------------------------------------------- /src/gnome-ask-password-agent.vala: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of systemd. 3 | 4 | Copyright 2010 Lennart Poettering 5 | 6 | systemd is free software; you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | systemd is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with systemd; If not, see . 18 | ***/ 19 | 20 | using Gtk; 21 | using GLib; 22 | using Posix; 23 | using Notify; 24 | 25 | [CCode (cheader_filename = "time.h")] 26 | extern int clock_gettime(int id, out timespec ts); 27 | 28 | public class PasswordDialog : Dialog { 29 | 30 | public Entry entry; 31 | 32 | public PasswordDialog(string message, string icon) { 33 | set_title("System Password"); 34 | set_border_width(8); 35 | set_default_response(ResponseType.OK); 36 | set_icon_name(icon); 37 | 38 | add_button("_Cancel", ResponseType.CANCEL); 39 | add_button("_OK", ResponseType.OK); 40 | 41 | Container content = (Container) get_content_area(); 42 | 43 | Box hbox = new Box(Orientation.HORIZONTAL, 16); 44 | hbox.set_border_width(8); 45 | content.add(hbox); 46 | 47 | Image image = new Image.from_icon_name(icon, IconSize.DIALOG); 48 | hbox.pack_start(image, false, false); 49 | 50 | Box vbox = new Box(Orientation.VERTICAL, 8); 51 | hbox.pack_start(vbox, true, true); 52 | 53 | Label label = new Label(message); 54 | vbox.pack_start(label, false, false); 55 | 56 | entry = new Entry(); 57 | entry.set_visibility(false); 58 | entry.set_activates_default(true); 59 | vbox.pack_start(entry, false, false); 60 | 61 | entry.activate.connect(on_entry_activated); 62 | 63 | show_all(); 64 | } 65 | 66 | public void on_entry_activated() { 67 | response(ResponseType.OK); 68 | } 69 | } 70 | 71 | public class MyStatusIcon : StatusIcon { 72 | 73 | File directory; 74 | File current; 75 | FileMonitor file_monitor; 76 | 77 | string message; 78 | string icon; 79 | string socket; 80 | 81 | PasswordDialog password_dialog; 82 | Notify.Notification n; 83 | 84 | public MyStatusIcon() throws GLib.Error { 85 | GLib.Object(icon_name : "dialog-password"); 86 | set_title("System Password Request"); 87 | 88 | directory = File.new_for_path("/run/systemd/ask-password/"); 89 | file_monitor = directory.monitor_directory(0); 90 | file_monitor.changed.connect(file_monitor_changed); 91 | 92 | current = null; 93 | look_for_password(); 94 | 95 | activate.connect(status_icon_activate); 96 | } 97 | 98 | void file_monitor_changed(GLib.File file, GLib.File? other_file, GLib.FileMonitorEvent event_type) { 99 | 100 | if (!file.get_basename().has_prefix("ask.")) 101 | return; 102 | 103 | if (event_type == FileMonitorEvent.CREATED || 104 | event_type == FileMonitorEvent.DELETED) { 105 | try { 106 | look_for_password(); 107 | } catch (Error e) { 108 | show_error(e.message); 109 | } 110 | } 111 | } 112 | 113 | void look_for_password() throws GLib.Error { 114 | 115 | if (current != null) { 116 | if (!current.query_exists()) { 117 | current = null; 118 | if (password_dialog != null) 119 | password_dialog.response(ResponseType.REJECT); 120 | } 121 | } 122 | 123 | if (current == null) { 124 | FileEnumerator enumerator = directory.enumerate_children("standard::name", FileQueryInfoFlags.NOFOLLOW_SYMLINKS); 125 | 126 | FileInfo i; 127 | while ((i = enumerator.next_file()) != null) { 128 | if (!i.get_name().has_prefix("ask.")) 129 | continue; 130 | 131 | current = directory.get_child(i.get_name()); 132 | 133 | if (load_password()) 134 | break; 135 | 136 | current = null; 137 | } 138 | } 139 | 140 | if (current == null) 141 | set_visible(false); 142 | } 143 | 144 | bool load_password() throws GLib.Error { 145 | 146 | KeyFile key_file = new KeyFile(); 147 | 148 | try { 149 | timespec ts; 150 | 151 | key_file.load_from_file(current.get_path(), KeyFileFlags.NONE); 152 | 153 | string not_after_as_string = key_file.get_string("Ask", "NotAfter"); 154 | 155 | clock_gettime(1, out ts); 156 | uint64 now = (ts.tv_sec * 1000000) + (ts.tv_nsec / 1000); 157 | 158 | uint64 not_after = uint64.parse(not_after_as_string);; 159 | if ((not_after == 0 && GLib.errno == Posix.EINVAL) || 160 | (not_after == int64.MAX && GLib.errno == Posix.ERANGE)) 161 | return false; 162 | 163 | if (not_after > 0 && not_after < now) 164 | return false; 165 | 166 | socket = key_file.get_string("Ask", "Socket"); 167 | } catch (GLib.Error e) { 168 | return false; 169 | } 170 | 171 | try { 172 | message = key_file.get_string("Ask", "Message").compress(); 173 | } catch (GLib.Error e) { 174 | message = "Please Enter System Password!"; 175 | } 176 | 177 | set_tooltip_text(message); 178 | 179 | try { 180 | icon = key_file.get_string("Ask", "Icon"); 181 | } catch (GLib.Error e) { 182 | icon = "dialog-password"; 183 | } 184 | set_from_icon_name(icon); 185 | 186 | n = new Notify.Notification(title, message, icon); 187 | n.set_timeout(5000); 188 | n.closed.connect(() => { 189 | set_visible(true); 190 | }); 191 | n.add_action("enter_pw", "Enter password", status_icon_activate); 192 | n.show(); 193 | 194 | return true; 195 | } 196 | 197 | void status_icon_activate() { 198 | 199 | if (current == null) 200 | return; 201 | 202 | if (password_dialog != null) { 203 | password_dialog.present(); 204 | return; 205 | } 206 | 207 | password_dialog = new PasswordDialog(message, icon); 208 | 209 | int result = password_dialog.run(); 210 | string password = password_dialog.entry.get_text(); 211 | 212 | password_dialog.destroy(); 213 | password_dialog = null; 214 | 215 | if (result == ResponseType.REJECT || 216 | result == ResponseType.DELETE_EVENT || 217 | result == ResponseType.CANCEL) 218 | return; 219 | 220 | Pid child_pid; 221 | int to_process; 222 | 223 | try { 224 | Process.spawn_async_with_pipes( 225 | null, 226 | { "/usr/bin/pkexec", "/lib/systemd/systemd-reply-password", result == ResponseType.OK ? "1" : "0", socket }, 227 | null, 228 | SpawnFlags.DO_NOT_REAP_CHILD, 229 | null, 230 | out child_pid, 231 | out to_process, 232 | null, 233 | null); 234 | ChildWatch.add(child_pid, (pid, status) => { 235 | Process.close_pid(pid); 236 | }); 237 | 238 | OutputStream stream = new UnixOutputStream(to_process, true); 239 | stream.write(password.data, null); 240 | } catch (Error e) { 241 | show_error(e.message); 242 | } 243 | } 244 | } 245 | 246 | const OptionEntry entries[] = { 247 | { null } 248 | }; 249 | 250 | void show_error(string e) { 251 | Posix.stderr.printf("%s\n", e); 252 | var m = new MessageDialog(null, 0, MessageType.ERROR, ButtonsType.CLOSE, "%s", e); 253 | m.run(); 254 | m.destroy(); 255 | } 256 | 257 | int main(string[] args) { 258 | try { 259 | Gtk.init_with_args(ref args, "[OPTION...]", entries, "systemd-ask-password-agent"); 260 | Notify.init("Password Agent"); 261 | 262 | MyStatusIcon i = new MyStatusIcon(); 263 | Gtk.main(); 264 | } catch (IOError e) { 265 | show_error(e.message); 266 | } catch (GLib.Error e) { 267 | Posix.stderr.printf("%s\n", e.message); 268 | } 269 | 270 | return 0; 271 | } 272 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | systemadm_files = files('systemadm.vala', 2 | 'systemd-interfaces.vala') 3 | systemadm = executable('systemadm', systemadm_files, 4 | dependencies: [common_flags, gtk3, gee, posix], 5 | install: true) 6 | install_data('systemadm.desktop', install_dir: applicationsdir) 7 | install_data('systemadm.appdata.xml', install_dir: appdatadir) 8 | 9 | sgapa_files = files('gnome-ask-password-agent.vala') 10 | sgapa = executable('systemd-gnome-ask-password-agent', sgapa_files, 11 | dependencies: [common_flags, gtk3, gee, gio_unix, libnotify, posix], 12 | install: true) 13 | install_data('systemd-gnome-ask-password-agent.rules', install_dir: polkitrulesdir) 14 | sgapa_units = files('systemd-gnome-ask-password-agent.path', 15 | 'systemd-gnome-ask-password-agent.service') 16 | install_data(sgapa_units, install_dir: userunitdir) 17 | -------------------------------------------------------------------------------- /src/systemadm.appdata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | systemadm.desktop 5 | CC0 6 | 7 |

8 | systemadm is the graphical browser for systemd units. It 9 | can show the list of units, possibly filtered by type. 10 | For individual units, name, description and dependencies 11 | with other units will be shown. 12 |

13 | 14 |

15 | This tool has been largely obsoleted by the commandline 16 | tools like systemctl and systemd-analyze. Bugs are likely 17 | to be present. 18 |

19 |
20 | 21 | http://in.waw.pl/~zbyszek/systemd/systemadm-main-window.png 22 | http://in.waw.pl/~zbyszek/systemd/systemadm-sockets-target.png 23 | 24 | http://www.freedesktop.org/wiki/Software/systemd/ 25 | zbyszek@in.waw.pl 26 |
27 | -------------------------------------------------------------------------------- /src/systemadm.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=systemadm 3 | Comment=Manage Systemd Units 4 | Exec=systemadm 5 | Icon=applications-system 6 | Terminal=false 7 | Type=Application 8 | Categories=System;Settings; 9 | -------------------------------------------------------------------------------- /src/systemadm.vala: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of systemd. 3 | 4 | Copyright 2010 Lennart Poettering 5 | 6 | systemd is free software; you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | systemd is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with systemd; If not, see . 18 | ***/ 19 | 20 | using Gtk; 21 | using GLib; 22 | using Pango; 23 | 24 | static bool user = false; 25 | 26 | public string format_time(uint64 time_ns) { 27 | if (time_ns <= 0) 28 | return ""; 29 | Time timestamp = Time.local((time_t) (time_ns / 1000000)); 30 | return timestamp.format("%a, %d %b %Y %H:%M:%S"); 31 | } 32 | 33 | public void new_column(TreeView view, int column_id, string title) { 34 | TreeViewColumn col; 35 | col = new TreeViewColumn.with_attributes(title, new CellRendererText(), "text", column_id); 36 | col.set_sort_column_id(column_id); 37 | view.insert_column(col, -1); 38 | } 39 | 40 | public class LeftLabel : Label { 41 | public LeftLabel(string? text = null) { 42 | if (text != null) 43 | set_markup("%s".printf(text)); 44 | halign = Align.START; 45 | valign = Align.START; 46 | } 47 | } 48 | 49 | public class RightLabel : Label { 50 | 51 | public RightLabel(string? text = null) { 52 | set_selectable(true); 53 | set_text_or_na(text); 54 | wrap = true; 55 | wrap_mode = Pango.WrapMode.WORD_CHAR; 56 | halign = Align.START; 57 | valign = Align.START; 58 | hexpand = true; 59 | } 60 | 61 | public void set_text_or_na(string? text = null) { 62 | if (text == null || text == "") 63 | set_markup("n/a"); 64 | else 65 | set_text(text); 66 | } 67 | 68 | public void set_markup_or_na(string? text = null) { 69 | if (text == null || text == "") 70 | set_markup("n/a"); 71 | else 72 | set_markup(text); 73 | } 74 | } 75 | 76 | public ScrolledWindow new_scrolled_window(Widget widget) { 77 | ScrolledWindow scroll = new ScrolledWindow(null, null); 78 | scroll.set_policy(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC); 79 | scroll.set_shadow_type(ShadowType.IN); 80 | scroll.add(widget); 81 | return scroll; 82 | } 83 | 84 | public class MainWindow : Window { 85 | 86 | private string? current_unit_id; 87 | private uint32 current_job_id; 88 | 89 | private TreeView unit_view; 90 | private TreeView job_view; 91 | 92 | private Gtk.ListStore unit_model; 93 | private Gtk.ListStore job_model; 94 | 95 | private Gee.HashMap unit_map; 96 | 97 | private Button start_button; 98 | private Button stop_button; 99 | private Button restart_button; 100 | private Button reload_button; 101 | private Button cancel_button; 102 | 103 | private Entry unit_load_entry; 104 | private Button unit_load_button; 105 | 106 | private Button server_snapshot_button; 107 | private Button server_reload_button; 108 | 109 | private Manager manager; 110 | 111 | private RightLabel unit_id_label; 112 | private RightLabel unit_dependency_label; 113 | private RightLabel unit_description_label; 114 | private RightLabel unit_load_state_label; 115 | private RightLabel unit_active_state_label; 116 | private RightLabel unit_sub_state_label; 117 | private RightLabel unit_fragment_path_label; 118 | private RightLabel unit_active_enter_timestamp_label; 119 | private RightLabel unit_active_exit_timestamp_label; 120 | private RightLabel unit_can_start_label; 121 | private RightLabel unit_can_reload_label; 122 | private RightLabel unit_cgroup_label; 123 | 124 | private RightLabel job_id_label; 125 | private RightLabel job_state_label; 126 | private RightLabel job_type_label; 127 | 128 | private ComboBoxText unit_type_combo_box; 129 | private CheckButton inactive_checkbox; 130 | 131 | public MainWindow() throws DBusError, IOError { 132 | title = user ? "systemd User Service Manager" : "systemd System Manager"; 133 | set_position(WindowPosition.CENTER); 134 | set_default_size(1000, 700); 135 | set_border_width(12); 136 | destroy.connect(Gtk.main_quit); 137 | 138 | Notebook notebook = new Notebook(); 139 | add(notebook); 140 | 141 | Box unit_vbox = new Box(Orientation.VERTICAL, 12); 142 | notebook.append_page(unit_vbox, new Label("Units")); 143 | unit_vbox.set_border_width(12); 144 | 145 | Box job_vbox = new Box(Orientation.VERTICAL, 12); 146 | notebook.append_page(job_vbox, new Label("Jobs")); 147 | job_vbox.set_border_width(12); 148 | 149 | unit_type_combo_box = new ComboBoxText(); 150 | Box type_hbox = new Box(Orientation.HORIZONTAL, 6); 151 | type_hbox.pack_start(unit_type_combo_box, false, false, 0); 152 | unit_vbox.pack_start(type_hbox, false, false, 0); 153 | 154 | unit_type_combo_box.append_text("All unit types"); 155 | unit_type_combo_box.append_text("Targets"); 156 | unit_type_combo_box.append_text("Slices"); 157 | unit_type_combo_box.append_text("Scopes"); 158 | unit_type_combo_box.append_text("Services"); 159 | unit_type_combo_box.append_text("Devices"); 160 | unit_type_combo_box.append_text("Mounts"); 161 | unit_type_combo_box.append_text("Automounts"); 162 | unit_type_combo_box.append_text("Swaps"); 163 | unit_type_combo_box.append_text("Sockets"); 164 | unit_type_combo_box.append_text("Paths"); 165 | unit_type_combo_box.append_text("Timers"); 166 | unit_type_combo_box.append_text("Snapshots"); // adjust index in server_on_snapshot 167 | unit_type_combo_box.set_active(0); // Show All 168 | unit_type_combo_box.changed.connect(unit_type_changed); 169 | 170 | inactive_checkbox = new CheckButton.with_label("inactive too"); 171 | inactive_checkbox.toggled.connect(unit_type_changed); 172 | type_hbox.pack_start(inactive_checkbox, false, false, 0); 173 | 174 | unit_load_entry = new Entry(); 175 | unit_load_button = new Button.with_mnemonic("_Load"); 176 | unit_load_button.set_sensitive(false); 177 | 178 | unit_load_entry.changed.connect(on_unit_load_entry_changed); 179 | unit_load_entry.activate.connect(on_unit_load); 180 | unit_load_button.clicked.connect(on_unit_load); 181 | 182 | Box unit_load_hbox = new Box(Orientation.HORIZONTAL, 6); 183 | unit_load_hbox.pack_start(unit_load_entry, false, true, 0); 184 | unit_load_hbox.pack_start(unit_load_button, false, true, 0); 185 | 186 | server_snapshot_button = new Button.with_mnemonic("Take S_napshot"); 187 | server_reload_button = new Button.with_mnemonic("Reload _Configuration"); 188 | 189 | server_snapshot_button.clicked.connect(on_server_snapshot); 190 | server_reload_button.clicked.connect(on_server_reload); 191 | 192 | type_hbox.pack_end(server_snapshot_button, false, true, 0); 193 | type_hbox.pack_end(server_reload_button, false, true, 0); 194 | type_hbox.pack_end(unit_load_hbox, false, true, 24); 195 | 196 | unit_model = new Gtk.ListStore(7, typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(Unit)); 197 | job_model = new Gtk.ListStore(6, typeof(string), typeof(string), typeof(string), typeof(string), typeof(Job), typeof(uint32)); 198 | 199 | unit_map = new Gee.HashMap(); 200 | 201 | TreeModelFilter unit_model_filter; 202 | unit_model_filter = new TreeModelFilter(unit_model, null); 203 | unit_model_filter.set_visible_func(unit_filter); 204 | 205 | TreeModelSort unit_model_sort = new TreeModelSort.with_model(unit_model_filter); 206 | 207 | unit_view = new TreeView.with_model(unit_model_sort); 208 | job_view = new TreeView.with_model(job_model); 209 | 210 | unit_view.cursor_changed.connect(unit_changed); 211 | job_view.cursor_changed.connect(job_changed); 212 | 213 | new_column(unit_view, 2, "Load State"); 214 | new_column(unit_view, 3, "Active State"); 215 | new_column(unit_view, 4, "Unit State"); 216 | new_column(unit_view, 0, "Unit"); 217 | new_column(unit_view, 5, "Job"); 218 | 219 | new_column(job_view, 0, "Job"); 220 | new_column(job_view, 1, "Unit"); 221 | new_column(job_view, 2, "Type"); 222 | new_column(job_view, 3, "State"); 223 | 224 | Paned paned = new Paned(Orientation.VERTICAL); 225 | unit_vbox.pack_start(paned, true, true, 0); 226 | 227 | paned.pack1(new_scrolled_window(unit_view), true, true); 228 | 229 | Box unit_vbox2 = new Box(Orientation.VERTICAL, 12); 230 | paned.pack2(new_scrolled_window(unit_vbox2), false, true); 231 | 232 | paned = new Paned(Orientation.VERTICAL); 233 | job_vbox.pack_start(paned, true, true, 0); 234 | 235 | paned.pack1(new_scrolled_window(job_view), true, true); 236 | 237 | Box job_vbox2 = new Box(Orientation.VERTICAL, 12); 238 | paned.pack2(new_scrolled_window(job_vbox2), false, true); 239 | 240 | unit_id_label = new RightLabel(); 241 | unit_dependency_label = new RightLabel(); 242 | unit_description_label = new RightLabel(); 243 | unit_load_state_label = new RightLabel(); 244 | unit_active_state_label = new RightLabel(); 245 | unit_sub_state_label = new RightLabel(); 246 | unit_fragment_path_label = new RightLabel(); 247 | unit_active_enter_timestamp_label = new RightLabel(); 248 | unit_active_exit_timestamp_label = new RightLabel(); 249 | unit_can_start_label = new RightLabel(); 250 | unit_can_reload_label = new RightLabel(); 251 | unit_cgroup_label = new RightLabel(); 252 | 253 | job_id_label = new RightLabel(); 254 | job_state_label = new RightLabel(); 255 | job_type_label = new RightLabel(); 256 | 257 | unit_dependency_label.set_track_visited_links(false); 258 | unit_dependency_label.set_selectable(true); 259 | unit_dependency_label.activate_link.connect(on_activate_link); 260 | 261 | unit_fragment_path_label.set_track_visited_links(false); 262 | 263 | Grid unit_grid = new Grid(); 264 | unit_grid.column_spacing = 6; 265 | unit_grid.row_spacing = 6; 266 | unit_vbox2.pack_start(unit_grid, false, true, 0); 267 | 268 | Grid job_grid = new Grid(); 269 | job_grid.column_spacing = 6; 270 | job_grid.column_spacing = 6; 271 | job_vbox2.pack_start(job_grid, false, true, 0); 272 | 273 | unit_grid.attach(new LeftLabel("Id:"), 0, 0, 1, 1); 274 | unit_grid.attach(unit_id_label, 1, 0, 5, 1); 275 | unit_grid.attach(new LeftLabel("Description:"), 0, 1, 1, 1); 276 | unit_grid.attach(unit_description_label, 1, 1, 5, 1); 277 | unit_grid.attach(new LeftLabel("Dependencies:"), 0, 2, 1, 1); 278 | unit_grid.attach(unit_dependency_label, 1, 2, 5, 1); 279 | unit_grid.attach(new LeftLabel("Fragment Path:"), 0, 3, 1, 1); 280 | unit_grid.attach(unit_fragment_path_label, 1, 3, 5, 1); 281 | unit_grid.attach(new LeftLabel("Control Group:"), 0, 4, 1, 1); 282 | unit_grid.attach(unit_cgroup_label, 1, 4, 5, 1); 283 | unit_grid.attach(new LeftLabel("Load State:"), 0, 5, 1, 1); 284 | unit_grid.attach(unit_load_state_label, 1, 5, 5, 1); 285 | 286 | unit_grid.attach(new LeftLabel("Active State:"), 0, 6, 1, 1); 287 | unit_grid.attach(unit_active_state_label, 1, 6, 1, 1); 288 | unit_grid.attach(new LeftLabel("Unit State:"), 0, 7, 1, 1); 289 | unit_grid.attach(unit_sub_state_label, 1, 7, 1, 1); 290 | 291 | unit_grid.attach(new LeftLabel("Activated:"), 2, 6, 1, 1); 292 | unit_grid.attach(unit_active_enter_timestamp_label, 3, 6, 1, 1); 293 | unit_grid.attach(new LeftLabel("Deactivated:"), 2, 7, 1, 1); 294 | unit_grid.attach(unit_active_exit_timestamp_label, 3, 7, 1, 1); 295 | 296 | unit_grid.attach(new LeftLabel("Can Start/Stop:"), 4, 6, 1, 1); 297 | unit_grid.attach(unit_can_start_label, 5, 6, 1, 1); 298 | unit_grid.attach(new LeftLabel("Can Reload:"), 4, 7, 1, 1); 299 | unit_grid.attach(unit_can_reload_label, 5, 7, 1, 1); 300 | 301 | job_grid.attach(new LeftLabel("Id:"), 0, 1, 1, 1); 302 | job_grid.attach(job_id_label, 1, 1, 1, 1); 303 | job_grid.attach(new LeftLabel("State:"), 0, 2, 1, 1); 304 | job_grid.attach(job_state_label, 1, 2, 1, 1); 305 | job_grid.attach(new LeftLabel("Type:"), 0, 3, 1, 1); 306 | job_grid.attach(job_type_label, 1, 3, 1, 1); 307 | 308 | ButtonBox bbox = new ButtonBox(Orientation.HORIZONTAL); 309 | bbox.set_layout(ButtonBoxStyle.START); 310 | bbox.set_spacing(6); 311 | unit_vbox2.pack_start(bbox, false, true, 0); 312 | 313 | start_button = new Button.with_mnemonic("_Start"); 314 | stop_button = new Button.with_mnemonic("Sto_p"); 315 | reload_button = new Button.with_mnemonic("_Reload"); 316 | restart_button = new Button.with_mnemonic("Res_tart"); 317 | 318 | start_button.clicked.connect(on_start); 319 | stop_button.clicked.connect(on_stop); 320 | reload_button.clicked.connect(on_reload); 321 | restart_button.clicked.connect(on_restart); 322 | 323 | bbox.pack_start(start_button, false, true, 0); 324 | bbox.pack_start(stop_button, false, true, 0); 325 | bbox.pack_start(restart_button, false, true, 0); 326 | bbox.pack_start(reload_button, false, true, 0); 327 | 328 | bbox = new ButtonBox(Orientation.HORIZONTAL); 329 | bbox.set_layout(ButtonBoxStyle.START); 330 | bbox.set_spacing(6); 331 | job_vbox2.pack_start(bbox, false, true, 0); 332 | 333 | cancel_button = new Button.with_mnemonic("_Cancel"); 334 | 335 | cancel_button.clicked.connect(on_cancel); 336 | 337 | bbox.pack_start(cancel_button, false, true, 0); 338 | 339 | manager = Bus.get_proxy_sync( 340 | user ? BusType.SESSION : BusType.SYSTEM, 341 | "org.freedesktop.systemd1", 342 | "/org/freedesktop/systemd1"); 343 | 344 | manager.unit_new.connect(on_unit_new); 345 | manager.job_new.connect(on_job_new); 346 | manager.unit_removed.connect(on_unit_removed); 347 | manager.job_removed.connect(on_job_removed); 348 | 349 | manager.subscribe(); 350 | 351 | clear_unit(); 352 | clear_job(); 353 | populate_unit_model(); 354 | populate_job_model(); 355 | } 356 | 357 | public void populate_unit_model() throws DBusError, IOError { 358 | unit_model.clear(); 359 | 360 | var list = manager.list_units(); 361 | 362 | foreach (var i in list) { 363 | TreeIter iter; 364 | 365 | Properties p = Bus.get_proxy_sync( 366 | user ? BusType.SESSION : BusType.SYSTEM, 367 | "org.freedesktop.systemd1", 368 | i.unit_path); 369 | 370 | p.properties_changed.connect(on_unit_changed); 371 | 372 | Unit u = Bus.get_proxy_sync( 373 | user ? BusType.SESSION : BusType.SYSTEM, 374 | "org.freedesktop.systemd1", 375 | i.unit_path); 376 | 377 | unit_map[i.id] = u; 378 | 379 | unit_model.append(out iter); 380 | unit_model.set(iter, 381 | 0, i.id, 382 | 1, i.description, 383 | 2, i.load_state, 384 | 3, i.active_state, 385 | 4, i.sub_state, 386 | 5, i.job_type != "" ? "→ %s".printf(i.job_type) : "", 387 | 6, u); 388 | } 389 | } 390 | 391 | public void populate_job_model() throws DBusError, IOError { 392 | job_model.clear(); 393 | 394 | var list = manager.list_jobs(); 395 | 396 | foreach (var i in list) { 397 | TreeIter iter; 398 | 399 | Properties p = Bus.get_proxy_sync( 400 | user ? BusType.SESSION : BusType.SYSTEM, 401 | "org.freedesktop.systemd1", 402 | i.job_path); 403 | 404 | p.properties_changed.connect(on_job_changed); 405 | 406 | Job j = Bus.get_proxy_sync( 407 | user ? BusType.SESSION : BusType.SYSTEM, 408 | "org.freedesktop.systemd1", 409 | i.job_path); 410 | 411 | job_model.append(out iter); 412 | job_model.set(iter, 413 | 0, "%u".printf(i.id), 414 | 1, i.name, 415 | 2, "→ %s".printf(i.type), 416 | 3, i.state, 417 | 4, j, 418 | 5, i.id); 419 | } 420 | } 421 | 422 | public Unit? get_current_unit() { 423 | TreePath p; 424 | unit_view.get_cursor(out p, null); 425 | 426 | if (p == null) 427 | return null; 428 | 429 | TreeModel model = unit_view.get_model(); 430 | TreeIter iter; 431 | Unit u; 432 | 433 | model.get_iter(out iter, p); 434 | model.get(iter, 6, out u); 435 | 436 | return u; 437 | } 438 | 439 | public Unit? get_unit(string id) { 440 | return this.unit_map[id]; 441 | } 442 | 443 | public void unit_changed() { 444 | Unit u = get_current_unit(); 445 | 446 | if (u == null) 447 | clear_unit(); 448 | else 449 | show_unit(u); 450 | } 451 | 452 | public void clear_unit() { 453 | current_unit_id = null; 454 | 455 | start_button.set_sensitive(false); 456 | stop_button.set_sensitive(false); 457 | reload_button.set_sensitive(false); 458 | restart_button.set_sensitive(false); 459 | 460 | unit_id_label.set_text_or_na(); 461 | unit_description_label.set_text_or_na(); 462 | unit_description_label.set_text_or_na(); 463 | unit_load_state_label.set_text_or_na(); 464 | unit_active_state_label.set_text_or_na(); 465 | unit_sub_state_label.set_text_or_na(); 466 | unit_fragment_path_label.set_text_or_na(); 467 | unit_active_enter_timestamp_label.set_text_or_na(); 468 | unit_active_exit_timestamp_label.set_text_or_na(); 469 | unit_can_reload_label.set_text_or_na(); 470 | unit_can_start_label.set_text_or_na(); 471 | unit_cgroup_label.set_text_or_na(); 472 | } 473 | 474 | public string format_unit_link(string i, bool link) { 475 | Unit? u = get_unit(i); 476 | if(u == null) 477 | return "" + i + "" 491 | + i + "(" + 492 | u.sub_state + ")" + ""; 493 | if(link) 494 | return " " + span + ""; 495 | else 496 | return span; 497 | } 498 | 499 | 500 | public string make_dependency_string(string? prefix, string word, string[] dependencies) { 501 | Gee.Collection sorted = new Gee.TreeSet(); 502 | foreach (string i in dependencies) 503 | sorted.add(i); 504 | 505 | bool first = true; 506 | string r; 507 | 508 | if (prefix == null) 509 | r = ""; 510 | else 511 | r = prefix; 512 | 513 | foreach (string i in sorted) { 514 | if (r != "") 515 | r += first ? "\n" : ","; 516 | 517 | if (first) { 518 | r += "" + word + ":"; 519 | first = false; 520 | } 521 | 522 | r += format_unit_link(i, true); 523 | } 524 | 525 | return r; 526 | } 527 | 528 | public void show_unit(Unit unit) { 529 | current_unit_id = unit.id; 530 | 531 | string id_display = format_unit_link(current_unit_id, false); 532 | bool has_alias = false; 533 | foreach (string i in unit.names) { 534 | if (i == current_unit_id) 535 | continue; 536 | 537 | if (!has_alias) { 538 | id_display += " (aliases:"; 539 | has_alias = true; 540 | } 541 | 542 | id_display += " " + i; 543 | } 544 | if(has_alias) 545 | id_display += ")"; 546 | 547 | unit_id_label.set_markup_or_na(id_display); 548 | 549 | string[] 550 | requires = unit.requires, 551 | requires_overridable = unit.requires_overridable, 552 | requisite = unit.requisite, 553 | requisite_overridable = unit.requisite_overridable, 554 | wants = unit.wants, 555 | required_by = unit.required_by, 556 | required_by_overridable = unit.required_by_overridable, 557 | wanted_by = unit.wanted_by, 558 | conflicts = unit.conflicts, 559 | before = unit.before, 560 | after = unit.after; 561 | 562 | unit_dependency_label.set_markup_or_na( 563 | make_dependency_string( 564 | make_dependency_string( 565 | make_dependency_string( 566 | make_dependency_string( 567 | make_dependency_string( 568 | make_dependency_string( 569 | make_dependency_string( 570 | make_dependency_string( 571 | make_dependency_string( 572 | make_dependency_string( 573 | make_dependency_string(null, 574 | "requires", requires), 575 | "overridable requires", requires_overridable), 576 | "requisite", requisite), 577 | "overridable requisite", requisite_overridable), 578 | "wants", wants), 579 | "conflicts", conflicts), 580 | "required by", required_by), 581 | "overridable required by", required_by_overridable), 582 | "wanted by", wanted_by), 583 | "after", after), 584 | "before", before)); 585 | 586 | unit_description_label.set_text_or_na(unit.description); 587 | unit_load_state_label.set_text_or_na(unit.load_state); 588 | unit_active_state_label.set_text_or_na(unit.active_state); 589 | unit_sub_state_label.set_text_or_na(unit.sub_state); 590 | 591 | string fp = unit.fragment_path; 592 | if (fp != "") 593 | unit_fragment_path_label.set_markup_or_na( 594 | "" + 595 | "" + fp + ""); 596 | else 597 | unit_fragment_path_label.set_text_or_na(); 598 | 599 | 600 | unit_active_enter_timestamp_label.set_text_or_na(format_time(unit.active_enter_timestamp)); 601 | 602 | unit_active_exit_timestamp_label.set_text_or_na(format_time(unit.active_exit_timestamp)); 603 | 604 | bool b = unit.can_start; 605 | start_button.set_sensitive(b); 606 | stop_button.set_sensitive(b); 607 | restart_button.set_sensitive(b); 608 | unit_can_start_label.set_text_or_na(b ? "Yes" : "No"); 609 | 610 | b = unit.can_reload; 611 | reload_button.set_sensitive(b); 612 | unit_can_reload_label.set_text_or_na(b ? "Yes" : "No"); 613 | 614 | unit_cgroup_label.set_text_or_na(unit.default_control_group); 615 | } 616 | 617 | public Job? get_current_job() { 618 | TreePath p; 619 | job_view.get_cursor(out p, null); 620 | 621 | if (p == null) 622 | return null; 623 | 624 | TreeIter iter; 625 | TreeModel model = job_view.get_model(); 626 | Job *j; 627 | 628 | model.get_iter(out iter, p); 629 | model.get(iter, 4, out j); 630 | 631 | return j; 632 | } 633 | 634 | public void job_changed() { 635 | Job j = get_current_job(); 636 | 637 | if (j == null) 638 | clear_job(); 639 | else 640 | show_job(j); 641 | } 642 | 643 | public void clear_job() { 644 | current_job_id = 0; 645 | 646 | job_id_label.set_text_or_na(); 647 | job_state_label.set_text_or_na(); 648 | job_type_label.set_text_or_na(); 649 | 650 | cancel_button.set_sensitive(false); 651 | } 652 | 653 | public void show_job(Job job) { 654 | current_job_id = job.id; 655 | 656 | job_id_label.set_text_or_na("%u".printf(current_job_id)); 657 | job_state_label.set_text_or_na(job.state); 658 | job_type_label.set_text_or_na(job.job_type); 659 | 660 | cancel_button.set_sensitive(true); 661 | } 662 | 663 | public void on_start() { 664 | Unit u = get_current_unit(); 665 | 666 | if (u == null) 667 | return; 668 | 669 | try { 670 | u.start("replace"); 671 | } catch (Error e) { 672 | show_error(e); 673 | } 674 | } 675 | 676 | public void on_stop() { 677 | Unit u = get_current_unit(); 678 | 679 | if (u == null) 680 | return; 681 | 682 | try { 683 | u.stop("replace"); 684 | } catch (Error e) { 685 | show_error(e); 686 | } 687 | } 688 | 689 | public void on_reload() { 690 | Unit u = get_current_unit(); 691 | 692 | if (u == null) 693 | return; 694 | 695 | try { 696 | u.reload("replace"); 697 | } catch (Error e) { 698 | show_error(e); 699 | } 700 | } 701 | 702 | public void on_restart() { 703 | Unit u = get_current_unit(); 704 | 705 | if (u == null) 706 | return; 707 | 708 | try { 709 | u.restart("replace"); 710 | } catch (Error e) { 711 | show_error(e); 712 | } 713 | } 714 | 715 | public void on_cancel() { 716 | Job j = get_current_job(); 717 | 718 | if (j == null) 719 | return; 720 | 721 | try { 722 | j.cancel(); 723 | } catch (Error e) { 724 | show_error(e); 725 | } 726 | } 727 | 728 | public void update_unit_iter(TreeIter iter, string id, Unit u) { 729 | 730 | try { 731 | string t = ""; 732 | Unit.JobLink jl = u.job; 733 | 734 | if (jl.id != 0) { 735 | Job j = Bus.get_proxy_sync( 736 | user ? BusType.SESSION : BusType.SYSTEM, 737 | "org.freedesktop.systemd1", 738 | jl.path); 739 | 740 | t = j.job_type; 741 | } 742 | 743 | unit_model.set(iter, 744 | 0, id, 745 | 1, u.description, 746 | 2, u.load_state, 747 | 3, u.active_state, 748 | 4, u.sub_state, 749 | 5, t != "" ? "→ %s".printf(t) : "", 750 | 6, u); 751 | } catch (Error e) { 752 | show_error(e); 753 | } 754 | } 755 | 756 | public void on_unit_new(string id, ObjectPath path) { 757 | try { 758 | 759 | Properties p = Bus.get_proxy_sync( 760 | user ? BusType.SESSION : BusType.SYSTEM, 761 | "org.freedesktop.systemd1", 762 | path); 763 | 764 | p.properties_changed.connect(on_unit_changed); 765 | 766 | TreeIter iter; 767 | unit_model.append(out iter); 768 | 769 | Unit u = Bus.get_proxy_sync( 770 | user ? BusType.SESSION : BusType.SYSTEM, 771 | "org.freedesktop.systemd1", 772 | path); 773 | 774 | unit_map[id] = u; 775 | 776 | update_unit_iter(iter, id, u); 777 | } catch (Error e) { 778 | show_error(e); 779 | } 780 | } 781 | 782 | public void update_job_iter(TreeIter iter, uint32 id, Job j) { 783 | job_model.set(iter, 784 | 0, "%u".printf(id), 785 | 1, j.unit.id, 786 | 2, "→ %s".printf(j.job_type), 787 | 3, j.state, 788 | 4, j, 789 | 5, id); 790 | } 791 | 792 | public void on_job_new(uint32 id, ObjectPath path) { 793 | 794 | try { 795 | 796 | Properties p = Bus.get_proxy_sync( 797 | user ? BusType.SESSION : BusType.SYSTEM, 798 | "org.freedesktop.systemd1", 799 | path); 800 | 801 | p.properties_changed.connect(on_job_changed); 802 | 803 | TreeIter iter; 804 | job_model.append(out iter); 805 | 806 | Job j = Bus.get_proxy_sync( 807 | user ? BusType.SESSION : BusType.SYSTEM, 808 | "org.freedesktop.systemd1", 809 | path); 810 | 811 | update_job_iter(iter, id, j); 812 | 813 | } catch (Error e) { 814 | show_error(e); 815 | } 816 | } 817 | 818 | public void on_unit_removed(string id, ObjectPath path) { 819 | TreeIter iter; 820 | if (!(unit_model.get_iter_first(out iter))) 821 | return; 822 | 823 | do { 824 | string name; 825 | 826 | unit_model.get(iter, 0, out name); 827 | 828 | if (id == name) { 829 | if (current_unit_id == name) 830 | clear_unit(); 831 | 832 | unit_model.remove(ref iter); 833 | break; 834 | } 835 | 836 | } while (unit_model.iter_next(ref iter)); 837 | 838 | unit_map.unset(id); 839 | } 840 | 841 | public void on_job_removed(uint32 id, ObjectPath path, string res) { 842 | TreeIter iter; 843 | if (!(job_model.get_iter_first(out iter))) 844 | return; 845 | 846 | do { 847 | uint32 j; 848 | 849 | job_model.get(iter, 5, out j); 850 | 851 | if (id == j) { 852 | if (current_job_id == j) 853 | clear_job(); 854 | 855 | job_model.remove(ref iter); 856 | 857 | break; 858 | } 859 | 860 | } while (job_model.iter_next(ref iter)); 861 | } 862 | 863 | public void on_unit_changed(Properties p, string iface, HashTable changed_properties, string[] invalidated_properties) { 864 | 865 | try { 866 | TreeIter iter; 867 | string id; 868 | 869 | Unit u = Bus.get_proxy_sync( 870 | user ? BusType.SESSION : BusType.SYSTEM, 871 | p.get_name(), 872 | p.get_object_path()); 873 | 874 | if (!(unit_model.get_iter_first(out iter))) 875 | return; 876 | 877 | id = u.id; 878 | 879 | do { 880 | string name; 881 | 882 | unit_model.get(iter, 0, out name); 883 | 884 | if (id == name) { 885 | update_unit_iter(iter, id, u); 886 | 887 | if (current_unit_id == id) 888 | show_unit(u); 889 | 890 | break; 891 | } 892 | 893 | } while (unit_model.iter_next(ref iter)); 894 | 895 | } catch (Error e) { 896 | show_error(e); 897 | } 898 | } 899 | 900 | public void on_job_changed(Properties p, string iface, HashTable changed_properties, string[] invalidated_properties) { 901 | try { 902 | TreeIter iter; 903 | uint32 id; 904 | 905 | Job j = Bus.get_proxy_sync( 906 | user ? BusType.SESSION : BusType.SYSTEM, 907 | p.get_name(), 908 | p.get_object_path()); 909 | 910 | if (!(job_model.get_iter_first(out iter))) 911 | return; 912 | 913 | id = j.id; 914 | 915 | do { 916 | uint32 k; 917 | 918 | job_model.get(iter, 5, out k); 919 | 920 | if (id == k) { 921 | update_job_iter(iter, id, j); 922 | 923 | if (current_job_id == id) 924 | show_job(j); 925 | 926 | break; 927 | } 928 | 929 | } while (job_model.iter_next(ref iter)); 930 | 931 | } catch (Error e) { 932 | show_error(e); 933 | } 934 | } 935 | 936 | public bool unit_filter(TreeModel model, TreeIter iter) { 937 | string id, active_state, job; 938 | 939 | model.get(iter, 0, out id, 3, out active_state, 5, out job); 940 | 941 | if (id == null) 942 | return false; 943 | 944 | if (!inactive_checkbox.get_active() 945 | && active_state == "inactive" && job == "") 946 | return false; 947 | 948 | switch (unit_type_combo_box.get_active()) { 949 | case 0: 950 | return true; 951 | case 1: 952 | return id.has_suffix(".target"); 953 | case 2: 954 | return id.has_suffix(".slice"); 955 | case 3: 956 | return id.has_suffix(".scope"); 957 | case 4: 958 | return id.has_suffix(".service"); 959 | case 5: 960 | return id.has_suffix(".device"); 961 | case 6: 962 | return id.has_suffix(".mount"); 963 | case 7: 964 | return id.has_suffix(".automount"); 965 | case 8: 966 | return id.has_suffix(".swap"); 967 | case 9: 968 | return id.has_suffix(".socket"); 969 | case 10: 970 | return id.has_suffix(".path"); 971 | case 11: 972 | return id.has_suffix(".timer"); 973 | case 12: 974 | return id.has_suffix(".snapshot"); 975 | default: 976 | assert(false); 977 | return false; 978 | } 979 | } 980 | 981 | public void unit_type_changed() { 982 | TreeModelFilter model = (TreeModelFilter) ((TreeModelSort) unit_view.get_model()).get_model(); 983 | 984 | model.refilter(); 985 | } 986 | 987 | public void on_server_reload() { 988 | try { 989 | manager.reload(); 990 | } catch (Error e) { 991 | show_error(e); 992 | } 993 | } 994 | 995 | public void on_server_snapshot() { 996 | try { 997 | manager.create_snapshot(); 998 | 999 | if (unit_type_combo_box.get_active() != 0) 1000 | unit_type_combo_box.set_active(12); 1001 | } catch (Error e) { 1002 | show_error(e); 1003 | } 1004 | } 1005 | 1006 | public void on_unit_load() { 1007 | string t = unit_load_entry.get_text(); 1008 | 1009 | if (t == "") 1010 | return; 1011 | 1012 | try { 1013 | var path = manager.load_unit(t); 1014 | 1015 | Unit u = Bus.get_proxy_sync( 1016 | user ? BusType.SESSION : BusType.SYSTEM, 1017 | "org.freedesktop.systemd1", 1018 | path); 1019 | 1020 | var m = new MessageDialog(this, 1021 | DialogFlags.DESTROY_WITH_PARENT, 1022 | MessageType.INFO, 1023 | ButtonsType.CLOSE, 1024 | "Unit available as id %s", u.id); 1025 | m.title = "Unit"; 1026 | m.run(); 1027 | m.destroy(); 1028 | 1029 | show_unit(u); 1030 | } catch (Error e) { 1031 | show_error(e); 1032 | } 1033 | } 1034 | 1035 | public void on_unit_load_entry_changed() { 1036 | unit_load_button.set_sensitive(unit_load_entry.get_text() != ""); 1037 | } 1038 | 1039 | public bool on_activate_link(string uri) { 1040 | 1041 | try { 1042 | string path = manager.get_unit(uri); 1043 | 1044 | Unit u = Bus.get_proxy_sync( 1045 | user ? BusType.SESSION : BusType.SYSTEM, 1046 | "org.freedesktop.systemd1", 1047 | path); 1048 | 1049 | show_unit(u); 1050 | } catch (Error e) { 1051 | show_error(e); 1052 | } 1053 | 1054 | return true; 1055 | } 1056 | 1057 | public void show_error(Error e) { 1058 | string title, message; 1059 | if (e is DBusError && DBusError.is_remote_error(e)) { 1060 | string remote = DBusError.get_remote_error(e); 1061 | title = "Remote DBus Error: %s".printf(remote); 1062 | message = strerror(dbus_error_to_errno(remote)); 1063 | } else { 1064 | title = "Error"; 1065 | message = "Error"; 1066 | } 1067 | 1068 | var m = new MessageDialog(this, 1069 | DialogFlags.DESTROY_WITH_PARENT, 1070 | MessageType.ERROR, 1071 | ButtonsType.CLOSE, "%s", message); 1072 | m.title = title; 1073 | m.format_secondary_text("%s", e.message); 1074 | m.run(); 1075 | m.destroy(); 1076 | } 1077 | } 1078 | 1079 | int dbus_error_to_errno(string error) { 1080 | switch (error) { 1081 | case "org.freedesktop.DBus.Error.Failed": 1082 | return Posix.EACCES; 1083 | case "org.freedesktop.DBus.Error.NoMemory": 1084 | return Posix.ENOMEM; 1085 | case "org.freedesktop.DBus.Error.ServiceUnknown": 1086 | return Posix.EHOSTUNREACH; 1087 | case "org.freedesktop.DBus.Error.NameHasNoOwner": 1088 | return Posix.ENXIO; 1089 | case "org.freedesktop.DBus.Error.NoReply": 1090 | return Posix.ETIMEDOUT; 1091 | case "org.freedesktop.DBus.Error.IOError": 1092 | return Posix.EIO; 1093 | case "org.freedesktop.DBus.Error.BadAddress": 1094 | return Posix.EADDRNOTAVAIL; 1095 | case "org.freedesktop.DBus.Error.NotSupported": 1096 | return Posix.ENOTSUP; 1097 | case "org.freedesktop.DBus.Error.LimitsExceeded": 1098 | return Posix.ENOBUFS; 1099 | case "org.freedesktop.DBus.Error.AccessDenied": 1100 | return Posix.EACCES; 1101 | case "org.freedesktop.DBus.Error.AuthFailed": 1102 | return Posix.EACCES; 1103 | case "org.freedesktop.DBus.Error.NoServer": 1104 | /* Errno missing from Posix */ 1105 | /* return Posix.EHOSTDOWN; */ 1106 | return Posix.EIO; 1107 | case "org.freedesktop.DBus.Error.Timeout": 1108 | return Posix.ETIMEDOUT; 1109 | case "org.freedesktop.DBus.Error.NoNetwork": 1110 | /* Errno missing from Posix */ 1111 | /* return Posix.ENONET; */ 1112 | return Posix.EIO; 1113 | case "org.freedesktop.DBus.Error.AddressInUse": 1114 | return Posix.EADDRINUSE; 1115 | case "org.freedesktop.DBus.Error.Disconnected": 1116 | return Posix.ECONNRESET; 1117 | case "org.freedesktop.DBus.Error.InvalidArgs": 1118 | return Posix.EINVAL; 1119 | case "org.freedesktop.DBus.Error.FileNotFound": 1120 | return Posix.ENOENT; 1121 | case "org.freedesktop.DBus.Error.FileExists": 1122 | return Posix.EEXIST; 1123 | case "org.freedesktop.DBus.Error.UnknownMethod": 1124 | case "org.freedesktop.DBus.Error.UnknownObject": 1125 | case "org.freedesktop.DBus.Error.UnknownInterface": 1126 | case "org.freedesktop.DBus.Error.UnknownProperty": 1127 | /* Errno missing from Posix */ 1128 | /* return Posix.EBADR; */ 1129 | return Posix.EIO; 1130 | case "org.freedesktop.DBus.Error.PropertyReadOnly": 1131 | return Posix.EROFS; 1132 | case "org.freedesktop.DBus.Error.UnixProcessIdUnknown": 1133 | return Posix.ESRCH; 1134 | case "org.freedesktop.DBus.Error.InvalidSignature": 1135 | return Posix.EINVAL; 1136 | case "org.freedesktop.DBus.Error.InconsistentMessage": 1137 | return Posix.EBADMSG; 1138 | case "org.freedesktop.DBus.Error.TimedOut": 1139 | return Posix.ETIMEDOUT; 1140 | case "org.freedesktop.DBus.Error.MatchRuleInvalid": 1141 | case "org.freedesktop.DBus.Error.InvalidFileContent": 1142 | return Posix.EINVAL; 1143 | case "org.freedesktop.DBus.Error.MatchRuleNotFound": 1144 | return Posix.ENOENT; 1145 | case "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown": 1146 | return Posix.ESRCH; 1147 | case "org.freedesktop.DBus.Error.ObjectPathInUse": 1148 | return Posix.EBUSY; 1149 | default: 1150 | return Posix.EIO; 1151 | } 1152 | } 1153 | 1154 | const OptionEntry entries[] = { 1155 | { "user", 0, 0, OptionArg.NONE, out user, "Connect to user service manager", null }, 1156 | { "system", 0, OptionFlags.REVERSE, OptionArg.NONE, out user, "Connect to system manager", null }, 1157 | { null } 1158 | }; 1159 | 1160 | void show_error(Error e) { 1161 | var m = new MessageDialog(null, 0, MessageType.ERROR, ButtonsType.CLOSE, "%s", e.message); 1162 | m.run(); 1163 | m.destroy(); 1164 | } 1165 | 1166 | int main(string[] args) { 1167 | 1168 | try { 1169 | Gtk.init_with_args(ref args, "[OPTION...]", entries, "systemadm"); 1170 | 1171 | MainWindow window = new MainWindow(); 1172 | window.show_all(); 1173 | 1174 | Gtk.main(); 1175 | } catch (IOError e) { 1176 | show_error(e); 1177 | } catch (GLib.Error e) { 1178 | stderr.printf("%s\n", e.message); 1179 | } 1180 | 1181 | return 0; 1182 | } 1183 | -------------------------------------------------------------------------------- /src/systemd-gnome-ask-password-agent.path: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: LGPL-2.1-or-later 2 | # 3 | # This file is part of systemd. 4 | # 5 | # systemd is free software; you can redistribute it and/or modify it 6 | # under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation; either version 2.1 of the License, or 8 | # (at your option) any later version. 9 | 10 | [Unit] 11 | Description=Forward Password Requests to GNOME Password Agent Directory Watch 12 | 13 | DefaultDependencies=no 14 | Before=paths.target cryptsetup.target 15 | Conflicts=emergency.service 16 | Before=emergency.service 17 | Conflicts=shutdown.target 18 | Before=shutdown.target 19 | 20 | [Path] 21 | DirectoryNotEmpty=/run/systemd/ask-password 22 | MakeDirectory=yes 23 | -------------------------------------------------------------------------------- /src/systemd-gnome-ask-password-agent.rules: -------------------------------------------------------------------------------- 1 | /** 2 | * Polkit permissions for systemd-gnome-ask-password-agent. 3 | */ 4 | polkit.addRule(function(action, subject) { 5 | // Allow the `wheel` group to run `systemd-reply-password` without passwords. 6 | if (action.id == "org.freedesktop.policykit.exec" && 7 | action.lookup("program") == "/usr/lib/systemd/systemd-reply-password" && 8 | subject.isInGroup("wheel")) { 9 | return polkit.Result.YES; 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /src/systemd-gnome-ask-password-agent.service: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: LGPL-2.1-or-later 2 | # 3 | # This file is part of systemd. 4 | # 5 | # systemd is free software; you can redistribute it and/or modify it 6 | # under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation; either version 2.1 of the License, or 8 | # (at your option) any later version. 9 | 10 | [Unit] 11 | Description=Forward Password Requests to GNOME Password Agent 12 | After=systemd-user-sessions.service 13 | 14 | [Service] 15 | ExecStart=systemd-gnome-ask-password-agent 16 | -------------------------------------------------------------------------------- /src/systemd-interfaces.vala: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of systemd. 3 | 4 | Copyright 2010 Lennart Poettering 5 | 6 | systemd is free software; you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | systemd is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with systemd; If not, see . 18 | ***/ 19 | 20 | [DBus (name = "org.freedesktop.systemd1.Manager")] 21 | public interface Manager : DBusProxy { 22 | 23 | public struct UnitInfo { 24 | string id; 25 | string description; 26 | string load_state; 27 | string active_state; 28 | string sub_state; 29 | string following; 30 | ObjectPath unit_path; 31 | uint32 job_id; 32 | string job_type; 33 | ObjectPath job_path; 34 | } 35 | 36 | public struct JobInfo { 37 | uint32 id; 38 | string name; 39 | string type; 40 | string state; 41 | ObjectPath job_path; 42 | ObjectPath unit_path; 43 | } 44 | 45 | public abstract string[] environment { owned get; } 46 | 47 | public abstract UnitInfo[] list_units() throws DBusError, IOError; 48 | public abstract JobInfo[] list_jobs() throws DBusError, IOError; 49 | 50 | public abstract ObjectPath get_unit(string name) throws DBusError, IOError; 51 | public abstract ObjectPath get_unit_by_pid(uint32 pid) throws DBusError, IOError; 52 | public abstract ObjectPath load_unit(string name) throws DBusError, IOError; 53 | public abstract ObjectPath get_job(uint32 id) throws DBusError, IOError; 54 | 55 | public abstract ObjectPath start_unit(string name, string mode = "replace") throws DBusError, IOError; 56 | public abstract ObjectPath stop_unit(string name, string mode = "replace") throws DBusError, IOError; 57 | public abstract ObjectPath reload_unit(string name, string mode = "replace") throws DBusError, IOError; 58 | public abstract ObjectPath restart_unit(string name, string mode = "replace") throws DBusError, IOError; 59 | public abstract ObjectPath try_restart_unit(string name, string mode = "replace") throws DBusError, IOError; 60 | public abstract ObjectPath reload_or_restart_unit(string name, string mode = "replace") throws DBusError, IOError; 61 | public abstract ObjectPath reload_or_try_restart_unit(string name, string mode = "replace") throws DBusError, IOError; 62 | 63 | public abstract void reset_failed_unit(string name = "") throws DBusError, IOError; 64 | 65 | public abstract void clear_jobs() throws DBusError, IOError; 66 | 67 | public abstract void subscribe() throws DBusError, IOError; 68 | public abstract void unsubscribe() throws DBusError, IOError; 69 | 70 | public abstract string dump() throws DBusError, IOError; 71 | 72 | public abstract void reload() throws DBusError, IOError; 73 | public abstract void reexecute() throws DBusError, IOError; 74 | public abstract void exit() throws DBusError, IOError; 75 | public abstract void halt() throws DBusError, IOError; 76 | public abstract void power_off() throws DBusError, IOError; 77 | public abstract void reboot() throws DBusError, IOError; 78 | public abstract void kexec() throws DBusError, IOError; 79 | 80 | public abstract ObjectPath create_snapshot(string name = "", bool cleanup = false) throws DBusError, IOError; 81 | 82 | public abstract void set_environment(string[] names) throws DBusError, IOError; 83 | public abstract void unset_environment(string[] names) throws DBusError, IOError; 84 | 85 | public abstract signal void unit_new(string id, ObjectPath path); 86 | public abstract signal void unit_removed(string id, ObjectPath path); 87 | public abstract signal void job_new(uint32 id, ObjectPath path); 88 | public abstract signal void job_removed(uint32 id, ObjectPath path, string res); 89 | } 90 | 91 | [DBus (name = "org.freedesktop.systemd1.Unit")] 92 | public interface Unit : DBusProxy { 93 | public struct JobLink { 94 | uint32 id; 95 | ObjectPath path; 96 | } 97 | 98 | public abstract string id { owned get; } 99 | public abstract string[] names { owned get; } 100 | public abstract string following { owned get; } 101 | public abstract string[] requires { owned get; } 102 | public abstract string[] requires_overridable { owned get; } 103 | public abstract string[] requisite { owned get; } 104 | public abstract string[] requisite_overridable { owned get; } 105 | public abstract string[] wants { owned get; } 106 | public abstract string[] required_by { owned get; } 107 | public abstract string[] required_by_overridable { owned get; } 108 | public abstract string[] wanted_by { owned get; } 109 | public abstract string[] conflicts { owned get; } 110 | public abstract string[] conflicted_by { owned get; } 111 | public abstract string[] before { owned get; } 112 | public abstract string[] after { owned get; } 113 | public abstract string[] on_failure { owned get; } 114 | public abstract string description { owned get; } 115 | public abstract string load_state { owned get; } 116 | public abstract string active_state { owned get; } 117 | public abstract string sub_state { owned get; } 118 | public abstract string fragment_path { owned get; } 119 | public abstract uint64 inactive_exit_timestamp { owned get; } 120 | public abstract uint64 active_enter_timestamp { owned get; } 121 | public abstract uint64 active_exit_timestamp { owned get; } 122 | public abstract uint64 inactive_enter_timestamp { owned get; } 123 | public abstract bool can_start { owned get; } 124 | public abstract bool can_stop { owned get; } 125 | public abstract bool can_reload { owned get; } 126 | public abstract JobLink job { owned get; } 127 | public abstract bool recursive_stop { owned get; } 128 | public abstract bool stop_when_unneeded { owned get; } 129 | public abstract bool refuse_manual_start { owned get; } 130 | public abstract bool refuse_manual_stop { owned get; } 131 | public abstract bool default_dependencies { owned get; } 132 | public abstract string default_control_group { owned get; } 133 | public abstract string[] control_groups { owned get; } 134 | public abstract bool need_daemon_reload { owned get; } 135 | public abstract uint64 job_timeout_usec { owned get; } 136 | 137 | public abstract ObjectPath start(string mode = "replace") throws DBusError, IOError; 138 | public abstract ObjectPath stop(string mode = "replace") throws DBusError, IOError; 139 | public abstract ObjectPath reload(string mode = "replace") throws DBusError, IOError; 140 | public abstract ObjectPath restart(string mode = "replace") throws DBusError, IOError; 141 | public abstract ObjectPath try_restart(string mode = "replace") throws DBusError, IOError; 142 | public abstract ObjectPath reload_or_restart(string mode = "replace") throws DBusError, IOError; 143 | public abstract ObjectPath reload_or_try_restart(string mode = "replace") throws DBusError, IOError; 144 | 145 | public abstract void reset_failed() throws DBusError, IOError; 146 | } 147 | 148 | [DBus (name = "org.freedesktop.systemd1.Job")] 149 | public interface Job : DBusProxy { 150 | public struct UnitLink { 151 | string id; 152 | ObjectPath path; 153 | } 154 | 155 | public abstract uint32 id { owned get; } 156 | public abstract string state { owned get; } 157 | public abstract string job_type { owned get; } 158 | public abstract UnitLink unit { owned get; } 159 | 160 | public abstract void cancel() throws DBusError, IOError; 161 | } 162 | 163 | [DBus (name = "org.freedesktop.Properties")] 164 | public interface Properties : DBusProxy { 165 | public abstract Variant? get(string iface, string property) throws DBusError, IOError; 166 | public abstract signal void properties_changed(string iface, HashTable changed_properties, string[] invalidated_properties); 167 | } 168 | --------------------------------------------------------------------------------