Fix Werrors with GCC-14.1.0
[platform/upstream/libzypp.git] / doc / autoinclude / Plugins.doc
1 /**
2
3 \page zypp-plugins Extending ZYpp: Plugins and Hooks
4
5 \author Duncan Mac-Vicar P. <dmacvicar@suse.de>
6 \author Michael Andres <ma@suse.de>
7
8 <HR><!-- ====================================================================== -->
9 \section plugins-intro Introduction
10
11 Plugins allow to extend the ZYpp package manager without the need to change
12 code. Plugins are designed as external programs so that they can be written in any language.
13
14 \section plugin-protocols Plugin protocols
15
16 Depending on the complexity and need for future extension, plugins talk
17 to ZYpp using two methods.
18
19 \subsection plugin-protocol-stateless Stateless
20
21 This type of plugin receive input reading the standard input, and answer ZYpp writing to the standard output. This means the plugin is executed once per hook and they are stateless (unless the plugin keeps the state out of process).
22
23 \subsection plugin-protocol-stateful Stateful
24
25 This type of plugin is called by ZYpp and a conversation using a simple protocol. The protocol is based on STOMP http://stomp.github.com (Streaming Text Orientated Messaging Protocol). Messages (called "frames") look like the following:
26
27 \verbatim
28 COMMAND
29 param1:val1
30 param2:val2
31 ...
32 Thus a COMMAND followed by key:value header lines
33 and a multiline body separated from header
34 by an empty line and terminated by NUL.
35 ^@
36 \endverbatim
37
38 \note ^@ is a null (control-@ in ASCII) byte.
39
40 <HR><!-- ====================================================================== -->
41 \section plugin-writing Writing plugins
42
43 A python class is offered as a helper to handle communication between the plugin and
44 libzypp. It is available by installing \c zypp-plugin-python.
45
46 \verbatim
47 zypper in -C zypp-plugin-python
48 \endverbatim
49
50 The plugin must define a method for each message it may receive from \ref libzypp. Message header list and body are passed to the method as arguments.
51
52 \verbatim
53 #!/usr/bin/env python
54 #
55 # zypp plugin
56 #
57 import os
58 import sys
59 from zypp_plugin import Plugin
60
61 class MyPlugin(Plugin):
62
63   def SAMPLE( self, headers, body ):
64     # called upon revieving a SAMPLE message
65     ...
66     if ( ok ):
67       self.ack()
68     else:
69       self.error( { "aheader":"header value" }, "body\n(multiline text ok)" )
70
71 plugin = MyPlugin()
72 plugin.main()
73 \endverbatim
74
75 Two methods \c ack and \c error are available to send back a standard \c ACK or \c ERROR message. You may optionally pass header entries and a multiline body. For sending custom messages use \c answer, which takes the command name as 1st argument, followed by optional header entries and a multiline body.
76
77 Plugin closes \c stdin and exits when receiving a \c _DISCONNECT message. Upon an \c ACK response to \c _DISCONNECT libzypp will not attempt to kill the script. An exit value different than \c 0 may be set via an \c 'exit' header in \c ACK.
78
79 \verbatim
80   def _DISCONNECT( self, headers, body ):
81     sys.stding.close()
82     self.ack( {'exit':'99'}, 'Famous last words.' )
83 \endverbatim
84
85 <HR><!-- ====================================================================== -->
86 \section plugins-impl Developers: Implementation
87
88 Plugins are implemented in the following classes:
89
90 - \ref zypp::PluginScript (Plugin as an external program)
91 - \ref zypp::PluginScriptException
92 - \ref zypp::PluginFrame (Message for the stateful protocol)
93 - \ref zypp::PluginFrameException
94 - \ref zypp::repo::PluginServices (Finds Service plugins)
95
96 The plugins default location is obtained from \ref zypp::ZConfig::pluginsPath()
97
98 <HR><!-- ====================================================================== -->
99 \section plugin-toc Supported plugins
100
101 \subpage plugin-commit Escort installation of packages
102
103 \subpage plugin-system Receive notification if system content has changed
104
105 \ref plugin-services
106
107 \ref plugin-url-resolver
108
109 \ref plugin-appdata
110
111 <HR><!-- ====================================================================== -->
112 \section plugin-services Service plugins
113
114 ZYpp will find a subscribed service for each executable located in /usr/lib/zypp/plugins/services and will set the alias as the executable name. The type will be set to "plugin".
115
116 Service plugins are used to provide a client a list of repositories from a central location where more complex logic is needed than a simple remote xml index accessible via http (in that case you can use \ref services-remote "Remote Services").
117
118 \subsection plugin-services-example1 Example: Management console
119
120 You have a custom mass management application that controls the repositories each client muss have. While you can use \ref services-remote "Remote Services" and subscribe each client to an url in the server providing a dynamic repoindex.xml based on the client, if you need to pass custom information in order for the server to calculate the repository list (e.g. number of CPUs) or the protocol that the client and the server and client speak is proprietary, you may implement the service locally, as an executable that will be installed in each client /usr/lib/zypp/plugins/services directory (it may be installed from a package).
121
122 \subsection plugin-services-how How to write a Services plugin
123
124 When listing services, ZYpp will find each plugin service as a subscribed service.
125
126 Service plugins are Stateless. When services are refreshed, ZYpp will call each plugin and the repository list will be taken from the output of the script in INI format (same as how they are stored in /etc/zypp/repos.d).
127
128 For our example:
129
130 \verbatim
131 # example plugin output
132 # comments are ignored
133 [repo1]
134 name=Base repository
135 summary=Standard repository
136 baseurl=http://server.com/repo1
137 type=rpm-md
138
139 # multiple repositories can be present in the output
140
141 [repo2]
142 ...
143 \endverbatim
144
145 The repositories will be added on service refresh with the alias present in the output, prefixed by the service alias (in this case, the executable name).
146
147 To protect your code from e.g python modules writing their own messages to stdout, you should use a copy of stdout and redirect stdout to stderr (Python3 may need a slightly different print syntax):
148
149 \verbatim
150 #!/usr/bin/python
151 import sys
152 sendback = sys.stdout
153 sys.stdout = sys.stderr
154
155 print >>sendback, "# example plugin output"
156 print >>sendback, "# comments are ignored"
157 print >>sendback, "[repo1]"
158 print >>sendback, "name=Base repository"
159 ...
160 \endverbatim
161
162 <HR><!-- ====================================================================== -->
163 \section plugin-url-resolver Url Resolver plugins
164
165 Url resolver plugins convert urls of scheme "plugin" into the output of the plugin named $name using the protocol. Thanks to the protocol, each header returned is also added as HTTP headers. The current protocol sequence is:
166
167 ZYpp sees a repository whose url has the format:
168
169 \verbatim
170 plugin:foo?param1=val1&param2=val2
171 \endverbatim
172
173 ZYpp tries to execute a plugin named foo (in /usr/lib/zypp/plugins/urlresolver) and call it with the following protocol:
174
175 \verbatim
176    RESOLVEURL
177    param1:val1
178    param2:val2
179    ...
180    ^@
181 \endverbatim
182
183 The plugin answers:
184
185 \verbatim
186    RESOLVEDURL:
187    header1:val1
188    header2:val2
189    ...
190    http://realurl.com?opts=vals
191    ^@
192 \endverbatim
193
194 And this url is used instead.
195
196 \subsection plugin-urlresolver-example Example
197
198 You have a repository with url:
199
200    plugin:lan
201
202 The script looks which distribution you have installed, and via SLP finds the right repositories in the lan and selects the update one and returns it url. But in addition, it adds a header with the update status that can be collected on the server side.
203
204 This type of plugin can be combined with service plugins, because a local service could return a list of repos like this:
205
206 \verbatim
207   [distro]
208   name=Distribution repository
209   baseurl=plugin:lan?repo=distro
210   [update]
211   name=Update repository
212   baseurl=plugin:lan?repo=update
213 \endverbatim
214
215 \note
216 In this example, the service plugin could have inmediately resolved the urls and returned http://realurl, but the url resolver allows also to add HTTP headers to the request.
217
218 <HR><!-- ====================================================================== -->
219 \section plugin-appdata Appdata refresh plugins (repo change)
220
221 Stateless plugins found in /usr/lib/zypp/plugins/appdata are called whenever any of the system repos has changed (added/removed/renamed/modified) or has been refreshed. Detailed information \b what exactly has changed is not available. (scripts are executed IFF euid is '0' and --root is not used). For every enabled system repo we pass alias type and metadata path on the commandline like this:
222
223 \verbatim
224   -R REPO_ALIAS -t REPO_TYPE -p REPO_METADATA_PATH   -R NEXT_REPO....
225 \endverbatim
226
227 \note REPO_TYPE can be e.g. "rpm-md", "yast2", "plaindir" or "NONE" indicating the repo was not yet probed.
228
229 \note REPO_METADATA_PATH can be empty or a not existing directory, indicating valid metadata for the repo are not yet available.
230
231 Scripts are executed 'fire and forget' whenever a RepoManager instance that performed changes goes out of scope. So it's up to the script to protect against concurrency.
232 */