Subject | Hash | Author | Date (UTC) |
---|---|---|---|
Initial commit | a7933be9fb44a9c0bb6834bf4f97a48db261c271 | Sylvain BERTRAND | 2012-09-05 09:39:46 |
File COPYING-GPL added (mode: 100644) (index 0000000..d159169) | |||
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 | <one line to give the program's name and a brief idea of what it does.> | ||
294 | Copyright (C) <year> <name of author> | ||
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 | <signature of Ty Coon>, 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. |
File COPYING-LGPL added (mode: 100644) (index 0000000..d2e3127) | |||
1 | GNU LESSER GENERAL PUBLIC LICENSE | ||
2 | Version 2.1, February 1999 | ||
3 | |||
4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. | ||
5 | 51 Franklin St, 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 | [This is the first released version of the Lesser GPL. It also counts | ||
10 | as the successor of the GNU Library Public License, version 2, hence | ||
11 | the version number 2.1.] | ||
12 | |||
13 | Preamble | ||
14 | |||
15 | The licenses for most software are designed to take away your | ||
16 | freedom to share and change it. By contrast, the GNU General Public | ||
17 | Licenses are intended to guarantee your freedom to share and change | ||
18 | free software--to make sure the software is free for all its users. | ||
19 | |||
20 | This license, the Lesser General Public License, applies to some | ||
21 | specially designated software packages--typically libraries--of the | ||
22 | Free Software Foundation and other authors who decide to use it. You | ||
23 | can use it too, but we suggest you first think carefully about whether | ||
24 | this license or the ordinary General Public License is the better | ||
25 | strategy to use in any particular case, based on the explanations below. | ||
26 | |||
27 | When we speak of free software, we are referring to freedom of use, | ||
28 | not price. Our General Public Licenses are designed to make sure that | ||
29 | you have the freedom to distribute copies of free software (and charge | ||
30 | for this service if you wish); that you receive source code or can get | ||
31 | it if you want it; that you can change the software and use pieces of | ||
32 | it in new free programs; and that you are informed that you can do | ||
33 | these things. | ||
34 | |||
35 | To protect your rights, we need to make restrictions that forbid | ||
36 | distributors to deny you these rights or to ask you to surrender these | ||
37 | rights. These restrictions translate to certain responsibilities for | ||
38 | you if you distribute copies of the library or if you modify it. | ||
39 | |||
40 | For example, if you distribute copies of the library, whether gratis | ||
41 | or for a fee, you must give the recipients all the rights that we gave | ||
42 | you. You must make sure that they, too, receive or can get the source | ||
43 | code. If you link other code with the library, you must provide | ||
44 | complete object files to the recipients, so that they can relink them | ||
45 | with the library after making changes to the library and recompiling | ||
46 | it. And you must show them these terms so they know their rights. | ||
47 | |||
48 | We protect your rights with a two-step method: (1) we copyright the | ||
49 | library, and (2) we offer you this license, which gives you legal | ||
50 | permission to copy, distribute and/or modify the library. | ||
51 | |||
52 | To protect each distributor, we want to make it very clear that | ||
53 | there is no warranty for the free library. Also, if the library is | ||
54 | modified by someone else and passed on, the recipients should know | ||
55 | that what they have is not the original version, so that the original | ||
56 | author's reputation will not be affected by problems that might be | ||
57 | introduced by others. | ||
58 | |||
59 | Finally, software patents pose a constant threat to the existence of | ||
60 | any free program. We wish to make sure that a company cannot | ||
61 | effectively restrict the users of a free program by obtaining a | ||
62 | restrictive license from a patent holder. Therefore, we insist that | ||
63 | any patent license obtained for a version of the library must be | ||
64 | consistent with the full freedom of use specified in this license. | ||
65 | |||
66 | Most GNU software, including some libraries, is covered by the | ||
67 | ordinary GNU General Public License. This license, the GNU Lesser | ||
68 | General Public License, applies to certain designated libraries, and | ||
69 | is quite different from the ordinary General Public License. We use | ||
70 | this license for certain libraries in order to permit linking those | ||
71 | libraries into non-free programs. | ||
72 | |||
73 | When a program is linked with a library, whether statically or using | ||
74 | a shared library, the combination of the two is legally speaking a | ||
75 | combined work, a derivative of the original library. The ordinary | ||
76 | General Public License therefore permits such linking only if the | ||
77 | entire combination fits its criteria of freedom. The Lesser General | ||
78 | Public License permits more lax criteria for linking other code with | ||
79 | the library. | ||
80 | |||
81 | We call this license the "Lesser" General Public License because it | ||
82 | does Less to protect the user's freedom than the ordinary General | ||
83 | Public License. It also provides other free software developers Less | ||
84 | of an advantage over competing non-free programs. These disadvantages | ||
85 | are the reason we use the ordinary General Public License for many | ||
86 | libraries. However, the Lesser license provides advantages in certain | ||
87 | special circumstances. | ||
88 | |||
89 | For example, on rare occasions, there may be a special need to | ||
90 | encourage the widest possible use of a certain library, so that it becomes | ||
91 | a de-facto standard. To achieve this, non-free programs must be | ||
92 | allowed to use the library. A more frequent case is that a free | ||
93 | library does the same job as widely used non-free libraries. In this | ||
94 | case, there is little to gain by limiting the free library to free | ||
95 | software only, so we use the Lesser General Public License. | ||
96 | |||
97 | In other cases, permission to use a particular library in non-free | ||
98 | programs enables a greater number of people to use a large body of | ||
99 | free software. For example, permission to use the GNU C Library in | ||
100 | non-free programs enables many more people to use the whole GNU | ||
101 | operating system, as well as its variant, the GNU/Linux operating | ||
102 | system. | ||
103 | |||
104 | Although the Lesser General Public License is Less protective of the | ||
105 | users' freedom, it does ensure that the user of a program that is | ||
106 | linked with the Library has the freedom and the wherewithal to run | ||
107 | that program using a modified version of the Library. | ||
108 | |||
109 | The precise terms and conditions for copying, distribution and | ||
110 | modification follow. Pay close attention to the difference between a | ||
111 | "work based on the library" and a "work that uses the library". The | ||
112 | former contains code derived from the library, whereas the latter must | ||
113 | be combined with the library in order to run. | ||
114 | |||
115 | GNU LESSER GENERAL PUBLIC LICENSE | ||
116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||
117 | |||
118 | 0. This License Agreement applies to any software library or other | ||
119 | program which contains a notice placed by the copyright holder or | ||
120 | other authorized party saying it may be distributed under the terms of | ||
121 | this Lesser General Public License (also called "this License"). | ||
122 | Each licensee is addressed as "you". | ||
123 | |||
124 | A "library" means a collection of software functions and/or data | ||
125 | prepared so as to be conveniently linked with application programs | ||
126 | (which use some of those functions and data) to form executables. | ||
127 | |||
128 | The "Library", below, refers to any such software library or work | ||
129 | which has been distributed under these terms. A "work based on the | ||
130 | Library" means either the Library or any derivative work under | ||
131 | copyright law: that is to say, a work containing the Library or a | ||
132 | portion of it, either verbatim or with modifications and/or translated | ||
133 | straightforwardly into another language. (Hereinafter, translation is | ||
134 | included without limitation in the term "modification".) | ||
135 | |||
136 | "Source code" for a work means the preferred form of the work for | ||
137 | making modifications to it. For a library, complete source code means | ||
138 | all the source code for all modules it contains, plus any associated | ||
139 | interface definition files, plus the scripts used to control compilation | ||
140 | and installation of the library. | ||
141 | |||
142 | Activities other than copying, distribution and modification are not | ||
143 | covered by this License; they are outside its scope. The act of | ||
144 | running a program using the Library is not restricted, and output from | ||
145 | such a program is covered only if its contents constitute a work based | ||
146 | on the Library (independent of the use of the Library in a tool for | ||
147 | writing it). Whether that is true depends on what the Library does | ||
148 | and what the program that uses the Library does. | ||
149 | |||
150 | 1. You may copy and distribute verbatim copies of the Library's | ||
151 | complete source code as you receive it, in any medium, provided that | ||
152 | you conspicuously and appropriately publish on each copy an | ||
153 | appropriate copyright notice and disclaimer of warranty; keep intact | ||
154 | all the notices that refer to this License and to the absence of any | ||
155 | warranty; and distribute a copy of this License along with the | ||
156 | Library. | ||
157 | |||
158 | You may charge a fee for the physical act of transferring a copy, | ||
159 | and you may at your option offer warranty protection in exchange for a | ||
160 | fee. | ||
161 | |||
162 | 2. You may modify your copy or copies of the Library or any portion | ||
163 | of it, thus forming a work based on the Library, and copy and | ||
164 | distribute such modifications or work under the terms of Section 1 | ||
165 | above, provided that you also meet all of these conditions: | ||
166 | |||
167 | a) The modified work must itself be a software library. | ||
168 | |||
169 | b) You must cause the files modified to carry prominent notices | ||
170 | stating that you changed the files and the date of any change. | ||
171 | |||
172 | c) You must cause the whole of the work to be licensed at no | ||
173 | charge to all third parties under the terms of this License. | ||
174 | |||
175 | d) If a facility in the modified Library refers to a function or a | ||
176 | table of data to be supplied by an application program that uses | ||
177 | the facility, other than as an argument passed when the facility | ||
178 | is invoked, then you must make a good faith effort to ensure that, | ||
179 | in the event an application does not supply such function or | ||
180 | table, the facility still operates, and performs whatever part of | ||
181 | its purpose remains meaningful. | ||
182 | |||
183 | (For example, a function in a library to compute square roots has | ||
184 | a purpose that is entirely well-defined independent of the | ||
185 | application. Therefore, Subsection 2d requires that any | ||
186 | application-supplied function or table used by this function must | ||
187 | be optional: if the application does not supply it, the square | ||
188 | root function must still compute square roots.) | ||
189 | |||
190 | These requirements apply to the modified work as a whole. If | ||
191 | identifiable sections of that work are not derived from the Library, | ||
192 | and can be reasonably considered independent and separate works in | ||
193 | themselves, then this License, and its terms, do not apply to those | ||
194 | sections when you distribute them as separate works. But when you | ||
195 | distribute the same sections as part of a whole which is a work based | ||
196 | on the Library, the distribution of the whole must be on the terms of | ||
197 | this License, whose permissions for other licensees extend to the | ||
198 | entire whole, and thus to each and every part regardless of who wrote | ||
199 | it. | ||
200 | |||
201 | Thus, it is not the intent of this section to claim rights or contest | ||
202 | your rights to work written entirely by you; rather, the intent is to | ||
203 | exercise the right to control the distribution of derivative or | ||
204 | collective works based on the Library. | ||
205 | |||
206 | In addition, mere aggregation of another work not based on the Library | ||
207 | with the Library (or with a work based on the Library) on a volume of | ||
208 | a storage or distribution medium does not bring the other work under | ||
209 | the scope of this License. | ||
210 | |||
211 | 3. You may opt to apply the terms of the ordinary GNU General Public | ||
212 | License instead of this License to a given copy of the Library. To do | ||
213 | this, you must alter all the notices that refer to this License, so | ||
214 | that they refer to the ordinary GNU General Public License, version 2, | ||
215 | instead of to this License. (If a newer version than version 2 of the | ||
216 | ordinary GNU General Public License has appeared, then you can specify | ||
217 | that version instead if you wish.) Do not make any other change in | ||
218 | these notices. | ||
219 | |||
220 | Once this change is made in a given copy, it is irreversible for | ||
221 | that copy, so the ordinary GNU General Public License applies to all | ||
222 | subsequent copies and derivative works made from that copy. | ||
223 | |||
224 | This option is useful when you wish to copy part of the code of | ||
225 | the Library into a program that is not a library. | ||
226 | |||
227 | 4. You may copy and distribute the Library (or a portion or | ||
228 | derivative of it, under Section 2) in object code or executable form | ||
229 | under the terms of Sections 1 and 2 above provided that you accompany | ||
230 | it with the complete corresponding machine-readable source code, which | ||
231 | must be distributed under the terms of Sections 1 and 2 above on a | ||
232 | medium customarily used for software interchange. | ||
233 | |||
234 | If distribution of object code is made by offering access to copy | ||
235 | from a designated place, then offering equivalent access to copy the | ||
236 | source code from the same place satisfies the requirement to | ||
237 | distribute the source code, even though third parties are not | ||
238 | compelled to copy the source along with the object code. | ||
239 | |||
240 | 5. A program that contains no derivative of any portion of the | ||
241 | Library, but is designed to work with the Library by being compiled or | ||
242 | linked with it, is called a "work that uses the Library". Such a | ||
243 | work, in isolation, is not a derivative work of the Library, and | ||
244 | therefore falls outside the scope of this License. | ||
245 | |||
246 | However, linking a "work that uses the Library" with the Library | ||
247 | creates an executable that is a derivative of the Library (because it | ||
248 | contains portions of the Library), rather than a "work that uses the | ||
249 | library". The executable is therefore covered by this License. | ||
250 | Section 6 states terms for distribution of such executables. | ||
251 | |||
252 | When a "work that uses the Library" uses material from a header file | ||
253 | that is part of the Library, the object code for the work may be a | ||
254 | derivative work of the Library even though the source code is not. | ||
255 | Whether this is true is especially significant if the work can be | ||
256 | linked without the Library, or if the work is itself a library. The | ||
257 | threshold for this to be true is not precisely defined by law. | ||
258 | |||
259 | If such an object file uses only numerical parameters, data | ||
260 | structure layouts and accessors, and small macros and small inline | ||
261 | functions (ten lines or less in length), then the use of the object | ||
262 | file is unrestricted, regardless of whether it is legally a derivative | ||
263 | work. (Executables containing this object code plus portions of the | ||
264 | Library will still fall under Section 6.) | ||
265 | |||
266 | Otherwise, if the work is a derivative of the Library, you may | ||
267 | distribute the object code for the work under the terms of Section 6. | ||
268 | Any executables containing that work also fall under Section 6, | ||
269 | whether or not they are linked directly with the Library itself. | ||
270 | |||
271 | 6. As an exception to the Sections above, you may also combine or | ||
272 | link a "work that uses the Library" with the Library to produce a | ||
273 | work containing portions of the Library, and distribute that work | ||
274 | under terms of your choice, provided that the terms permit | ||
275 | modification of the work for the customer's own use and reverse | ||
276 | engineering for debugging such modifications. | ||
277 | |||
278 | You must give prominent notice with each copy of the work that the | ||
279 | Library is used in it and that the Library and its use are covered by | ||
280 | this License. You must supply a copy of this License. If the work | ||
281 | during execution displays copyright notices, you must include the | ||
282 | copyright notice for the Library among them, as well as a reference | ||
283 | directing the user to the copy of this License. Also, you must do one | ||
284 | of these things: | ||
285 | |||
286 | a) Accompany the work with the complete corresponding | ||
287 | machine-readable source code for the Library including whatever | ||
288 | changes were used in the work (which must be distributed under | ||
289 | Sections 1 and 2 above); and, if the work is an executable linked | ||
290 | with the Library, with the complete machine-readable "work that | ||
291 | uses the Library", as object code and/or source code, so that the | ||
292 | user can modify the Library and then relink to produce a modified | ||
293 | executable containing the modified Library. (It is understood | ||
294 | that the user who changes the contents of definitions files in the | ||
295 | Library will not necessarily be able to recompile the application | ||
296 | to use the modified definitions.) | ||
297 | |||
298 | b) Use a suitable shared library mechanism for linking with the | ||
299 | Library. A suitable mechanism is one that (1) uses at run time a | ||
300 | copy of the library already present on the user's computer system, | ||
301 | rather than copying library functions into the executable, and (2) | ||
302 | will operate properly with a modified version of the library, if | ||
303 | the user installs one, as long as the modified version is | ||
304 | interface-compatible with the version that the work was made with. | ||
305 | |||
306 | c) Accompany the work with a written offer, valid for at | ||
307 | least three years, to give the same user the materials | ||
308 | specified in Subsection 6a, above, for a charge no more | ||
309 | than the cost of performing this distribution. | ||
310 | |||
311 | d) If distribution of the work is made by offering access to copy | ||
312 | from a designated place, offer equivalent access to copy the above | ||
313 | specified materials from the same place. | ||
314 | |||
315 | e) Verify that the user has already received a copy of these | ||
316 | materials or that you have already sent this user a copy. | ||
317 | |||
318 | For an executable, the required form of the "work that uses the | ||
319 | Library" must include any data and utility programs needed for | ||
320 | reproducing the executable from it. However, as a special exception, | ||
321 | the materials to be distributed need not include anything that is | ||
322 | normally distributed (in either source or binary form) with the major | ||
323 | components (compiler, kernel, and so on) of the operating system on | ||
324 | which the executable runs, unless that component itself accompanies | ||
325 | the executable. | ||
326 | |||
327 | It may happen that this requirement contradicts the license | ||
328 | restrictions of other proprietary libraries that do not normally | ||
329 | accompany the operating system. Such a contradiction means you cannot | ||
330 | use both them and the Library together in an executable that you | ||
331 | distribute. | ||
332 | |||
333 | 7. You may place library facilities that are a work based on the | ||
334 | Library side-by-side in a single library together with other library | ||
335 | facilities not covered by this License, and distribute such a combined | ||
336 | library, provided that the separate distribution of the work based on | ||
337 | the Library and of the other library facilities is otherwise | ||
338 | permitted, and provided that you do these two things: | ||
339 | |||
340 | a) Accompany the combined library with a copy of the same work | ||
341 | based on the Library, uncombined with any other library | ||
342 | facilities. This must be distributed under the terms of the | ||
343 | Sections above. | ||
344 | |||
345 | b) Give prominent notice with the combined library of the fact | ||
346 | that part of it is a work based on the Library, and explaining | ||
347 | where to find the accompanying uncombined form of the same work. | ||
348 | |||
349 | 8. You may not copy, modify, sublicense, link with, or distribute | ||
350 | the Library except as expressly provided under this License. Any | ||
351 | attempt otherwise to copy, modify, sublicense, link with, or | ||
352 | distribute the Library is void, and will automatically terminate your | ||
353 | rights under this License. However, parties who have received copies, | ||
354 | or rights, from you under this License will not have their licenses | ||
355 | terminated so long as such parties remain in full compliance. | ||
356 | |||
357 | 9. You are not required to accept this License, since you have not | ||
358 | signed it. However, nothing else grants you permission to modify or | ||
359 | distribute the Library or its derivative works. These actions are | ||
360 | prohibited by law if you do not accept this License. Therefore, by | ||
361 | modifying or distributing the Library (or any work based on the | ||
362 | Library), you indicate your acceptance of this License to do so, and | ||
363 | all its terms and conditions for copying, distributing or modifying | ||
364 | the Library or works based on it. | ||
365 | |||
366 | 10. Each time you redistribute the Library (or any work based on the | ||
367 | Library), the recipient automatically receives a license from the | ||
368 | original licensor to copy, distribute, link with or modify the Library | ||
369 | subject to these terms and conditions. You may not impose any further | ||
370 | restrictions on the recipients' exercise of the rights granted herein. | ||
371 | You are not responsible for enforcing compliance by third parties with | ||
372 | this License. | ||
373 | |||
374 | 11. If, as a consequence of a court judgment or allegation of patent | ||
375 | infringement or for any other reason (not limited to patent issues), | ||
376 | conditions are imposed on you (whether by court order, agreement or | ||
377 | otherwise) that contradict the conditions of this License, they do not | ||
378 | excuse you from the conditions of this License. If you cannot | ||
379 | distribute so as to satisfy simultaneously your obligations under this | ||
380 | License and any other pertinent obligations, then as a consequence you | ||
381 | may not distribute the Library at all. For example, if a patent | ||
382 | license would not permit royalty-free redistribution of the Library by | ||
383 | all those who receive copies directly or indirectly through you, then | ||
384 | the only way you could satisfy both it and this License would be to | ||
385 | refrain entirely from distribution of the Library. | ||
386 | |||
387 | If any portion of this section is held invalid or unenforceable under any | ||
388 | particular circumstance, the balance of the section is intended to apply, | ||
389 | and the section as a whole is intended to apply in other circumstances. | ||
390 | |||
391 | It is not the purpose of this section to induce you to infringe any | ||
392 | patents or other property right claims or to contest validity of any | ||
393 | such claims; this section has the sole purpose of protecting the | ||
394 | integrity of the free software distribution system which is | ||
395 | implemented by public license practices. Many people have made | ||
396 | generous contributions to the wide range of software distributed | ||
397 | through that system in reliance on consistent application of that | ||
398 | system; it is up to the author/donor to decide if he or she is willing | ||
399 | to distribute software through any other system and a licensee cannot | ||
400 | impose that choice. | ||
401 | |||
402 | This section is intended to make thoroughly clear what is believed to | ||
403 | be a consequence of the rest of this License. | ||
404 | |||
405 | 12. If the distribution and/or use of the Library is restricted in | ||
406 | certain countries either by patents or by copyrighted interfaces, the | ||
407 | original copyright holder who places the Library under this License may add | ||
408 | an explicit geographical distribution limitation excluding those countries, | ||
409 | so that distribution is permitted only in or among countries not thus | ||
410 | excluded. In such case, this License incorporates the limitation as if | ||
411 | written in the body of this License. | ||
412 | |||
413 | 13. The Free Software Foundation may publish revised and/or new | ||
414 | versions of the Lesser General Public License from time to time. | ||
415 | Such new versions will be similar in spirit to the present version, | ||
416 | but may differ in detail to address new problems or concerns. | ||
417 | |||
418 | Each version is given a distinguishing version number. If the Library | ||
419 | specifies a version number of this License which applies to it and | ||
420 | "any later version", you have the option of following the terms and | ||
421 | conditions either of that version or of any later version published by | ||
422 | the Free Software Foundation. If the Library does not specify a | ||
423 | license version number, you may choose any version ever published by | ||
424 | the Free Software Foundation. | ||
425 | |||
426 | 14. If you wish to incorporate parts of the Library into other free | ||
427 | programs whose distribution conditions are incompatible with these, | ||
428 | write to the author to ask for permission. For software which is | ||
429 | copyrighted by the Free Software Foundation, write to the Free | ||
430 | Software Foundation; we sometimes make exceptions for this. Our | ||
431 | decision will be guided by the two goals of preserving the free status | ||
432 | of all derivatives of our free software and of promoting the sharing | ||
433 | and reuse of software generally. | ||
434 | |||
435 | NO WARRANTY | ||
436 | |||
437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO | ||
438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. | ||
439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR | ||
440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY | ||
441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE | ||
442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE | ||
444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME | ||
445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. | ||
446 | |||
447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN | ||
448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY | ||
449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU | ||
450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR | ||
451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE | ||
452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING | ||
453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A | ||
454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF | ||
455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | ||
456 | DAMAGES. | ||
457 | |||
458 | END OF TERMS AND CONDITIONS | ||
459 | |||
460 | How to Apply These Terms to Your New Libraries | ||
461 | |||
462 | If you develop a new library, and you want it to be of the greatest | ||
463 | possible use to the public, we recommend making it free software that | ||
464 | everyone can redistribute and change. You can do so by permitting | ||
465 | redistribution under these terms (or, alternatively, under the terms of the | ||
466 | ordinary General Public License). | ||
467 | |||
468 | To apply these terms, attach the following notices to the library. It is | ||
469 | safest to attach them to the start of each source file to most effectively | ||
470 | convey the exclusion of warranty; and each file should have at least the | ||
471 | "copyright" line and a pointer to where the full notice is found. | ||
472 | |||
473 | <one line to give the library's name and a brief idea of what it does.> | ||
474 | Copyright (C) <year> <name of author> | ||
475 | |||
476 | This library is free software; you can redistribute it and/or | ||
477 | modify it under the terms of the GNU Lesser General Public | ||
478 | License as published by the Free Software Foundation; either | ||
479 | version 2.1 of the License, or (at your option) any later version. | ||
480 | |||
481 | This library is distributed in the hope that it will be useful, | ||
482 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
484 | Lesser General Public License for more details. | ||
485 | |||
486 | You should have received a copy of the GNU Lesser General Public | ||
487 | License along with this library; if not, write to the Free Software | ||
488 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
489 | |||
490 | Also add information on how to contact you by electronic and paper mail. | ||
491 | |||
492 | You should also get your employer (if you work as a programmer) or your | ||
493 | school, if any, to sign a "copyright disclaimer" for the library, if | ||
494 | necessary. Here is a sample; alter the names: | ||
495 | |||
496 | Yoyodyne, Inc., hereby disclaims all copyright interest in the | ||
497 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. | ||
498 | |||
499 | <signature of Ty Coon>, 1 April 1990 | ||
500 | Ty Coon, President of Vice | ||
501 | |||
502 | That's all there is to it! |
File README added (mode: 100644) (index 0000000..d0ce166) | |||
1 | This is the systemd-ectomized version of udev. Most features from the upstream | ||
2 | are backported, though we do not guarantee 100% API compatibility. The | ||
3 | development tree can be found at https://bitbucket.org/braindamaged/udev | ||
4 | |||
5 | Original udev readme follows. | ||
6 | |||
7 | ---------------------------------------- | ||
8 | |||
9 | udev - Linux userspace device management | ||
10 | |||
11 | Integrating udev in the system has complex dependencies and may differ from | ||
12 | distribution to distribution. A system may not be able to boot up or work | ||
13 | reliably without a properly installed udev version. The upstream udev project | ||
14 | does not recommend replacing a distro's udev installation with the upstream | ||
15 | version. | ||
16 | |||
17 | The upstream udev project's set of default rules may require a most recent | ||
18 | kernel release to work properly. | ||
19 | |||
20 | Tools and rules shipped by udev are not public API and may change at any time. | ||
21 | Never call any private tool in /usr/lib/udev from any external application; it | ||
22 | might just go away in the next release. Access to udev information is only offered | ||
23 | by udevadm and libudev. Tools and rules in /usr/lib/udev and the entire contents | ||
24 | of the /run/udev directory are private to udev and do change whenever needed. | ||
25 | |||
26 | Requirements: | ||
27 | - Version 2.6.34 of the Linux kernel with sysfs, procfs, signalfd, inotify, | ||
28 | unix domain sockets, networking and hotplug enabled | ||
29 | |||
30 | - Some architectures might need a later kernel, that supports accept4(), | ||
31 | or need to backport the accept4() syscall wiring in the kernel. | ||
32 | |||
33 | - These options are required: | ||
34 | CONFIG_DEVTMPFS=y | ||
35 | CONFIG_HOTPLUG=y | ||
36 | CONFIG_INOTIFY_USER=y | ||
37 | CONFIG_NET=y | ||
38 | CONFIG_PROC_FS=y | ||
39 | CONFIG_SIGNALFD=y | ||
40 | CONFIG_SYSFS=y | ||
41 | CONFIG_SYSFS_DEPRECATED*=n | ||
42 | CONFIG_UEVENT_HELPER_PATH="" | ||
43 | |||
44 | - These options might be needed: | ||
45 | CONFIG_BLK_DEV_BSG=y (SCSI devices) | ||
46 | CONFIG_TMPFS_POSIX_ACL=y (user ACLs for device nodes) | ||
47 | |||
48 | - The /dev directory needs the 'devtmpfs' filesystem mounted. | ||
49 | Udev only manages the permissions and ownership of the | ||
50 | kernel-provided device nodes, and possibly creates additional symlinks. | ||
51 | |||
52 | - Udev requires /run to be writable, which is usually done by mounting a | ||
53 | 'tmpfs' filesystem. | ||
54 | |||
55 | - This version of udev does not work properly with the CONFIG_SYSFS_DEPRECATED* | ||
56 | option enabled. | ||
57 | |||
58 | - The deprecated hotplug helper /sbin/hotplug should be disabled in the | ||
59 | kernel configuration, it is not needed today, and may render the system | ||
60 | unusable because the kernel may create too many processes in parallel | ||
61 | so that the system runs out-of-memory. | ||
62 | |||
63 | - The proc filesystem must be mounted on /proc, and the sysfs filesystem must | ||
64 | be mounted at /sys. No other locations are supported by a standard | ||
65 | udev installation. | ||
66 | |||
67 | - The default rules set requires the following group names resolvable at udev startup: | ||
68 | disk, cdrom, floppy, tape, audio, video, lp, tty, dialout, and kmem. | ||
69 | Especially in LDAP setups, it is required that getgrnam() be able to resolve | ||
70 | these group names with only the rootfs mounted and while no network is | ||
71 | available. | ||
72 | |||
73 | - Some udev extras have external dependencies like: | ||
74 | libglib2, usbutils, pciutils, and gperf. | ||
75 | All these extras can be disabled with configure options. | ||
76 | |||
77 | Setup: | ||
78 | - The udev daemon should be started to handle device events sent by the kernel. | ||
79 | During bootup, the events for already existing devices can be replayed, so | ||
80 | that they are configured by udev. The systemd service files contain the | ||
81 | needed commands to start the udev daemon and the coldplug sequence. | ||
82 | |||
83 | - Restarting the daemon never applies any rules to existing devices. | ||
84 | |||
85 | - New/changed rule files are picked up automatically; there is usually no | ||
86 | daemon restart or signal needed. | ||
87 | |||
88 | Operation: | ||
89 | - Based on events the kernel sends out on device creation/removal, udev | ||
90 | creates/removes device nodes and symlinks in the /dev directory. | ||
91 | |||
92 | - All kernel events are matched against a set of specified rules, which | ||
93 | possibly hook into the event processing and load required kernel | ||
94 | modules to set up devices. For all devices, the kernel exports a major/minor | ||
95 | number; if needed, udev creates a device node with the default kernel | ||
96 | device name. If specified, udev applies permissions/ownership to the device | ||
97 | node, creates additional symlinks pointing to the node, and executes | ||
98 | programs to handle the device. | ||
99 | |||
100 | - The events udev handles, and the information udev merges into its device | ||
101 | database, can be accessed with libudev: | ||
102 | http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/ | ||
103 | http://www.kernel.org/pub/linux/utils/kernel/hotplug/gudev/ | ||
104 | |||
105 | For more details about udev and udev rules, see the udev man pages: | ||
106 | http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev/ | ||
107 | |||
108 | Please direct any comment/question to the linux-hotplug mailing list at: | ||
109 | linux-hotplug@vger.kernel.org |
File TODO added (mode: 100644) (index 0000000..7d05661) | |||
1 | - find a way to tell udev to not cancel firmware | ||
2 | requests in initramfs | ||
3 | |||
4 | - scsi_id -> sg3_utils? | ||
5 | |||
6 | - make gtk-doc optional like kmod | ||
7 | |||
8 | - move /usr/lib/udev/devices/ to tmpfiles | ||
9 | |||
10 | - trigger --subsystem-match=usb/usb_device | ||
11 | |||
12 | - kill rules_generator | ||
13 | |||
14 | - have a $attrs{} ? | ||
15 | |||
16 | - libudev.so.1 | ||
17 | - symbol versioning | ||
18 | - return object with *_unref() | ||
19 | - udev_monitor_from_socket() | ||
20 | - udev_queue_get_failed_list_entry() |
File conf.mk added (mode: 100644) (index 0000000..df91d12) | |||
1 | #******************************************************************************* | ||
2 | #this code is protected by the GNU affero GPLv3 | ||
3 | #author:Sylvain BERTRAND <sylvain.bertrand AT gmail dot com> | ||
4 | # <digital.ragnarok AT gmail dot com> | ||
5 | #******************************************************************************* | ||
6 | |||
7 | #******************************************************************************* | ||
8 | #build configuration | ||
9 | export | ||
10 | DEBUG?= | ||
11 | SYSROOT?= | ||
12 | |||
13 | BUILD_DIR?=build | ||
14 | BIN_DIR?=$(BUILD_DIR)/bin | ||
15 | LIB_DIR?=$(BUILD_DIR)/lib | ||
16 | OBJ_DIR?=$(BUILD_DIR)/obj | ||
17 | S_DIR?=$(BUILD_DIR)/s | ||
18 | CPP_DIR?=$(BUILD_DIR)/cpp | ||
19 | DEVEL_DIR?=$(BUILD_DIR)/devel | ||
20 | MAN_DIR?=$(BUILD_DIR)/man | ||
21 | |||
22 | LD:=gcc | ||
23 | CC:=gcc | ||
24 | CPP:=cpp | ||
25 | AS:=as | ||
26 | PKG_CONFIG:=pkg-config | ||
27 | XSLTPROC:=xsltproc | ||
28 | |||
29 | ifndef DEBUG | ||
30 | CPPFLAGS?=-D_GNU_SOURCE -Wall -Wextra -I./ -I./src | ||
31 | CFLAGS?=-Wall -Wextra -Ofast -march=native | ||
32 | CFLAGS+= -fverbose-asm -S | ||
33 | LDFLAGS?=-Wl,-O,10 | ||
34 | ASFLAGS?= | ||
35 | else | ||
36 | CPPFLAGS?=-D_GNU_SOURCE -Wall -Wextra -I./ -I./src | ||
37 | CFLAGS?=-Wall -Wextra -std=gnu99 -ggdb3 -march=native | ||
38 | CFLAGS+= -fverbose-asm -S | ||
39 | ASFLAGS?= | ||
40 | endif | ||
41 | #******************************************************************************* | ||
42 | |||
43 | #******************************************************************************* | ||
44 | #software configuration | ||
45 | PREFIX?=/usr/local | ||
46 | INCLUDEDIR?=$(PREFIX)/include | ||
47 | #want the followinc at root | ||
48 | EXECPREFIX?=/ | ||
49 | LIBDIR?=/lib | ||
50 | SYSCONFDIR?=/etc | ||
51 | PKGLIBEXECDIR?=/lib/udev | ||
52 | FIRMWARE_PATH?=/lib/firmware | ||
53 | USB_DATABASE?=$(shell $(PKG_CONFIG) --variable=usbids usbutils) | ||
54 | PCI_DATABASE?=$(shell $(PKG_CONFIG) --variable=idsdir libpci)/pci.ids | ||
55 | |||
56 | CPPFLAGS+= -DSYSCONFDIR=\"$(SYSCONFDIR)\" -DPKGLIBEXECDIR=\"$(PKGLIBEXECDIR)\" | ||
57 | CPPFLAGS+= -DFIRMWARE_PATH=\"$(FIRMWARE_PATH)\" | ||
58 | CPPFLAGS+= -DUSB_DATABASE=\"$(USB_DATABASE)\" -DPCI_DATABASE=\"$(PCI_DATABASE)\" | ||
59 | |||
60 | #udevd, version is used by other software components, must be consistent | ||
61 | VERSION=189 | ||
62 | CPPFLAGS+= -DVERSION=\"$(VERSION)\" | ||
63 | |||
64 | #libudev | ||
65 | LIBUDEV_API=0 | ||
66 | LIBUDEV_VERSION=$(LIBUDEV_API).0.0 | ||
67 | #******************************************************************************* |
File gentoo/overlay/sys-fs/udev/udev-189.ebuild added (mode: 100644) (index 0000000..05241a6) | |||
1 | EAPI=4 | ||
2 | |||
3 | inherit eutils | ||
4 | |||
5 | SRC_URI="http://www.legeek.net/mudev-${PV}.tar.xz" | ||
6 | KEYWORDS="~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86" | ||
7 | |||
8 | DESCRIPTION="Linux dynamic and persistent device naming support (aka userspace devfs)" | ||
9 | HOMEPAGE="http://www.legeek.net" | ||
10 | |||
11 | LICENSE="LGPL-2.1 GPL-2" | ||
12 | SLOT="0" | ||
13 | IUSE="doc" | ||
14 | |||
15 | COMMON_DEPEND=">=sys-apps/kmod-5 | ||
16 | >=sys-apps/util-linux-2.20 | ||
17 | !<sys-libs/glibc-2.12" | ||
18 | |||
19 | DEPEND="${COMMON_DEPEND} | ||
20 | virtual/pkgconfig | ||
21 | sys-apps/usbutils | ||
22 | sys-apps/pciutils | ||
23 | doc? ( app-text/docbook-xsl-stylesheets | ||
24 | dev-libs/libxslt )" | ||
25 | |||
26 | RDEPEND="${COMMON_DEPEND} | ||
27 | sys-apps/hwids | ||
28 | !sys-apps/coldplug | ||
29 | !sys-apps/systemd | ||
30 | !<sys-fs/lvm2-2.02.45 | ||
31 | !sys-fs/device-mapper | ||
32 | !<sys-fs/udev-init-scripts-16 | ||
33 | !<sys-kernel/dracut-017-r1 | ||
34 | !<sys-kernel/genkernel-3.4.25" | ||
35 | |||
36 | S="${WORKDIR}/mudev-${PV}" | ||
37 | |||
38 | src_configure() { | ||
39 | sed -i "s:PREFIX?=/usr/local:PREFIX?=/usr:" "${S}"/conf.mk | ||
40 | sed -i "s:LIBDIR?=/lib:LIBDIR?=/$(get_libdir):" "${S}"/conf.mk | ||
41 | sed -i "s:PKGLIBEXECDIR?=/lib/udev:PKGLIBEXECDIR?=/$(get_libdir)/udev:" "${S}"/conf.mk | ||
42 | sed -i "s:FIRMWARE_PATH?=/lib/firmware:FIRMWARE_PATH?=/$(get_libdir)/firmware:" "${S}"/conf.mk | ||
43 | } | ||
44 | |||
45 | src_compile() { | ||
46 | if use doc | ||
47 | then | ||
48 | MAN=1 emake all | ||
49 | else | ||
50 | emake all | ||
51 | fi | ||
52 | } | ||
53 | |||
54 | src_install() { | ||
55 | into / | ||
56 | dolib.so build/lib/libudev.so.0.0.0 | ||
57 | dosym libudev.so.0.0.0 /$(get_libdir)/libudev.so.0 | ||
58 | dosym libudev.so.0.0.0 /$(get_libdir)/libudev.so | ||
59 | dosbin build/bin/udevd | ||
60 | dosbin build/bin/udevadm | ||
61 | |||
62 | insinto /$(get_libdir)/udev/rules.d | ||
63 | doins rules/* | ||
64 | |||
65 | insinto /usr/share/pkgconfig | ||
66 | doins build/devel/udev.pc | ||
67 | |||
68 | insinto /usr/$(get_libdir)/pkgconfig | ||
69 | doins build/devel/libudev.pc | ||
70 | |||
71 | insinto /usr/include | ||
72 | doins src/libudev.h | ||
73 | } | ||
74 | |||
75 | pkg_postinst() { | ||
76 | mkdir -p "${ROOT}"/run | ||
77 | } |
File makefile added (mode: 100644) (index 0000000..a20087c) | |||
1 | #This is a brutal makefile... but extremely easy to read as it is actually | ||
2 | #very basic makefile logic. | ||
3 | #**this is on purpose, then keep it that way** | ||
4 | include conf.mk | ||
5 | |||
6 | .PHONY:all dirs clean help | ||
7 | |||
8 | DIRS=\ | ||
9 | $(S_DIR) \ | ||
10 | $(CPP_DIR) \ | ||
11 | $(OBJ_DIR) \ | ||
12 | $(BIN_DIR) \ | ||
13 | $(LIB_DIR) \ | ||
14 | $(DEVEL_DIR) | ||
15 | |||
16 | ifdef MAN | ||
17 | DIRS+=$(MAN_DIR) | ||
18 | endif | ||
19 | |||
20 | LIBUDEV_OBJS=\ | ||
21 | $(OBJ_DIR)/libudev.o \ | ||
22 | $(OBJ_DIR)/libudev-device.o \ | ||
23 | $(OBJ_DIR)/libudev-device-private.o \ | ||
24 | $(OBJ_DIR)/libudev-enumerate.o \ | ||
25 | $(OBJ_DIR)/libudev-list.o \ | ||
26 | $(OBJ_DIR)/libudev-monitor.o \ | ||
27 | $(OBJ_DIR)/libudev-queue.o \ | ||
28 | $(OBJ_DIR)/libudev-queue-private.o \ | ||
29 | $(OBJ_DIR)/libudev-util.o \ | ||
30 | $(OBJ_DIR)/libudev-util-private.o | ||
31 | |||
32 | UDEVD_COMMON_OBJS=\ | ||
33 | $(OBJ_DIR)/udev-event.o \ | ||
34 | $(OBJ_DIR)/udev-watch.o \ | ||
35 | $(OBJ_DIR)/udev-node.o \ | ||
36 | $(OBJ_DIR)/udev-rules.o \ | ||
37 | $(OBJ_DIR)/udev-ctrl.o \ | ||
38 | $(OBJ_DIR)/udev-builtin.o \ | ||
39 | $(OBJ_DIR)/udev-builtin-blkid.o \ | ||
40 | $(OBJ_DIR)/udev-builtin-firmware.o \ | ||
41 | $(OBJ_DIR)/udev-builtin-hwdb.o \ | ||
42 | $(OBJ_DIR)/udev-builtin-input_id.o \ | ||
43 | $(OBJ_DIR)/udev-builtin-kmod.o \ | ||
44 | $(OBJ_DIR)/udev-builtin-path_id.o \ | ||
45 | $(OBJ_DIR)/udev-builtin-usb_id.o | ||
46 | |||
47 | UDEVD_OBJS=\ | ||
48 | $(UDEVD_COMMON_OBJS) \ | ||
49 | $(OBJ_DIR)/udevd.o | ||
50 | |||
51 | UDEVADM_OBJS=\ | ||
52 | $(UDEVD_COMMON_OBJS) \ | ||
53 | $(OBJ_DIR)/udevadm.o \ | ||
54 | $(OBJ_DIR)/udevadm-info.o \ | ||
55 | $(OBJ_DIR)/udevadm-control.o \ | ||
56 | $(OBJ_DIR)/udevadm-monitor.o \ | ||
57 | $(OBJ_DIR)/udevadm-settle.o \ | ||
58 | $(OBJ_DIR)/udevadm-trigger.o \ | ||
59 | $(OBJ_DIR)/udevadm-test.o \ | ||
60 | $(OBJ_DIR)/udevadm-test-builtin.o | ||
61 | |||
62 | ifdef MAN | ||
63 | MAN_PAGES=\ | ||
64 | $(MAN_DIR)/udev.7 \ | ||
65 | $(MAN_DIR)/udevadm.8 \ | ||
66 | $(MAN_DIR)/udevd.8 | ||
67 | endif | ||
68 | |||
69 | #******************************************************************************* | ||
70 | help: | ||
71 | @echo "targets are 'all', 'help'(this output), 'clean'" | ||
72 | @echo -e "you can configure the build with the following variables:\\n\ | ||
73 | SYSROOT, CPP, CC, LD, CPPFLAGS, CFLAGS, LDFLAGS, SYSCONFDIR...\\n\ | ||
74 | (you can tune the conf.mk file)" | ||
75 | |||
76 | all:dirs \ | ||
77 | $(LIB_DIR)/libudev.so.$(LIBUDEV_VERSION) \ | ||
78 | $(BIN_DIR)/udevd \ | ||
79 | $(BIN_DIR)/udevadm \ | ||
80 | $(DEVEL_DIR)/udev.pc \ | ||
81 | $(DEVEL_DIR)/libudev.pc \ | ||
82 | $(MAN_PAGES) | ||
83 | |||
84 | dirs:$(DIRS) | ||
85 | $(DIRS): | ||
86 | -mkdir -p $@ | ||
87 | |||
88 | clean: | ||
89 | -rm -Rf $(BUILD_DIR) | ||
90 | #******************************************************************************* | ||
91 | |||
92 | #******************************************************************************* | ||
93 | #man pages implicit rule | ||
94 | $(MAN_DIR)/%.7 $(MAN_DIR)/%.8:src/%.xml | ||
95 | $(XSLTPROC) -o $@ -nonet \ | ||
96 | http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl \ | ||
97 | $< | ||
98 | #******************************************************************************* | ||
99 | |||
100 | #******************************************************************************* | ||
101 | #pkg-config | ||
102 | $(DEVEL_DIR)/udev.pc:src/udev.pc.in | ||
103 | cp -f $< $@ | ||
104 | sed -i "s%@VERSION@%$(VERSION)%" $@ | ||
105 | sed -i "s%@pkglibexecdir@%$(PKGLIBEXECDIR)%" $@ | ||
106 | |||
107 | $(DEVEL_DIR)/libudev.pc:src/libudev.pc.in | ||
108 | cp -f $< $@ | ||
109 | sed -i "s%@VERSION@%$(VERSION)%" $@ | ||
110 | sed -i "s%@prefix@%$(PREFIX)%" $@ | ||
111 | sed -i "s%@exec_prefix@%$(EXECPREFIX)%" $@ | ||
112 | sed -i "s%@libdir@%$(LIBDIR)%" $@ | ||
113 | sed -i "s%@includedir@%$(INCLUDEDIR)%" $@ | ||
114 | #******************************************************************************* | ||
115 | |||
116 | #******************************************************************************* | ||
117 | #udevd | ||
118 | $(CPP_DIR)/udev-event.c:src/udev-event.c \ | ||
119 | src/udev.h \ | ||
120 | src/libudev.h \ | ||
121 | src/libudev-private.h | ||
122 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
123 | $(S_DIR)/udev-event.s:$(CPP_DIR)/udev-event.c | ||
124 | $(CC) $(CFLAGS) $< -o $@ | ||
125 | $(OBJ_DIR)/udev-event.o:$(S_DIR)/udev-event.s | ||
126 | $(AS) $(ASFLAGS) $< -o $@ | ||
127 | |||
128 | $(CPP_DIR)/udev-watch.c:src/udev-watch.c \ | ||
129 | src/udev.h \ | ||
130 | src/libudev.h \ | ||
131 | src/libudev-private.h | ||
132 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
133 | $(S_DIR)/udev-watch.s:$(CPP_DIR)/udev-watch.c | ||
134 | $(CC) $(CFLAGS) $< -o $@ | ||
135 | $(OBJ_DIR)/udev-watch.o:$(S_DIR)/udev-watch.s | ||
136 | $(AS) $(ASFLAGS) $< -o $@ | ||
137 | |||
138 | $(CPP_DIR)/udev-node.c:src/udev-node.c \ | ||
139 | src/udev.h \ | ||
140 | src/libudev.h \ | ||
141 | src/libudev-private.h | ||
142 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
143 | $(S_DIR)/udev-node.s:$(CPP_DIR)/udev-node.c | ||
144 | $(CC) $(CFLAGS) $< -o $@ | ||
145 | $(OBJ_DIR)/udev-node.o:$(S_DIR)/udev-node.s | ||
146 | $(AS) $(ASFLAGS) $< -o $@ | ||
147 | |||
148 | $(CPP_DIR)/udev-rules.c:src/udev-rules.c \ | ||
149 | src/udev.h \ | ||
150 | src/libudev.h \ | ||
151 | src/libudev-private.h | ||
152 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
153 | $(S_DIR)/udev-rules.s:$(CPP_DIR)/udev-rules.c | ||
154 | $(CC) $(CFLAGS) $< -o $@ | ||
155 | $(OBJ_DIR)/udev-rules.o:$(S_DIR)/udev-rules.s | ||
156 | $(AS) $(ASFLAGS) $< -o $@ | ||
157 | |||
158 | $(CPP_DIR)/udev-ctrl.c:src/udev-ctrl.c \ | ||
159 | src/udev.h \ | ||
160 | src/libudev.h \ | ||
161 | src/libudev-private.h | ||
162 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
163 | $(S_DIR)/udev-ctrl.s:$(CPP_DIR)/udev-ctrl.c | ||
164 | $(CC) $(CFLAGS) $< -o $@ | ||
165 | $(OBJ_DIR)/udev-ctrl.o:$(S_DIR)/udev-ctrl.s | ||
166 | $(AS) $(ASFLAGS) $< -o $@ | ||
167 | |||
168 | $(CPP_DIR)/udev-builtin.c:src/udev-builtin.c \ | ||
169 | src/udev.h \ | ||
170 | src/libudev.h \ | ||
171 | src/libudev-private.h | ||
172 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
173 | $(S_DIR)/udev-builtin.s:$(CPP_DIR)/udev-builtin.c | ||
174 | $(CC) $(CFLAGS) $< -o $@ | ||
175 | $(OBJ_DIR)/udev-builtin.o:$(S_DIR)/udev-builtin.s | ||
176 | $(AS) $(ASFLAGS) $< -o $@ | ||
177 | |||
178 | $(CPP_DIR)/udev-builtin-blkid.c:src/udev-builtin-blkid.c \ | ||
179 | src/udev.h \ | ||
180 | src/libudev.h \ | ||
181 | src/libudev-private.h | ||
182 | $(CPP) $$($(PKG_CONFIG) --cflags-only-I blkid) $(CPPFLAGS) $< -o $@ | ||
183 | $(S_DIR)/udev-builtin-blkid.s:$(CPP_DIR)/udev-builtin-blkid.c | ||
184 | $(CC) $$($(PKG_CONFIG) --cflags-only-other blkid) $(CFLAGS) $< -o $@ | ||
185 | $(OBJ_DIR)/udev-builtin-blkid.o:$(S_DIR)/udev-builtin-blkid.s | ||
186 | $(AS) $(ASFLAGS) $< -o $@ | ||
187 | |||
188 | $(CPP_DIR)/udev-builtin-firmware.c:src/udev-builtin-firmware.c \ | ||
189 | src/udev.h \ | ||
190 | src/libudev.h \ | ||
191 | src/libudev-private.h | ||
192 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
193 | $(S_DIR)/udev-builtin-firmware.s:$(CPP_DIR)/udev-builtin-firmware.c | ||
194 | $(CC) $(CFLAGS) $< -o $@ | ||
195 | $(OBJ_DIR)/udev-builtin-firmware.o:$(S_DIR)/udev-builtin-firmware.s | ||
196 | $(AS) $(ASFLAGS) $< -o $@ | ||
197 | |||
198 | $(CPP_DIR)/udev-builtin-hwdb.c:src/udev-builtin-hwdb.c \ | ||
199 | src/udev.h \ | ||
200 | src/libudev.h \ | ||
201 | src/libudev-private.h | ||
202 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
203 | $(S_DIR)/udev-builtin-hwdb.s:$(CPP_DIR)/udev-builtin-hwdb.c | ||
204 | $(CC) $(CFLAGS) $< -o $@ | ||
205 | $(OBJ_DIR)/udev-builtin-hwdb.o:$(S_DIR)/udev-builtin-hwdb.s | ||
206 | $(AS) $(ASFLAGS) $< -o $@ | ||
207 | |||
208 | $(CPP_DIR)/udev-builtin-input_id.c:src/udev-builtin-input_id.c \ | ||
209 | src/udev.h \ | ||
210 | src/libudev.h \ | ||
211 | src/libudev-private.h | ||
212 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
213 | $(S_DIR)/udev-builtin-input_id.s:$(CPP_DIR)/udev-builtin-input_id.c | ||
214 | $(CC) $(CFLAGS) $< -o $@ | ||
215 | $(OBJ_DIR)/udev-builtin-input_id.o:$(S_DIR)/udev-builtin-input_id.s | ||
216 | $(AS) $(ASFLAGS) $< -o $@ | ||
217 | |||
218 | $(CPP_DIR)/udev-builtin-kmod.c:src/udev-builtin-kmod.c \ | ||
219 | src/udev.h \ | ||
220 | src/libudev.h \ | ||
221 | src/libudev-private.h | ||
222 | $(CPP) $$($(PKG_CONFIG) --cflags-only-I libkmod) $(CPPFLAGS) $< -o $@ | ||
223 | $(S_DIR)/udev-builtin-kmod.s:$(CPP_DIR)/udev-builtin-kmod.c | ||
224 | $(CC) $$($(PKG_CONFIG) --cflags-only-other libkmod) $(CFLAGS) $< -o $@ | ||
225 | $(OBJ_DIR)/udev-builtin-kmod.o:$(S_DIR)/udev-builtin-kmod.s | ||
226 | $(AS) $(ASFLAGS) $< -o $@ | ||
227 | |||
228 | $(CPP_DIR)/udev-builtin-path_id.c:src/udev-builtin-path_id.c \ | ||
229 | src/udev.h \ | ||
230 | src/libudev.h \ | ||
231 | src/libudev-private.h | ||
232 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
233 | $(S_DIR)/udev-builtin-path_id.s:$(CPP_DIR)/udev-builtin-path_id.c | ||
234 | $(CC) $(CFLAGS) $< -o $@ | ||
235 | $(OBJ_DIR)/udev-builtin-path_id.o:$(S_DIR)/udev-builtin-path_id.s | ||
236 | $(AS) $(ASFLAGS) $< -o $@ | ||
237 | |||
238 | $(CPP_DIR)/udev-builtin-usb_id.c:src/udev-builtin-usb_id.c \ | ||
239 | src/udev.h \ | ||
240 | src/libudev.h \ | ||
241 | src/libudev-private.h | ||
242 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
243 | $(S_DIR)/udev-builtin-usb_id.s:$(CPP_DIR)/udev-builtin-usb_id.c | ||
244 | $(CC) $(CFLAGS) $< -o $@ | ||
245 | $(OBJ_DIR)/udev-builtin-usb_id.o:$(S_DIR)/udev-builtin-usb_id.s | ||
246 | $(AS) $(ASFLAGS) $< -o $@ | ||
247 | |||
248 | $(CPP_DIR)/udevd.c:src/udevd.c \ | ||
249 | src/udev.h \ | ||
250 | src/libudev.h \ | ||
251 | src/libudev-private.h | ||
252 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
253 | $(S_DIR)/udevd.s:$(CPP_DIR)/udevd.c | ||
254 | $(CC) $(CFLAGS) $< -o $@ | ||
255 | $(OBJ_DIR)/udevd.o:$(S_DIR)/udevd.s | ||
256 | $(AS) $(ASFLAGS) $< -o $@ | ||
257 | #------------------------------------------------------------------------------- | ||
258 | $(BIN_DIR)/udevd:$(LIB_DIR)/libudev.so.$(LIBUDEV_VERSION) $(UDEVD_OBJS) | ||
259 | $(CC) $(LIB_DIR)/libudev.so.$(LIBUDEV_VERSION) $(UDEVD_OBJS) \ | ||
260 | $$($(PKG_CONFIG) --libs blkid) \ | ||
261 | $$($(PKG_CONFIG) --libs libkmod) \ | ||
262 | -o $@ | ||
263 | #******************************************************************************* | ||
264 | |||
265 | #******************************************************************************* | ||
266 | #udevadm | ||
267 | $(CPP_DIR)/udevadm.c:src/udevadm.c \ | ||
268 | src/udev.h \ | ||
269 | src/libudev.h \ | ||
270 | src/libudev-private.h | ||
271 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
272 | $(S_DIR)/udevadm.s:$(CPP_DIR)/udevadm.c | ||
273 | $(CC) $(CFLAGS) $< -o $@ | ||
274 | $(OBJ_DIR)/udevadm.o:$(S_DIR)/udevadm.s | ||
275 | $(AS) $(ASFLAGS) $< -o $@ | ||
276 | |||
277 | $(CPP_DIR)/udevadm-info.c:src/udevadm-info.c \ | ||
278 | src/udev.h \ | ||
279 | src/libudev.h \ | ||
280 | src/libudev-private.h | ||
281 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
282 | $(S_DIR)/udevadm-info.s:$(CPP_DIR)/udevadm-info.c | ||
283 | $(CC) $(CFLAGS) $< -o $@ | ||
284 | $(OBJ_DIR)/udevadm-info.o:$(S_DIR)/udevadm-info.s | ||
285 | $(AS) $(ASFLAGS) $< -o $@ | ||
286 | |||
287 | $(CPP_DIR)/udevadm-control.c:src/udevadm-control.c \ | ||
288 | src/udev.h \ | ||
289 | src/libudev.h \ | ||
290 | src/libudev-private.h | ||
291 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
292 | $(S_DIR)/udevadm-control.s:$(CPP_DIR)/udevadm-control.c | ||
293 | $(CC) $(CFLAGS) $< -o $@ | ||
294 | $(OBJ_DIR)/udevadm-control.o:$(S_DIR)/udevadm-control.s | ||
295 | $(AS) $(ASFLAGS) $< -o $@ | ||
296 | |||
297 | $(CPP_DIR)/udevadm-monitor.c:src/udevadm-monitor.c \ | ||
298 | src/udev.h \ | ||
299 | src/libudev.h \ | ||
300 | src/libudev-private.h | ||
301 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
302 | $(S_DIR)/udevadm-monitor.s:$(CPP_DIR)/udevadm-monitor.c | ||
303 | $(CC) $(CFLAGS) $< -o $@ | ||
304 | $(OBJ_DIR)/udevadm-monitor.o:$(S_DIR)/udevadm-monitor.s | ||
305 | $(AS) $(ASFLAGS) $< -o $@ | ||
306 | |||
307 | $(CPP_DIR)/udevadm-settle.c:src/udevadm-settle.c \ | ||
308 | src/udev.h \ | ||
309 | src/libudev.h \ | ||
310 | src/libudev-private.h | ||
311 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
312 | $(S_DIR)/udevadm-settle.s:$(CPP_DIR)/udevadm-settle.c | ||
313 | $(CC) $(CFLAGS) $< -o $@ | ||
314 | $(OBJ_DIR)/udevadm-settle.o:$(S_DIR)/udevadm-settle.s | ||
315 | $(AS) $(ASFLAGS) $< -o $@ | ||
316 | |||
317 | $(CPP_DIR)/udevadm-trigger.c:src/udevadm-trigger.c \ | ||
318 | src/udev.h \ | ||
319 | src/libudev.h \ | ||
320 | src/libudev-private.h | ||
321 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
322 | $(S_DIR)/udevadm-trigger.s:$(CPP_DIR)/udevadm-trigger.c | ||
323 | $(CC) $(CFLAGS) $< -o $@ | ||
324 | $(OBJ_DIR)/udevadm-trigger.o:$(S_DIR)/udevadm-trigger.s | ||
325 | $(AS) $(ASFLAGS) $< -o $@ | ||
326 | |||
327 | $(CPP_DIR)/udevadm-test.c:src/udevadm-test.c \ | ||
328 | src/udev.h \ | ||
329 | src/libudev.h \ | ||
330 | src/libudev-private.h | ||
331 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
332 | $(S_DIR)/udevadm-test.s:$(CPP_DIR)/udevadm-test.c | ||
333 | $(CC) $(CFLAGS) $< -o $@ | ||
334 | $(OBJ_DIR)/udevadm-test.o:$(S_DIR)/udevadm-test.s | ||
335 | $(AS) $(ASFLAGS) $< -o $@ | ||
336 | |||
337 | $(CPP_DIR)/udevadm-test-builtin.c:src/udevadm-test-builtin.c \ | ||
338 | src/udev.h \ | ||
339 | src/libudev.h \ | ||
340 | src/libudev-private.h | ||
341 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
342 | $(S_DIR)/udevadm-test-builtin.s:$(CPP_DIR)/udevadm-test-builtin.c | ||
343 | $(CC) $(CFLAGS) $< -o $@ | ||
344 | $(OBJ_DIR)/udevadm-test-builtin.o:$(S_DIR)/udevadm-test-builtin.s | ||
345 | $(AS) $(ASFLAGS) $< -o $@ | ||
346 | #------------------------------------------------------------------------------- | ||
347 | $(BIN_DIR)/udevadm:$(LIB_DIR)/libudev.so.$(LIBUDEV_VERSION) $(UDEVADM_OBJS) | ||
348 | $(CC) $(LIB_DIR)/libudev.so.$(LIBUDEV_VERSION) $(UDEVADM_OBJS) \ | ||
349 | $$($(PKG_CONFIG) --libs blkid) \ | ||
350 | $$($(PKG_CONFIG) --libs libkmod) \ | ||
351 | -o $@ | ||
352 | #******************************************************************************* | ||
353 | |||
354 | #******************************************************************************* | ||
355 | #libudev | ||
356 | $(CPP_DIR)/libudev.c:src/libudev.c \ | ||
357 | src/libudev.h \ | ||
358 | src/libudev-private.h | ||
359 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
360 | $(S_DIR)/libudev.s:$(CPP_DIR)/libudev.c | ||
361 | $(CC) -fPIC $(CFLAGS) $< -o $@ | ||
362 | $(OBJ_DIR)/libudev.o:$(S_DIR)/libudev.s | ||
363 | $(AS) $(ASFLAGS) $< -o $@ | ||
364 | |||
365 | $(CPP_DIR)/libudev-device.c:src/libudev-device.c \ | ||
366 | src/libudev.h \ | ||
367 | src/libudev-private.h | ||
368 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
369 | $(S_DIR)/libudev-device.s:$(CPP_DIR)/libudev-device.c | ||
370 | $(CC) -fPIC $(CFLAGS) $< -o $@ | ||
371 | $(OBJ_DIR)/libudev-device.o:$(S_DIR)/libudev-device.s | ||
372 | $(AS) $(ASFLAGS) $< -o $@ | ||
373 | |||
374 | $(CPP_DIR)/libudev-device-private.c:src/libudev-device-private.c \ | ||
375 | src/libudev.h \ | ||
376 | src/libudev-private.h | ||
377 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
378 | $(S_DIR)/libudev-device-private.s:$(CPP_DIR)/libudev-device-private.c | ||
379 | $(CC) -fPIC $(CFLAGS) $< -o $@ | ||
380 | $(OBJ_DIR)/libudev-device-private.o:$(S_DIR)/libudev-device-private.s | ||
381 | $(AS) $(ASFLAGS) $< -o $@ | ||
382 | |||
383 | $(CPP_DIR)/libudev-enumerate.c:src/libudev-enumerate.c \ | ||
384 | src/libudev.h \ | ||
385 | src/libudev-private.h | ||
386 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
387 | $(S_DIR)/libudev-enumerate.s:$(CPP_DIR)/libudev-enumerate.c | ||
388 | $(CC) -fPIC $(CFLAGS) $< -o $@ | ||
389 | $(OBJ_DIR)/libudev-enumerate.o:$(S_DIR)/libudev-enumerate.s | ||
390 | $(AS) $(ASFLAGS) $< -o $@ | ||
391 | |||
392 | $(CPP_DIR)/libudev-list.c:src/libudev-list.c \ | ||
393 | src/libudev.h \ | ||
394 | src/libudev-private.h | ||
395 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
396 | $(S_DIR)/libudev-list.s:$(CPP_DIR)/libudev-list.c | ||
397 | $(CC) -fPIC $(CFLAGS) $< -o $@ | ||
398 | $(OBJ_DIR)/libudev-list.o:$(S_DIR)/libudev-list.s | ||
399 | $(AS) $(ASFLAGS) $< -o $@ | ||
400 | |||
401 | $(CPP_DIR)/libudev-monitor.c:src/libudev-monitor.c \ | ||
402 | src/libudev.h \ | ||
403 | src/libudev-private.h | ||
404 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
405 | $(S_DIR)/libudev-monitor.s:$(CPP_DIR)/libudev-monitor.c | ||
406 | $(CC) -fPIC $(CFLAGS) $< -o $@ | ||
407 | $(OBJ_DIR)/libudev-monitor.o:$(S_DIR)/libudev-monitor.s | ||
408 | $(AS) $(ASFLAGS) $< -o $@ | ||
409 | |||
410 | $(CPP_DIR)/libudev-queue.c:src/libudev-queue.c \ | ||
411 | src/libudev.h \ | ||
412 | src/libudev-private.h | ||
413 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
414 | $(S_DIR)/libudev-queue.s:$(CPP_DIR)/libudev-queue.c | ||
415 | $(CC) -fPIC $(CFLAGS) $< -o $@ | ||
416 | $(OBJ_DIR)/libudev-queue.o:$(S_DIR)/libudev-queue.s | ||
417 | $(AS) $(ASFLAGS) $< -o $@ | ||
418 | |||
419 | $(CPP_DIR)/libudev-queue-private.c:src/libudev-queue-private.c \ | ||
420 | src/libudev.h \ | ||
421 | src/libudev-private.h | ||
422 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
423 | $(S_DIR)/libudev-queue-private.s:$(CPP_DIR)/libudev-queue-private.c | ||
424 | $(CC) -fPIC $(CFLAGS) $< -o $@ | ||
425 | $(OBJ_DIR)/libudev-queue-private.o:$(S_DIR)/libudev-queue-private.s | ||
426 | $(AS) $(ASFLAGS) $< -o $@ | ||
427 | |||
428 | $(CPP_DIR)/libudev-util.c:src/libudev-util.c \ | ||
429 | src/libudev.h \ | ||
430 | src/libudev-private.h | ||
431 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
432 | $(S_DIR)/libudev-util.s:$(CPP_DIR)/libudev-util.c | ||
433 | $(CC) -fPIC $(CFLAGS) $< -o $@ | ||
434 | $(OBJ_DIR)/libudev-util.o:$(S_DIR)/libudev-util.s | ||
435 | $(AS) $(ASFLAGS) $< -o $@ | ||
436 | |||
437 | $(CPP_DIR)/libudev-util-private.c:src/libudev-util-private.c \ | ||
438 | src/libudev.h \ | ||
439 | src/libudev-private.h | ||
440 | $(CPP) $(CPPFLAGS) $< -o $@ | ||
441 | $(S_DIR)/libudev-util-private.s:$(CPP_DIR)/libudev-util-private.c | ||
442 | $(CC) -fPIC $(CFLAGS) $< -o $@ | ||
443 | $(OBJ_DIR)/libudev-util-private.o:$(S_DIR)/libudev-util-private.s | ||
444 | $(AS) $(ASFLAGS) $< -o $@ | ||
445 | #------------------------------------------------------------------------------- | ||
446 | $(LIB_DIR)/libudev.so.$(LIBUDEV_VERSION):$(LIBUDEV_OBJS) | ||
447 | $(LD) -o $@ -shared $(LDFLAGS) -Wl,-h,libudev.so.$(LIBUDEV_API) \ | ||
448 | $(LIBUDEV_OBJS) -lc -lrt | ||
449 | #******************************************************************************* |
File rules/42-usb-hid-pm.rules added (mode: 100644) (index 0000000..d5d5897) | |||
1 | # | ||
2 | # Enable autosuspend for qemu emulated usb hid devices. | ||
3 | # | ||
4 | # Note that there are buggy qemu versions which advertise remote | ||
5 | # wakeup support but don't actually implement it correctly. This | ||
6 | # is the reason why we need a match for the serial number here. | ||
7 | # The serial number "42" is used to tag the implementations where | ||
8 | # remote wakeup is working. | ||
9 | # | ||
10 | |||
11 | ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Mouse", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto" | ||
12 | ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Tablet", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto" | ||
13 | ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Keyboard", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto" | ||
14 | |||
15 | # | ||
16 | # Enable autosuspend for KVM and iLO usb hid devices. These are | ||
17 | # effectively self-powered (despite what some claim in their USB | ||
18 | # profiles) and so it's safe to do so. | ||
19 | # | ||
20 | |||
21 | # AMI 046b:ff10 | ||
22 | ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="046b", ATTR{idProduct}=="ff10", TEST=="power/control", ATTR{power/control}="auto" | ||
23 | |||
24 | # | ||
25 | # Catch-all for Avocent HID devices. Keyed off interface in order to only | ||
26 | # trigger on HID class devices. | ||
27 | # | ||
28 | ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="0624", ATTR{bInterfaceClass}=="03", TEST=="../power/control", ATTR{../power/control}="auto" | ||
29 | |||
30 | # Dell DRAC 4 | ||
31 | ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="413c", ATTR{idProduct}=="2500", TEST=="power/control", ATTR{power/control}="auto" | ||
32 | |||
33 | # Dell DRAC 5 | ||
34 | ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="413c", ATTR{idProduct}=="0000", TEST=="power/control", ATTR{power/control}="auto" | ||
35 | |||
36 | # Hewlett Packard iLO | ||
37 | ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="03f0", ATTR{idProduct}=="7029", TEST=="power/control", ATTR{power/control}="auto" | ||
38 | ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="03f0", ATTR{idProduct}=="1027", TEST=="power/control", ATTR{power/control}="auto" | ||
39 | |||
40 | # IBM remote access | ||
41 | ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="04b3", ATTR{idProduct}=="4001", TEST=="power/control", ATTR{power/control}="auto" | ||
42 | ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="04b3", ATTR{idProduct}=="4002", TEST=="power/control", ATTR{power/control}="auto" | ||
43 | ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="04b3", ATTR{idProduct}=="4012", TEST=="power/control", ATTR{power/control}="auto" | ||
44 | |||
45 | # Raritan Computer, Inc KVM. | ||
46 | ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="14dd", ATTR{idProduct}="0002", TEST=="power/control", ATTR{power/control}="auto" | ||
47 | |||
48 | # USB HID devices that are internal to the machine should also be safe to autosuspend | ||
49 | ACTION=="add", SUBSYSTEM=="usb", ATTR{bInterfaceClass}=="03", ATTRS{removable}=="fixed", TEST=="../power/control", ATTR{../power/control}="auto" |
File rules/50-udev-default.rules added (mode: 100644) (index 0000000..5ad787f) | |||
1 | # do not edit this file, it will be overwritten on update | ||
2 | |||
3 | KERNEL=="pty[pqrstuvwxyzabcdef][0123456789abcdef]", GROUP="tty", MODE="0660" | ||
4 | KERNEL=="tty[pqrstuvwxyzabcdef][0123456789abcdef]", GROUP="tty", MODE="0660" | ||
5 | KERNEL=="ptmx", GROUP="tty", MODE="0666" | ||
6 | KERNEL=="tty", GROUP="tty", MODE="0666" | ||
7 | KERNEL=="tty[0-9]*", GROUP="tty", MODE="0620" | ||
8 | KERNEL=="vcs|vcs[0-9]*|vcsa|vcsa[0-9]*", GROUP="tty" | ||
9 | |||
10 | # serial | ||
11 | KERNEL=="tty[A-Z]*[0-9]|pppox[0-9]*|ircomm[0-9]*|noz[0-9]*|rfcomm[0-9]*", GROUP="dialout" | ||
12 | KERNEL=="mwave", GROUP="dialout" | ||
13 | KERNEL=="hvc*|hvsi*", GROUP="dialout" | ||
14 | |||
15 | # virtio serial / console ports | ||
16 | KERNEL=="vport*", ATTR{name}=="?*", SYMLINK+="virtio-ports/$attr{name}" | ||
17 | |||
18 | # mem | ||
19 | KERNEL=="null|zero|full|random|urandom", MODE="0666" | ||
20 | KERNEL=="mem|kmem|port|nvram", GROUP="kmem", MODE="0640" | ||
21 | |||
22 | # input | ||
23 | SUBSYSTEM=="input", ENV{ID_INPUT}=="", IMPORT{builtin}="input_id" | ||
24 | KERNEL=="mouse*|mice|event*", MODE="0640" | ||
25 | KERNEL=="ts[0-9]*|uinput", MODE="0640" | ||
26 | KERNEL=="js[0-9]*", MODE="0644" | ||
27 | |||
28 | # video4linux | ||
29 | SUBSYSTEM=="video4linux", GROUP="video" | ||
30 | KERNEL=="vttuner*", GROUP="video" | ||
31 | KERNEL=="vtx*|vbi*", GROUP="video" | ||
32 | KERNEL=="winradio*", GROUP="video" | ||
33 | |||
34 | # graphics | ||
35 | KERNEL=="agpgart", GROUP="video" | ||
36 | KERNEL=="pmu", GROUP="video" | ||
37 | KERNEL=="nvidia*|nvidiactl*", GROUP="video" | ||
38 | SUBSYSTEM=="graphics", GROUP="video" | ||
39 | SUBSYSTEM=="drm", GROUP="video" | ||
40 | |||
41 | # sound | ||
42 | SUBSYSTEM=="sound", GROUP="audio", \ | ||
43 | OPTIONS+="static_node=snd/seq", OPTIONS+="static_node=snd/timer" | ||
44 | |||
45 | # DVB (video) | ||
46 | SUBSYSTEM=="dvb", GROUP="video" | ||
47 | |||
48 | # FireWire (firewire-core driver: IIDC devices, AV/C devices) | ||
49 | SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x00010*", GROUP="video" | ||
50 | SUBSYSTEM=="firewire", ATTR{units}=="*0x00b09d:0x00010*", GROUP="video" | ||
51 | SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x010001*", GROUP="video" | ||
52 | SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", GROUP="video" | ||
53 | |||
54 | # 'libusb' device nodes | ||
55 | SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0664" | ||
56 | SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id" | ||
57 | |||
58 | # printer | ||
59 | KERNEL=="parport[0-9]*", GROUP="lp" | ||
60 | SUBSYSTEM=="printer", KERNEL=="lp*", GROUP="lp" | ||
61 | SUBSYSTEM=="ppdev", GROUP="lp" | ||
62 | KERNEL=="lp[0-9]*", GROUP="lp" | ||
63 | KERNEL=="irlpt[0-9]*", GROUP="lp" | ||
64 | SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", GROUP="lp" | ||
65 | |||
66 | # block | ||
67 | SUBSYSTEM=="block", GROUP="disk" | ||
68 | |||
69 | # floppy | ||
70 | SUBSYSTEM=="block", KERNEL=="fd[0-9]", GROUP="floppy" | ||
71 | |||
72 | # cdrom | ||
73 | SUBSYSTEM=="block", KERNEL=="sr[0-9]*", GROUP="cdrom" | ||
74 | SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", GROUP="cdrom" | ||
75 | KERNEL=="pktcdvd[0-9]*", GROUP="cdrom" | ||
76 | KERNEL=="pktcdvd", GROUP="cdrom" | ||
77 | |||
78 | # tape | ||
79 | KERNEL=="ht[0-9]*|nht[0-9]*", GROUP="tape" | ||
80 | KERNEL=="pt[0-9]*|npt[0-9]*|pht[0-9]*", GROUP="tape" | ||
81 | SUBSYSTEM=="scsi_generic|scsi_tape", SUBSYSTEMS=="scsi", ATTRS{type}=="1|8", GROUP="tape" | ||
82 | |||
83 | # block-related | ||
84 | KERNEL=="sch[0-9]*", GROUP="disk" | ||
85 | SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="0", GROUP="disk" | ||
86 | KERNEL=="pg[0-9]*", GROUP="disk" | ||
87 | KERNEL=="qft[0-9]*|nqft[0-9]*|zqft[0-9]*|nzqft[0-9]*|rawqft[0-9]*|nrawqft[0-9]*", GROUP="disk" | ||
88 | KERNEL=="rawctl", GROUP="disk" | ||
89 | SUBSYSTEM=="raw", KERNEL=="raw[0-9]*", GROUP="disk" | ||
90 | SUBSYSTEM=="aoe", GROUP="disk", MODE="0220" | ||
91 | SUBSYSTEM=="aoe", KERNEL=="err", MODE="0440" | ||
92 | |||
93 | # network | ||
94 | KERNEL=="tun", MODE="0666", OPTIONS+="static_node=net/tun" | ||
95 | KERNEL=="rfkill", MODE="0644" | ||
96 | |||
97 | # CPU | ||
98 | KERNEL=="cpu[0-9]*", MODE="0444" | ||
99 | |||
100 | KERNEL=="fuse", ACTION=="add", MODE="0666", OPTIONS+="static_node=fuse" | ||
101 | |||
102 | SUBSYSTEM=="rtc", ATTR{hctosys}=="1", SYMLINK+="rtc" | ||
103 | KERNEL=="mmtimer", MODE="0644" | ||
104 | KERNEL=="rflash[0-9]*", MODE="0400" | ||
105 | KERNEL=="rrom[0-9]*", MODE="0400" | ||
106 | |||
107 | SUBSYSTEM=="firmware", ACTION=="add", IMPORT{builtin}="firmware" |
File rules/60-persistent-alsa.rules added (mode: 100644) (index 0000000..8154e2d) | |||
1 | # do not edit this file, it will be overwritten on update | ||
2 | |||
3 | ACTION=="remove", GOTO="persistent_alsa_end" | ||
4 | SUBSYSTEM!="sound", GOTO="persistent_alsa_end" | ||
5 | KERNEL!="controlC[0-9]*", GOTO="persistent_alsa_end" | ||
6 | |||
7 | SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id" | ||
8 | ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="?*", SYMLINK+="snd/by-id/$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_USB_INTERFACE_NUM}" | ||
9 | ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="", SYMLINK+="snd/by-id/$env{ID_BUS}-$env{ID_SERIAL}" | ||
10 | |||
11 | IMPORT{builtin}="path_id" | ||
12 | ENV{ID_PATH}=="?*", SYMLINK+="snd/by-path/$env{ID_PATH}" | ||
13 | |||
14 | LABEL="persistent_alsa_end" |
File rules/60-persistent-input.rules added (mode: 100644) (index 0000000..fb798dd) | |||
1 | # do not edit this file, it will be overwritten on update | ||
2 | |||
3 | ACTION=="remove", GOTO="persistent_input_end" | ||
4 | SUBSYSTEM!="input", GOTO="persistent_input_end" | ||
5 | SUBSYSTEMS=="bluetooth", GOTO="persistent_input_end" | ||
6 | |||
7 | SUBSYSTEMS=="usb", ENV{ID_BUS}=="", IMPORT{builtin}="usb_id" | ||
8 | |||
9 | # determine class name for persistent symlinks | ||
10 | ENV{ID_INPUT_KEYBOARD}=="?*", ENV{.INPUT_CLASS}="kbd" | ||
11 | ENV{ID_INPUT_MOUSE}=="?*", ENV{.INPUT_CLASS}="mouse" | ||
12 | ENV{ID_INPUT_TOUCHPAD}=="?*", ENV{.INPUT_CLASS}="mouse" | ||
13 | ENV{ID_INPUT_TABLET}=="?*", ENV{.INPUT_CLASS}="mouse" | ||
14 | ENV{ID_INPUT_JOYSTICK}=="?*", ENV{.INPUT_CLASS}="joystick" | ||
15 | DRIVERS=="pcspkr", ENV{.INPUT_CLASS}="spkr" | ||
16 | ATTRS{name}=="*dvb*|*DVB*|* IR *", ENV{.INPUT_CLASS}="ir" | ||
17 | |||
18 | # fill empty serial number | ||
19 | ENV{.INPUT_CLASS}=="?*", ENV{ID_SERIAL}=="", ENV{ID_SERIAL}="noserial" | ||
20 | |||
21 | # by-id links | ||
22 | KERNEL=="mouse*|js*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-$env{.INPUT_CLASS}" | ||
23 | KERNEL=="mouse*|js*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="?*", ATTRS{bInterfaceNumber}!="00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$attr{bInterfaceNumber}-$env{.INPUT_CLASS}" | ||
24 | KERNEL=="event*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-event-$env{.INPUT_CLASS}" | ||
25 | KERNEL=="event*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="?*", ATTRS{bInterfaceNumber}!="00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$attr{bInterfaceNumber}-event-$env{.INPUT_CLASS}" | ||
26 | # allow empty class for USB devices, by appending the interface number | ||
27 | SUBSYSTEMS=="usb", ENV{ID_BUS}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="", ATTRS{bInterfaceNumber}=="?*", \ | ||
28 | SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-event-if$attr{bInterfaceNumber}" | ||
29 | |||
30 | # by-path | ||
31 | SUBSYSTEMS=="pci|usb|platform|acpi", IMPORT{builtin}="path_id" | ||
32 | ENV{ID_PATH}=="?*", KERNEL=="mouse*|js*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-path/$env{ID_PATH}-$env{.INPUT_CLASS}" | ||
33 | ENV{ID_PATH}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-path/$env{ID_PATH}-event-$env{.INPUT_CLASS}" | ||
34 | # allow empty class for platform and usb devices; platform supports only a single interface that way | ||
35 | SUBSYSTEMS=="usb|platform", ENV{ID_PATH}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="", \ | ||
36 | SYMLINK+="input/by-path/$env{ID_PATH}-event" | ||
37 | |||
38 | LABEL="persistent_input_end" |
File rules/60-persistent-net.rules added (mode: 100644) (index 0000000..3265afe) | |||
1 | # do not edit this file, it will be overwritten on update | ||
2 | # | ||
3 | # General idea: Set up ID_* entries in the udev db that can be looked up by the | ||
4 | # distro's network config later to find the right NIC device to configure. This | ||
5 | # is compatible with the persistent-net-generator rules if those are enabled. | ||
6 | # | ||
7 | # Find the NIC for a given attribute with something like: | ||
8 | # $(find-nic [ mac-{address} | path-{path specifier} | ethN ]) | ||
9 | # | ||
10 | # where find-nic looks something like: | ||
11 | # | ||
12 | # #!/bin/bash | ||
13 | # # Accept real interfaces as well as identifiers | ||
14 | # if [ -e /sys/class/net/"${1}" ] ; then | ||
15 | # echo "${1}" | ||
16 | # exit 0 | ||
17 | # fi | ||
18 | # | ||
19 | # id="${1#*-}" | ||
20 | # | ||
21 | # case "$1" in | ||
22 | # mac-*) | ||
23 | # for dev in /sys/class/net/* ; do | ||
24 | # if /sbin/udevadm info -q env -p "$dev" | grep -q "ID_ADDRESS=${id}\$" ; then | ||
25 | # echo "${dev##*/}" | ||
26 | # exit 0 | ||
27 | # fi | ||
28 | # done | ||
29 | # exit 1 | ||
30 | # ;; | ||
31 | # path-*) | ||
32 | # for dev in /sys/class/net/* ; do | ||
33 | # if /sbin/udevadm info -q env -p "$dev" | grep -q "ID_PATH=${id}\$" ; then | ||
34 | # echo "${dev##*/}" | ||
35 | # exit 0 | ||
36 | # fi | ||
37 | # done | ||
38 | # exit 1 | ||
39 | # ;; | ||
40 | # *) | ||
41 | # echo "Don't know how to dereference $1." >&2 | ||
42 | # exit 2 | ||
43 | # ;; | ||
44 | # esac | ||
45 | |||
46 | ACTION=="remove", GOTO="persistent_net_end" | ||
47 | SUBSYSTEM!="net", GOTO="persistent_net_end" | ||
48 | |||
49 | # Stash away the MAC address into the udev db | ||
50 | ENV{ID_ADDRESS}="$attr{address}" | ||
51 | |||
52 | # Grab a set of path-id exports as well | ||
53 | IMPORT{builtin}="path_id" | ||
54 | |||
55 | LABEL="persistent_net_end" |
File rules/60-persistent-serial.rules added (mode: 100644) (index 0000000..2948200) | |||
1 | # do not edit this file, it will be overwritten on update | ||
2 | |||
3 | ACTION=="remove", GOTO="persistent_serial_end" | ||
4 | SUBSYSTEM!="tty", GOTO="persistent_serial_end" | ||
5 | KERNEL!="ttyUSB[0-9]*|ttyACM[0-9]*", GOTO="persistent_serial_end" | ||
6 | |||
7 | SUBSYSTEMS=="usb-serial", ENV{.ID_PORT}="$attr{port_number}" | ||
8 | |||
9 | IMPORT{builtin}="path_id" | ||
10 | ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="", SYMLINK+="serial/by-path/$env{ID_PATH}" | ||
11 | ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-path/$env{ID_PATH}-port$env{.ID_PORT}" | ||
12 | |||
13 | IMPORT{builtin}="usb_id" | ||
14 | ENV{ID_SERIAL}=="", GOTO="persistent_serial_end" | ||
15 | SUBSYSTEMS=="usb", ENV{ID_USB_INTERFACE_NUM}="$attr{bInterfaceNumber}" | ||
16 | ENV{ID_USB_INTERFACE_NUM}=="", GOTO="persistent_serial_end" | ||
17 | ENV{.ID_PORT}=="", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}" | ||
18 | ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}-port$env{.ID_PORT}" | ||
19 | |||
20 | LABEL="persistent_serial_end" |
File rules/60-persistent-storage-tape.rules added (mode: 100644) (index 0000000..f2eabd9) | |||
1 | # do not edit this file, it will be overwritten on update | ||
2 | |||
3 | # persistent storage links: /dev/tape/{by-id,by-path} | ||
4 | |||
5 | ACTION=="remove", GOTO="persistent_storage_tape_end" | ||
6 | |||
7 | # type 8 devices are "Medium Changers" | ||
8 | SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", IMPORT{program}="scsi_id --sg-version=3 --export --whitelisted -d $devnode", \ | ||
9 | SYMLINK+="tape/by-id/scsi-$env{ID_SERIAL}" | ||
10 | |||
11 | SUBSYSTEM!="scsi_tape", GOTO="persistent_storage_tape_end" | ||
12 | |||
13 | KERNEL=="st*[0-9]|nst*[0-9]", ATTRS{ieee1394_id}=="?*", ENV{ID_SERIAL}="$attr{ieee1394_id}", ENV{ID_BUS}="ieee1394" | ||
14 | KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" | ||
15 | KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", KERNELS=="[0-9]*:*[0-9]", ENV{.BSG_DEV}="$root/bsg/$id" | ||
16 | KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --whitelisted --export --device=$env{.BSG_DEV}", ENV{ID_BUS}="scsi" | ||
17 | KERNEL=="st*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}" | ||
18 | KERNEL=="nst*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}-nst" | ||
19 | |||
20 | # by-path (parent device path) | ||
21 | KERNEL=="st*[0-9]|nst*[0-9]", IMPORT{builtin}="path_id" | ||
22 | KERNEL=="st*[0-9]", ENV{ID_PATH}=="?*", SYMLINK+="tape/by-path/$env{ID_PATH}" | ||
23 | KERNEL=="nst*[0-9]", ENV{ID_PATH}=="?*", SYMLINK+="tape/by-path/$env{ID_PATH}-nst" | ||
24 | |||
25 | LABEL="persistent_storage_tape_end" |
File rules/60-persistent-storage.rules added (mode: 100644) (index 0000000..b74821e) | |||
1 | # do not edit this file, it will be overwritten on update | ||
2 | |||
3 | # persistent storage links: /dev/disk/{by-id,by-uuid,by-label,by-path} | ||
4 | # scheme based on "Linux persistent device names", 2004, Hannes Reinecke <hare@suse.de> | ||
5 | |||
6 | # forward scsi device event to corresponding block device | ||
7 | ACTION=="change", SUBSYSTEM=="scsi", ENV{DEVTYPE}=="scsi_device", TEST=="block", ATTR{block/*/uevent}="change" | ||
8 | |||
9 | ACTION=="remove", GOTO="persistent_storage_end" | ||
10 | |||
11 | # enable in-kernel media-presence polling | ||
12 | ACTION=="add", SUBSYSTEM=="module", KERNEL=="block", ATTR{parameters/events_dfl_poll_msecs}=="0", ATTR{parameters/events_dfl_poll_msecs}="2000" | ||
13 | |||
14 | SUBSYSTEM!="block", GOTO="persistent_storage_end" | ||
15 | |||
16 | # skip rules for inappropriate block devices | ||
17 | KERNEL=="fd*|mtd*|nbd*|gnbd*|btibm*|dm-*|md*", GOTO="persistent_storage_end" | ||
18 | |||
19 | # ignore partitions that span the entire disk | ||
20 | TEST=="whole_disk", GOTO="persistent_storage_end" | ||
21 | |||
22 | # for partitions import parent information | ||
23 | ENV{DEVTYPE}=="partition", IMPORT{parent}="ID_*" | ||
24 | |||
25 | # virtio-blk | ||
26 | KERNEL=="vd*[!0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}" | ||
27 | KERNEL=="vd*[0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}-part%n" | ||
28 | |||
29 | # ATA devices with their own "ata" kernel subsystem | ||
30 | KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="ata", IMPORT{program}="ata_id --export $devnode" | ||
31 | # ATA devices using the "scsi" subsystem | ||
32 | KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", IMPORT{program}="ata_id --export $devnode" | ||
33 | # ATA/ATAPI devices (SPC-3 or later) using the "scsi" subsystem | ||
34 | KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{type}=="5", ATTRS{scsi_level}=="[6-9]*", IMPORT{program}="ata_id --export $devnode" | ||
35 | |||
36 | # Run ata_id on non-removable USB Mass Storage (SATA/PATA disks in enclosures) | ||
37 | KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", ATTR{removable}=="0", SUBSYSTEMS=="usb", IMPORT{program}="ata_id --export $devnode" | ||
38 | # Otherwise fall back to using usb_id for USB devices | ||
39 | KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" | ||
40 | |||
41 | # scsi devices | ||
42 | KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="scsi" | ||
43 | KERNEL=="cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="cciss" | ||
44 | KERNEL=="sd*|sr*|cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}" | ||
45 | KERNEL=="sd*|cciss*", ENV{DEVTYPE}=="partition", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}-part%n" | ||
46 | |||
47 | # firewire | ||
48 | KERNEL=="sd*[!0-9]|sr*", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}" | ||
49 | KERNEL=="sd*[0-9]", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}-part%n" | ||
50 | |||
51 | KERNEL=="mmcblk[0-9]", SUBSYSTEMS=="mmc", ATTRS{name}=="?*", ATTRS{serial}=="?*", ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}" | ||
52 | KERNEL=="mmcblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}-part%n" | ||
53 | KERNEL=="mspblk[0-9]", SUBSYSTEMS=="memstick", ATTRS{name}=="?*", ATTRS{serial}=="?*", ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}" | ||
54 | KERNEL=="mspblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}-part%n" | ||
55 | |||
56 | # by-path (parent device path) | ||
57 | ENV{DEVTYPE}=="disk", DEVPATH!="*/virtual/*", IMPORT{builtin}="path_id" | ||
58 | ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}" | ||
59 | ENV{DEVTYPE}=="partition", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-part%n" | ||
60 | |||
61 | # skip unpartitioned removable media devices from drivers which do not send "change" events | ||
62 | ENV{DEVTYPE}=="disk", KERNEL!="sd*|sr*", ATTR{removable}=="1", GOTO="persistent_storage_end" | ||
63 | |||
64 | # probe filesystem metadata of optical drives which have a media inserted | ||
65 | KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DATA}=="?*", ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="?*", \ | ||
66 | IMPORT{builtin}="blkid --offset=$env{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}" | ||
67 | # single-session CDs do not have ID_CDROM_MEDIA_SESSION_LAST_OFFSET | ||
68 | KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DATA}=="?*", ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="", \ | ||
69 | IMPORT{builtin}="blkid --noraid" | ||
70 | |||
71 | # probe filesystem metadata of disks | ||
72 | KERNEL!="sr*", IMPORT{builtin}="blkid" | ||
73 | |||
74 | # watch metadata changes by tools closing the device after writing | ||
75 | KERNEL!="sr*", OPTIONS+="watch" | ||
76 | |||
77 | # by-label/by-uuid links (filesystem metadata) | ||
78 | ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}" | ||
79 | ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}" | ||
80 | |||
81 | # by-id (World Wide Name) | ||
82 | ENV{DEVTYPE}=="disk", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}" | ||
83 | ENV{DEVTYPE}=="partition", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}-part%n" | ||
84 | |||
85 | # by-partlabel/by-partuuid links (partition metadata) | ||
86 | ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_UUID}=="?*", SYMLINK+="disk/by-partuuid/$env{ID_PART_ENTRY_UUID}" | ||
87 | ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_NAME}=="?*", SYMLINK+="disk/by-partlabel/$env{ID_PART_ENTRY_NAME}" | ||
88 | |||
89 | LABEL="persistent_storage_end" |
File rules/75-net-description.rules added (mode: 100644) (index 0000000..ce57d48) | |||
1 | # do not edit this file, it will be overwritten on update | ||
2 | |||
3 | ACTION=="remove", GOTO="net_end" | ||
4 | SUBSYSTEM!="net", GOTO="net_end" | ||
5 | |||
6 | SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id" | ||
7 | SUBSYSTEMS=="usb", IMPORT{builtin}="usb-db" | ||
8 | SUBSYSTEMS=="usb", ATTRS{idVendor}!="", ATTRS{idProduct}!="", ENV{ID_VENDOR_ID}="$attr{idVendor}", ENV{ID_MODEL_ID}="$attr{idProduct}" | ||
9 | SUBSYSTEMS=="usb", GOTO="net_end" | ||
10 | |||
11 | SUBSYSTEMS=="pci", IMPORT{builtin}="pci-db" | ||
12 | SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" | ||
13 | |||
14 | LABEL="net_end" |
File rules/75-tty-description.rules added (mode: 100644) (index 0000000..2e63e14) | |||
1 | # do not edit this file, it will be overwritten on update | ||
2 | |||
3 | ACTION=="remove", GOTO="tty_end" | ||
4 | SUBSYSTEM!="tty", GOTO="tty_end" | ||
5 | |||
6 | SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id" | ||
7 | SUBSYSTEMS=="usb", IMPORT{builtin}="usb-db" | ||
8 | SUBSYSTEMS=="usb", ATTRS{idVendor}!="", ATTRS{idProduct}!="", ENV{ID_VENDOR_ID}="$attr{idVendor}", ENV{ID_MODEL_ID}="$attr{idProduct}" | ||
9 | SUBSYSTEMS=="usb", GOTO="tty_end" | ||
10 | |||
11 | SUBSYSTEMS=="pci", IMPORT{builtin}="pci-db" | ||
12 | SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" | ||
13 | |||
14 | LABEL="tty_end" |
File rules/78-sound-card.rules added (mode: 100644) (index 0000000..e564441) | |||
1 | # do not edit this file, it will be overwritten on update | ||
2 | |||
3 | SUBSYSTEM!="sound", GOTO="sound_end" | ||
4 | |||
5 | ACTION=="add|change", KERNEL=="controlC*", ATTR{../uevent}="change" | ||
6 | ACTION!="change", GOTO="sound_end" | ||
7 | |||
8 | # Ok, we probably need a little explanation here for what the two lines above | ||
9 | # are good for. | ||
10 | # | ||
11 | # The story goes like this: when ALSA registers a new sound card it emits a | ||
12 | # series of 'add' events to userspace, for the main card device and for all the | ||
13 | # child device nodes that belong to it. udev relays those to applications, | ||
14 | # however only maintains the order between father and child, but not between | ||
15 | # the siblings. The control device node creation can be used as synchronization | ||
16 | # point. All other devices that belong to a card are created in the kernel | ||
17 | # before it. However unfortunately due to the fact that siblings are forwarded | ||
18 | # out of order by udev this fact is lost to applications. | ||
19 | # | ||
20 | # OTOH before an application can open a device it needs to make sure that all | ||
21 | # its device nodes are completely created and set up. | ||
22 | # | ||
23 | # As a workaround for this issue we have added the udev rule above which will | ||
24 | # generate a 'change' event on the main card device from the 'add' event of the | ||
25 | # card's control device. Due to the ordering semantics of udev this event will | ||
26 | # only be relayed after all child devices have finished processing properly. | ||
27 | # When an application needs to listen for appearing devices it can hence look | ||
28 | # for 'change' events only, and ignore the actual 'add' events. | ||
29 | # | ||
30 | # When the application is initialized at the same time as a device is plugged | ||
31 | # in it may need to figure out if the 'change' event has already been triggered | ||
32 | # or not for a card. To find that out we store the flag environment variable | ||
33 | # SOUND_INITIALIZED on the device which simply tells us if the card 'change' | ||
34 | # event has already been processed. | ||
35 | |||
36 | KERNEL!="card*", GOTO="sound_end" | ||
37 | |||
38 | ENV{SOUND_INITIALIZED}="1" | ||
39 | |||
40 | SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" | ||
41 | SUBSYSTEMS=="usb", IMPORT{builtin}="usb-db" | ||
42 | SUBSYSTEMS=="usb", GOTO="skip_pci" | ||
43 | |||
44 | SUBSYSTEMS=="firewire", ATTRS{vendor_name}=="?*", ATTRS{model_name}=="?*", \ | ||
45 | ENV{ID_BUS}="firewire", ENV{ID_VENDOR}="$attr{vendor_name}", ENV{ID_MODEL}="$attr{model_name}" | ||
46 | SUBSYSTEMS=="firewire", ATTRS{guid}=="?*", ENV{ID_ID}="firewire-$attr{guid}" | ||
47 | SUBSYSTEMS=="firewire", GOTO="skip_pci" | ||
48 | |||
49 | |||
50 | SUBSYSTEMS=="pci", IMPORT{builtin}="pci-db" | ||
51 | SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" | ||
52 | |||
53 | LABEL="skip_pci" | ||
54 | |||
55 | ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="?*", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_USB_INTERFACE_NUM}-$attr{id}" | ||
56 | ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}-$attr{id}" | ||
57 | |||
58 | IMPORT{builtin}="path_id" | ||
59 | |||
60 | # The values used here for $SOUND_FORM_FACTOR and $SOUND_CLASS should be kept | ||
61 | # in sync with those defined for PulseAudio's src/pulse/proplist.h | ||
62 | # PA_PROP_DEVICE_FORM_FACTOR, PA_PROP_DEVICE_CLASS properties. | ||
63 | |||
64 | # If the first PCM device of this card has the pcm class 'modem', then the card is a modem | ||
65 | ATTR{pcmC%nD0p/pcm_class}=="modem", ENV{SOUND_CLASS}="modem", GOTO="sound_end" | ||
66 | |||
67 | # Identify cards on the internal PCI bus as internal | ||
68 | SUBSYSTEMS=="pci", DEVPATH=="*/0000:00:??.?/sound/*", ENV{SOUND_FORM_FACTOR}="internal", GOTO="sound_end" | ||
69 | |||
70 | # Devices that also support Image/Video interfaces are most likely webcams | ||
71 | SUBSYSTEMS=="usb", ENV{ID_USB_INTERFACES}=="*:0e????:*", ENV{SOUND_FORM_FACTOR}="webcam", GOTO="sound_end" | ||
72 | |||
73 | # Matching on the model strings is a bit ugly, I admit | ||
74 | ENV{ID_MODEL}=="*[Ss]peaker*", ENV{SOUND_FORM_FACTOR}="speaker", GOTO="sound_end" | ||
75 | ENV{ID_MODEL_FROM_DATABASE}=="*[Ss]peaker*", ENV{SOUND_FORM_FACTOR}="speaker", GOTO="sound_end" | ||
76 | |||
77 | ENV{ID_MODEL}=="*[Hh]eadphone*", ENV{SOUND_FORM_FACTOR}="headphone", GOTO="sound_end" | ||
78 | ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]eadphone*", ENV{SOUND_FORM_FACTOR}="headphone", GOTO="sound_end" | ||
79 | |||
80 | ENV{ID_MODEL}=="*[Hh]eadset*", ENV{SOUND_FORM_FACTOR}="headset", GOTO="sound_end" | ||
81 | ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]eadset*", ENV{SOUND_FORM_FACTOR}="headset", GOTO="sound_end" | ||
82 | |||
83 | ENV{ID_MODEL}=="*[Hh]andset*", ENV{SOUND_FORM_FACTOR}="handset", GOTO="sound_end" | ||
84 | ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]andset*", ENV{SOUND_FORM_FACTOR}="handset", GOTO="sound_end" | ||
85 | |||
86 | ENV{ID_MODEL}=="*[Mm]icrophone*", ENV{SOUND_FORM_FACTOR}="microphone", GOTO="sound_end" | ||
87 | ENV{ID_MODEL_FROM_DATABASE}=="*[Mm]icrophone*", ENV{SOUND_FORM_FACTOR}="microphone", GOTO="sound_end" | ||
88 | |||
89 | LABEL="sound_end" |
File rules/80-drivers.rules added (mode: 100644) (index 0000000..38ebfeb) | |||
1 | # do not edit this file, it will be overwritten on update | ||
2 | |||
3 | ACTION=="remove", GOTO="drivers_end" | ||
4 | |||
5 | DRIVER!="?*", ENV{MODALIAS}=="?*", IMPORT{builtin}="kmod load $env{MODALIAS}" | ||
6 | SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="SD", IMPORT{builtin}="kmod load tifm_sd" | ||
7 | SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="MS", IMPORT{builtin}="kmod load tifm_ms" | ||
8 | SUBSYSTEM=="memstick", IMPORT{builtin}="kmod load ms_block mspro_block" | ||
9 | SUBSYSTEM=="i2o", IMPORT{builtin}="kmod load i2o_block" | ||
10 | SUBSYSTEM=="module", KERNEL=="parport_pc", IMPORT{builtin}="kmod load ppdev" | ||
11 | |||
12 | LABEL="drivers_end" |
File rules/95-udev-late.rules added (mode: 100644) (index 0000000..eca0faa) | |||
1 | # do not edit this file, it will be overwritten on update | ||
2 | |||
3 | # run a command on remove events | ||
4 | ACTION=="remove", ENV{REMOVE_CMD}!="", RUN+="$env{REMOVE_CMD}" |
File src/.udev-builtin-path_id.c.swp added (mode: 100644) (index 0000000..024a874) |
File src/libudev-device-private.c added (mode: 100644) (index 0000000..13fdb8e) | |||
1 | /* | ||
2 | * libudev - interface to udev device information | ||
3 | * | ||
4 | * Copyright (C) 2008-2010 Kay Sievers <kay.sievers@vrfy.org> | ||
5 | * | ||
6 | * This library is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU Lesser General Public | ||
8 | * License as published by the Free Software Foundation; either | ||
9 | * version 2.1 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <stdlib.h> | ||
13 | #include <stdio.h> | ||
14 | #include <string.h> | ||
15 | #include <stddef.h> | ||
16 | #include <stdbool.h> | ||
17 | #include <unistd.h> | ||
18 | #include <fcntl.h> | ||
19 | #include <string.h> | ||
20 | #include <sys/stat.h> | ||
21 | |||
22 | #include "libudev.h" | ||
23 | #include "libudev-private.h" | ||
24 | |||
25 | static void udev_device_tag(struct udev_device *dev, const char *tag, bool add) | ||
26 | { | ||
27 | const char *id; | ||
28 | struct udev *udev = udev_device_get_udev(dev); | ||
29 | char filename[UTIL_PATH_SIZE]; | ||
30 | |||
31 | id = udev_device_get_id_filename(dev); | ||
32 | if (id == NULL) | ||
33 | return; | ||
34 | util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/tags/", tag, "/", id, NULL); | ||
35 | |||
36 | if (add) { | ||
37 | int fd; | ||
38 | |||
39 | util_create_path(udev, filename); | ||
40 | fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444); | ||
41 | if (fd >= 0) | ||
42 | close(fd); | ||
43 | } else { | ||
44 | unlink(filename); | ||
45 | } | ||
46 | } | ||
47 | |||
48 | int udev_device_tag_index(struct udev_device *dev, struct udev_device *dev_old, bool add) | ||
49 | { | ||
50 | struct udev_list_entry *list_entry; | ||
51 | bool found; | ||
52 | |||
53 | if (add && dev_old != NULL) { | ||
54 | /* delete possible left-over tags */ | ||
55 | udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(dev_old)) { | ||
56 | const char *tag_old = udev_list_entry_get_name(list_entry); | ||
57 | struct udev_list_entry *list_entry_current; | ||
58 | |||
59 | found = false; | ||
60 | udev_list_entry_foreach(list_entry_current, udev_device_get_tags_list_entry(dev)) { | ||
61 | const char *tag = udev_list_entry_get_name(list_entry_current); | ||
62 | |||
63 | if (strcmp(tag, tag_old) == 0) { | ||
64 | found = true; | ||
65 | break; | ||
66 | } | ||
67 | } | ||
68 | if (!found) | ||
69 | udev_device_tag(dev_old, tag_old, false); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(dev)) | ||
74 | udev_device_tag(dev, udev_list_entry_get_name(list_entry), add); | ||
75 | |||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static bool device_has_info(struct udev_device *udev_device) | ||
80 | { | ||
81 | struct udev_list_entry *list_entry; | ||
82 | |||
83 | if (udev_device_get_devlinks_list_entry(udev_device) != NULL) | ||
84 | return true; | ||
85 | if (udev_device_get_devlink_priority(udev_device) != 0) | ||
86 | return true; | ||
87 | udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device)) | ||
88 | if (udev_list_entry_get_num(list_entry)) | ||
89 | return true; | ||
90 | if (udev_device_get_tags_list_entry(udev_device) != NULL) | ||
91 | return true; | ||
92 | if (udev_device_get_watch_handle(udev_device) >= 0) | ||
93 | return true; | ||
94 | return false; | ||
95 | } | ||
96 | |||
97 | int udev_device_update_db(struct udev_device *udev_device) | ||
98 | { | ||
99 | bool has_info; | ||
100 | const char *id; | ||
101 | struct udev *udev = udev_device_get_udev(udev_device); | ||
102 | char filename[UTIL_PATH_SIZE]; | ||
103 | char filename_tmp[UTIL_PATH_SIZE]; | ||
104 | FILE *f; | ||
105 | |||
106 | id = udev_device_get_id_filename(udev_device); | ||
107 | if (id == NULL) | ||
108 | return -1; | ||
109 | |||
110 | has_info = device_has_info(udev_device); | ||
111 | util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data/", id, NULL); | ||
112 | |||
113 | /* do not store anything for otherwise empty devices */ | ||
114 | if (!has_info && | ||
115 | major(udev_device_get_devnum(udev_device)) == 0 && | ||
116 | udev_device_get_ifindex(udev_device) == 0) { | ||
117 | unlink(filename); | ||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | /* write a database file */ | ||
122 | util_strscpyl(filename_tmp, sizeof(filename_tmp), filename, ".tmp", NULL); | ||
123 | util_create_path(udev, filename_tmp); | ||
124 | f = fopen(filename_tmp, "we"); | ||
125 | if (f == NULL) { | ||
126 | err(udev, "unable to create temporary db file '%s': %m\n", filename_tmp); | ||
127 | return -1; | ||
128 | } | ||
129 | |||
130 | /* | ||
131 | * set 'sticky' bit to indicate that we should not clean the | ||
132 | * database when we transition from initramfs to the real root | ||
133 | */ | ||
134 | if (udev_device_get_db_persist(udev_device)) | ||
135 | fchmod(fileno(f), 01644); | ||
136 | |||
137 | if (has_info) { | ||
138 | struct udev_list_entry *list_entry; | ||
139 | |||
140 | if (major(udev_device_get_devnum(udev_device)) > 0) { | ||
141 | size_t devlen = strlen(udev_get_dev_path(udev))+1; | ||
142 | |||
143 | udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(udev_device)) | ||
144 | fprintf(f, "S:%s\n", &udev_list_entry_get_name(list_entry)[devlen]); | ||
145 | if (udev_device_get_devlink_priority(udev_device) != 0) | ||
146 | fprintf(f, "L:%i\n", udev_device_get_devlink_priority(udev_device)); | ||
147 | if (udev_device_get_watch_handle(udev_device) >= 0) | ||
148 | fprintf(f, "W:%i\n", udev_device_get_watch_handle(udev_device)); | ||
149 | } | ||
150 | |||
151 | if (udev_device_get_usec_initialized(udev_device) > 0) | ||
152 | fprintf(f, "I:%llu\n", udev_device_get_usec_initialized(udev_device)); | ||
153 | |||
154 | udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device)) { | ||
155 | if (!udev_list_entry_get_num(list_entry)) | ||
156 | continue; | ||
157 | fprintf(f, "E:%s=%s\n", | ||
158 | udev_list_entry_get_name(list_entry), | ||
159 | udev_list_entry_get_value(list_entry)); | ||
160 | } | ||
161 | |||
162 | udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device)) | ||
163 | fprintf(f, "G:%s\n", udev_list_entry_get_name(list_entry)); | ||
164 | } | ||
165 | |||
166 | fclose(f); | ||
167 | rename(filename_tmp, filename); | ||
168 | info(udev, "created %s file '%s' for '%s'\n", has_info ? "db" : "empty", | ||
169 | filename, udev_device_get_devpath(udev_device)); | ||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | int udev_device_delete_db(struct udev_device *udev_device) | ||
174 | { | ||
175 | const char *id; | ||
176 | struct udev *udev = udev_device_get_udev(udev_device); | ||
177 | char filename[UTIL_PATH_SIZE]; | ||
178 | |||
179 | id = udev_device_get_id_filename(udev_device); | ||
180 | if (id == NULL) | ||
181 | return -1; | ||
182 | util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data/", id, NULL); | ||
183 | unlink(filename); | ||
184 | return 0; | ||
185 | } |
File src/libudev-device.c added (mode: 100644) (index 0000000..69b2d7b) | |||
1 | /* | ||
2 | * libudev - interface to udev device information | ||
3 | * | ||
4 | * Copyright (C) 2008-2010 Kay Sievers <kay.sievers@vrfy.org> | ||
5 | * | ||
6 | * This library is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU Lesser General Public | ||
8 | * License as published by the Free Software Foundation; either | ||
9 | * version 2.1 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <stdio.h> | ||
13 | #include <stdlib.h> | ||
14 | #include <stddef.h> | ||
15 | #include <unistd.h> | ||
16 | #include <stdbool.h> | ||
17 | #include <errno.h> | ||
18 | #include <string.h> | ||
19 | #include <dirent.h> | ||
20 | #include <fcntl.h> | ||
21 | #include <ctype.h> | ||
22 | #include <net/if.h> | ||
23 | #include <sys/stat.h> | ||
24 | #include <sys/ioctl.h> | ||
25 | #include <sys/socket.h> | ||
26 | #include <linux/sockios.h> | ||
27 | |||
28 | #include "libudev.h" | ||
29 | #include "libudev-private.h" | ||
30 | |||
31 | /** | ||
32 | * SECTION:libudev-device | ||
33 | * @short_description: kernel sys devices | ||
34 | * | ||
35 | * Representation of kernel sys devices. Devices are uniquely identified | ||
36 | * by their syspath, every device has exactly one path in the kernel sys | ||
37 | * filesystem. Devices usually belong to a kernel subsystem, and and have | ||
38 | * a unique name inside that subsystem. | ||
39 | */ | ||
40 | |||
41 | /** | ||
42 | * udev_device: | ||
43 | * | ||
44 | * Opaque object representing one kernel sys device. | ||
45 | */ | ||
46 | struct udev_device { | ||
47 | struct udev *udev; | ||
48 | struct udev_device *parent_device; | ||
49 | char *syspath; | ||
50 | const char *devpath; | ||
51 | char *sysname; | ||
52 | const char *sysnum; | ||
53 | char *devnode; | ||
54 | mode_t devnode_mode; | ||
55 | char *subsystem; | ||
56 | char *devtype; | ||
57 | char *driver; | ||
58 | char *action; | ||
59 | char *devpath_old; | ||
60 | char *id_filename; | ||
61 | char **envp; | ||
62 | char *monitor_buf; | ||
63 | size_t monitor_buf_len; | ||
64 | struct udev_list devlinks_list; | ||
65 | struct udev_list properties_list; | ||
66 | struct udev_list sysattr_value_list; | ||
67 | struct udev_list sysattr_list; | ||
68 | struct udev_list tags_list; | ||
69 | unsigned long long int seqnum; | ||
70 | unsigned long long int usec_initialized; | ||
71 | int devlink_priority; | ||
72 | int refcount; | ||
73 | dev_t devnum; | ||
74 | int ifindex; | ||
75 | int watch_handle; | ||
76 | int maj, min; | ||
77 | bool parent_set; | ||
78 | bool subsystem_set; | ||
79 | bool devtype_set; | ||
80 | bool devlinks_uptodate; | ||
81 | bool envp_uptodate; | ||
82 | bool tags_uptodate; | ||
83 | bool driver_set; | ||
84 | bool info_loaded; | ||
85 | bool db_loaded; | ||
86 | bool uevent_loaded; | ||
87 | bool is_initialized; | ||
88 | bool sysattr_list_read; | ||
89 | bool db_persist; | ||
90 | }; | ||
91 | |||
92 | /** | ||
93 | * udev_device_get_seqnum: | ||
94 | * @udev_device: udev device | ||
95 | * | ||
96 | * This is only valid if the device was received through a monitor. Devices read from | ||
97 | * sys do not have a sequence number. | ||
98 | * | ||
99 | * Returns: the kernel event sequence number, or 0 if there is no sequence number available. | ||
100 | **/ | ||
101 | UDEV_EXPORT unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device) | ||
102 | { | ||
103 | if (udev_device == NULL) | ||
104 | return 0; | ||
105 | return udev_device->seqnum; | ||
106 | } | ||
107 | |||
108 | static int udev_device_set_seqnum(struct udev_device *udev_device, unsigned long long int seqnum) | ||
109 | { | ||
110 | char num[32]; | ||
111 | |||
112 | udev_device->seqnum = seqnum; | ||
113 | snprintf(num, sizeof(num), "%llu", seqnum); | ||
114 | udev_device_add_property(udev_device, "SEQNUM", num); | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | int udev_device_get_ifindex(struct udev_device *udev_device) | ||
119 | { | ||
120 | if (!udev_device->info_loaded) | ||
121 | udev_device_read_uevent_file(udev_device); | ||
122 | return udev_device->ifindex; | ||
123 | } | ||
124 | |||
125 | static int udev_device_set_ifindex(struct udev_device *udev_device, int ifindex) | ||
126 | { | ||
127 | char num[32]; | ||
128 | |||
129 | udev_device->ifindex = ifindex; | ||
130 | snprintf(num, sizeof(num), "%u", ifindex); | ||
131 | udev_device_add_property(udev_device, "IFINDEX", num); | ||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | /** | ||
136 | * udev_device_get_devnum: | ||
137 | * @udev_device: udev device | ||
138 | * | ||
139 | * Get the device major/minor number. | ||
140 | * | ||
141 | * Returns: the dev_t number. | ||
142 | **/ | ||
143 | UDEV_EXPORT dev_t udev_device_get_devnum(struct udev_device *udev_device) | ||
144 | { | ||
145 | if (udev_device == NULL) | ||
146 | return makedev(0, 0); | ||
147 | if (!udev_device->info_loaded) | ||
148 | udev_device_read_uevent_file(udev_device); | ||
149 | return udev_device->devnum; | ||
150 | } | ||
151 | |||
152 | static int udev_device_set_devnum(struct udev_device *udev_device, dev_t devnum) | ||
153 | { | ||
154 | char num[32]; | ||
155 | |||
156 | udev_device->devnum = devnum; | ||
157 | |||
158 | snprintf(num, sizeof(num), "%u", major(devnum)); | ||
159 | udev_device_add_property(udev_device, "MAJOR", num); | ||
160 | snprintf(num, sizeof(num), "%u", minor(devnum)); | ||
161 | udev_device_add_property(udev_device, "MINOR", num); | ||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | const char *udev_device_get_devpath_old(struct udev_device *udev_device) | ||
166 | { | ||
167 | return udev_device->devpath_old; | ||
168 | } | ||
169 | |||
170 | static int udev_device_set_devpath_old(struct udev_device *udev_device, const char *devpath_old) | ||
171 | { | ||
172 | const char *pos; | ||
173 | |||
174 | free(udev_device->devpath_old); | ||
175 | udev_device->devpath_old = strdup(devpath_old); | ||
176 | if (udev_device->devpath_old == NULL) | ||
177 | return -ENOMEM; | ||
178 | udev_device_add_property(udev_device, "DEVPATH_OLD", udev_device->devpath_old); | ||
179 | |||
180 | pos = strrchr(udev_device->devpath_old, '/'); | ||
181 | if (pos == NULL) | ||
182 | return -EINVAL; | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | /** | ||
187 | * udev_device_get_driver: | ||
188 | * @udev_device: udev device | ||
189 | * | ||
190 | * Get the kernel driver name. | ||
191 | * | ||
192 | * Returns: the driver name string, or #NULL if there is no driver attached. | ||
193 | **/ | ||
194 | UDEV_EXPORT const char *udev_device_get_driver(struct udev_device *udev_device) | ||
195 | { | ||
196 | char driver[UTIL_NAME_SIZE]; | ||
197 | |||
198 | if (udev_device == NULL) | ||
199 | return NULL; | ||
200 | if (!udev_device->driver_set) { | ||
201 | udev_device->driver_set = true; | ||
202 | if (util_get_sys_core_link_value(udev_device->udev, "driver", udev_device->syspath, driver, sizeof(driver)) > 0) | ||
203 | udev_device->driver = strdup(driver); | ||
204 | } | ||
205 | return udev_device->driver; | ||
206 | } | ||
207 | |||
208 | static int udev_device_set_driver(struct udev_device *udev_device, const char *driver) | ||
209 | { | ||
210 | free(udev_device->driver); | ||
211 | udev_device->driver = strdup(driver); | ||
212 | if (udev_device->driver == NULL) | ||
213 | return -ENOMEM; | ||
214 | udev_device->driver_set = true; | ||
215 | udev_device_add_property(udev_device, "DRIVER", udev_device->driver); | ||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | /** | ||
220 | * udev_device_get_devtype: | ||
221 | * @udev_device: udev device | ||
222 | * | ||
223 | * Retrieve the devtype string of the udev device. | ||
224 | * | ||
225 | * Returns: the devtype name of the udev device, or #NULL if it can not be determined | ||
226 | **/ | ||
227 | UDEV_EXPORT const char *udev_device_get_devtype(struct udev_device *udev_device) | ||
228 | { | ||
229 | if (udev_device == NULL) | ||
230 | return NULL; | ||
231 | if (!udev_device->devtype_set) { | ||
232 | udev_device->devtype_set = true; | ||
233 | udev_device_read_uevent_file(udev_device); | ||
234 | } | ||
235 | return udev_device->devtype; | ||
236 | } | ||
237 | |||
238 | static int udev_device_set_devtype(struct udev_device *udev_device, const char *devtype) | ||
239 | { | ||
240 | free(udev_device->devtype); | ||
241 | udev_device->devtype = strdup(devtype); | ||
242 | if (udev_device->devtype == NULL) | ||
243 | return -ENOMEM; | ||
244 | udev_device->devtype_set = true; | ||
245 | udev_device_add_property(udev_device, "DEVTYPE", udev_device->devtype); | ||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | static int udev_device_set_subsystem(struct udev_device *udev_device, const char *subsystem) | ||
250 | { | ||
251 | free(udev_device->subsystem); | ||
252 | udev_device->subsystem = strdup(subsystem); | ||
253 | if (udev_device->subsystem == NULL) | ||
254 | return -ENOMEM; | ||
255 | udev_device->subsystem_set = true; | ||
256 | udev_device_add_property(udev_device, "SUBSYSTEM", udev_device->subsystem); | ||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | /** | ||
261 | * udev_device_get_subsystem: | ||
262 | * @udev_device: udev device | ||
263 | * | ||
264 | * Retrieve the subsystem string of the udev device. The string does not | ||
265 | * contain any "/". | ||
266 | * | ||
267 | * Returns: the subsystem name of the udev device, or #NULL if it can not be determined | ||
268 | **/ | ||
269 | UDEV_EXPORT const char *udev_device_get_subsystem(struct udev_device *udev_device) | ||
270 | { | ||
271 | char subsystem[UTIL_NAME_SIZE]; | ||
272 | |||
273 | if (udev_device == NULL) | ||
274 | return NULL; | ||
275 | if (!udev_device->subsystem_set) { | ||
276 | udev_device->subsystem_set = true; | ||
277 | /* read "subsystem" link */ | ||
278 | if (util_get_sys_core_link_value(udev_device->udev, "subsystem", udev_device->syspath, subsystem, sizeof(subsystem)) > 0) { | ||
279 | udev_device_set_subsystem(udev_device, subsystem); | ||
280 | return udev_device->subsystem; | ||
281 | } | ||
282 | /* implicit names */ | ||
283 | if (strncmp(udev_device->devpath, "/module/", 8) == 0) { | ||
284 | udev_device_set_subsystem(udev_device, "module"); | ||
285 | return udev_device->subsystem; | ||
286 | } | ||
287 | if (strstr(udev_device->devpath, "/drivers/") != NULL) { | ||
288 | udev_device_set_subsystem(udev_device, "drivers"); | ||
289 | return udev_device->subsystem; | ||
290 | } | ||
291 | if (strncmp(udev_device->devpath, "/subsystem/", 11) == 0 || | ||
292 | strncmp(udev_device->devpath, "/class/", 7) == 0 || | ||
293 | strncmp(udev_device->devpath, "/bus/", 5) == 0) { | ||
294 | udev_device_set_subsystem(udev_device, "subsystem"); | ||
295 | return udev_device->subsystem; | ||
296 | } | ||
297 | } | ||
298 | return udev_device->subsystem; | ||
299 | } | ||
300 | |||
301 | mode_t udev_device_get_devnode_mode(struct udev_device *udev_device) | ||
302 | { | ||
303 | if (!udev_device->info_loaded) | ||
304 | udev_device_read_uevent_file(udev_device); | ||
305 | return udev_device->devnode_mode; | ||
306 | } | ||
307 | |||
308 | static int udev_device_set_devnode_mode(struct udev_device *udev_device, mode_t mode) | ||
309 | { | ||
310 | char num[32]; | ||
311 | |||
312 | udev_device->devnode_mode = mode; | ||
313 | snprintf(num, sizeof(num), "%#o", mode); | ||
314 | udev_device_add_property(udev_device, "DEVMODE", num); | ||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value) | ||
319 | { | ||
320 | udev_device->envp_uptodate = false; | ||
321 | if (value == NULL) { | ||
322 | struct udev_list_entry *list_entry; | ||
323 | |||
324 | list_entry = udev_device_get_properties_list_entry(udev_device); | ||
325 | list_entry = udev_list_entry_get_by_name(list_entry, key); | ||
326 | if (list_entry != NULL) | ||
327 | udev_list_entry_delete(list_entry); | ||
328 | return NULL; | ||
329 | } | ||
330 | return udev_list_entry_add(&udev_device->properties_list, key, value); | ||
331 | } | ||
332 | |||
333 | static struct udev_list_entry *udev_device_add_property_from_string(struct udev_device *udev_device, const char *property) | ||
334 | { | ||
335 | char name[UTIL_LINE_SIZE]; | ||
336 | char *val; | ||
337 | |||
338 | util_strscpy(name, sizeof(name), property); | ||
339 | val = strchr(name, '='); | ||
340 | if (val == NULL) | ||
341 | return NULL; | ||
342 | val[0] = '\0'; | ||
343 | val = &val[1]; | ||
344 | if (val[0] == '\0') | ||
345 | val = NULL; | ||
346 | return udev_device_add_property(udev_device, name, val); | ||
347 | } | ||
348 | |||
349 | /* | ||
350 | * parse property string, and if needed, update internal values accordingly | ||
351 | * | ||
352 | * udev_device_add_property_from_string_parse_finish() needs to be | ||
353 | * called after adding properties, and its return value checked | ||
354 | * | ||
355 | * udev_device_set_info_loaded() needs to be set, to avoid trying | ||
356 | * to use a device without a DEVPATH set | ||
357 | */ | ||
358 | void udev_device_add_property_from_string_parse(struct udev_device *udev_device, const char *property) | ||
359 | { | ||
360 | if (strncmp(property, "DEVPATH=", 8) == 0) { | ||
361 | char path[UTIL_PATH_SIZE]; | ||
362 | |||
363 | util_strscpyl(path, sizeof(path), udev_get_sys_path(udev_device->udev), &property[8], NULL); | ||
364 | udev_device_set_syspath(udev_device, path); | ||
365 | } else if (strncmp(property, "SUBSYSTEM=", 10) == 0) { | ||
366 | udev_device_set_subsystem(udev_device, &property[10]); | ||
367 | } else if (strncmp(property, "DEVTYPE=", 8) == 0) { | ||
368 | udev_device_set_devtype(udev_device, &property[8]); | ||
369 | } else if (strncmp(property, "DEVNAME=", 8) == 0) { | ||
370 | udev_device_set_devnode(udev_device, &property[8]); | ||
371 | } else if (strncmp(property, "DEVLINKS=", 9) == 0) { | ||
372 | char devlinks[UTIL_PATH_SIZE]; | ||
373 | char *slink; | ||
374 | char *next; | ||
375 | |||
376 | util_strscpy(devlinks, sizeof(devlinks), &property[9]); | ||
377 | slink = devlinks; | ||
378 | next = strchr(slink, ' '); | ||
379 | while (next != NULL) { | ||
380 | next[0] = '\0'; | ||
381 | udev_device_add_devlink(udev_device, slink, 0); | ||
382 | slink = &next[1]; | ||
383 | next = strchr(slink, ' '); | ||
384 | } | ||
385 | if (slink[0] != '\0') | ||
386 | udev_device_add_devlink(udev_device, slink, 0); | ||
387 | } else if (strncmp(property, "TAGS=", 5) == 0) { | ||
388 | char tags[UTIL_PATH_SIZE]; | ||
389 | char *next; | ||
390 | |||
391 | util_strscpy(tags, sizeof(tags), &property[5]); | ||
392 | next = strchr(tags, ':'); | ||
393 | if (next != NULL) { | ||
394 | next++; | ||
395 | while (next[0] != '\0') { | ||
396 | char *tag; | ||
397 | |||
398 | tag = next; | ||
399 | next = strchr(tag, ':'); | ||
400 | if (next == NULL) | ||
401 | break; | ||
402 | next[0] = '\0'; | ||
403 | next++; | ||
404 | udev_device_add_tag(udev_device, tag); | ||
405 | } | ||
406 | } | ||
407 | } else if (strncmp(property, "USEC_INITIALIZED=", 19) == 0) { | ||
408 | udev_device_set_usec_initialized(udev_device, strtoull(&property[19], NULL, 10)); | ||
409 | } else if (strncmp(property, "DRIVER=", 7) == 0) { | ||
410 | udev_device_set_driver(udev_device, &property[7]); | ||
411 | } else if (strncmp(property, "ACTION=", 7) == 0) { | ||
412 | udev_device_set_action(udev_device, &property[7]); | ||
413 | } else if (strncmp(property, "MAJOR=", 6) == 0) { | ||
414 | udev_device->maj = strtoull(&property[6], NULL, 10); | ||
415 | } else if (strncmp(property, "MINOR=", 6) == 0) { | ||
416 | udev_device->min = strtoull(&property[6], NULL, 10); | ||
417 | } else if (strncmp(property, "DEVPATH_OLD=", 12) == 0) { | ||
418 | udev_device_set_devpath_old(udev_device, &property[12]); | ||
419 | } else if (strncmp(property, "SEQNUM=", 7) == 0) { | ||
420 | udev_device_set_seqnum(udev_device, strtoull(&property[7], NULL, 10)); | ||
421 | } else if (strncmp(property, "IFINDEX=", 8) == 0) { | ||
422 | udev_device_set_ifindex(udev_device, strtoull(&property[8], NULL, 10)); | ||
423 | } else if (strncmp(property, "DEVMODE=", 8) == 0) { | ||
424 | udev_device_set_devnode_mode(udev_device, strtoul(&property[8], NULL, 8)); | ||
425 | } else { | ||
426 | udev_device_add_property_from_string(udev_device, property); | ||
427 | } | ||
428 | } | ||
429 | |||
430 | int udev_device_add_property_from_string_parse_finish(struct udev_device *udev_device) | ||
431 | { | ||
432 | if (udev_device->maj > 0) | ||
433 | udev_device_set_devnum(udev_device, makedev(udev_device->maj, udev_device->min)); | ||
434 | udev_device->maj = 0; | ||
435 | udev_device->min = 0; | ||
436 | |||
437 | if (udev_device->devpath == NULL || udev_device->subsystem == NULL) | ||
438 | return -EINVAL; | ||
439 | return 0; | ||
440 | } | ||
441 | |||
442 | /** | ||
443 | * udev_device_get_property_value: | ||
444 | * @udev_device: udev device | ||
445 | * @key: property name | ||
446 | * | ||
447 | * Get the value of a given property. | ||
448 | * | ||
449 | * Returns: the property string, or #NULL if there is no such property. | ||
450 | **/ | ||
451 | UDEV_EXPORT const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key) | ||
452 | { | ||
453 | struct udev_list_entry *list_entry; | ||
454 | |||
455 | if (udev_device == NULL) | ||
456 | return NULL; | ||
457 | if (key == NULL) | ||
458 | return NULL; | ||
459 | |||
460 | list_entry = udev_device_get_properties_list_entry(udev_device); | ||
461 | list_entry = udev_list_entry_get_by_name(list_entry, key); | ||
462 | return udev_list_entry_get_value(list_entry); | ||
463 | } | ||
464 | |||
465 | int udev_device_read_db(struct udev_device *udev_device, const char *dbfile) | ||
466 | { | ||
467 | char filename[UTIL_PATH_SIZE]; | ||
468 | char line[UTIL_LINE_SIZE]; | ||
469 | FILE *f; | ||
470 | |||
471 | /* providing a database file will always force-load it */ | ||
472 | if (dbfile == NULL) { | ||
473 | const char *id; | ||
474 | |||
475 | if (udev_device->db_loaded) | ||
476 | return 0; | ||
477 | udev_device->db_loaded = true; | ||
478 | |||
479 | id = udev_device_get_id_filename(udev_device); | ||
480 | if (id == NULL) | ||
481 | return -1; | ||
482 | util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_device->udev), "/data/", id, NULL); | ||
483 | dbfile = filename; | ||
484 | } | ||
485 | |||
486 | f = fopen(dbfile, "re"); | ||
487 | if (f == NULL) { | ||
488 | info(udev_device->udev, "no db file to read %s: %m\n", dbfile); | ||
489 | return -1; | ||
490 | } | ||
491 | udev_device->is_initialized = true; | ||
492 | |||
493 | while (fgets(line, sizeof(line), f)) { | ||
494 | ssize_t len; | ||
495 | const char *val; | ||
496 | struct udev_list_entry *entry; | ||
497 | |||
498 | len = strlen(line); | ||
499 | if (len < 4) | ||
500 | break; | ||
501 | line[len-1] = '\0'; | ||
502 | val = &line[2]; | ||
503 | switch(line[0]) { | ||
504 | case 'S': | ||
505 | util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_device->udev), "/", val, NULL); | ||
506 | udev_device_add_devlink(udev_device, filename, 0); | ||
507 | break; | ||
508 | case 'L': | ||
509 | udev_device_set_devlink_priority(udev_device, atoi(val)); | ||
510 | break; | ||
511 | case 'E': | ||
512 | entry = udev_device_add_property_from_string(udev_device, val); | ||
513 | udev_list_entry_set_num(entry, true); | ||
514 | break; | ||
515 | case 'G': | ||
516 | udev_device_add_tag(udev_device, val); | ||
517 | break; | ||
518 | case 'W': | ||
519 | udev_device_set_watch_handle(udev_device, atoi(val)); | ||
520 | break; | ||
521 | case 'I': | ||
522 | udev_device_set_usec_initialized(udev_device, strtoull(val, NULL, 10)); | ||
523 | break; | ||
524 | } | ||
525 | } | ||
526 | fclose(f); | ||
527 | |||
528 | info(udev_device->udev, "device %p filled with db file data\n", udev_device); | ||
529 | return 0; | ||
530 | } | ||
531 | |||
532 | int udev_device_read_uevent_file(struct udev_device *udev_device) | ||
533 | { | ||
534 | char filename[UTIL_PATH_SIZE]; | ||
535 | FILE *f; | ||
536 | char line[UTIL_LINE_SIZE]; | ||
537 | int maj = 0; | ||
538 | int min = 0; | ||
539 | |||
540 | if (udev_device->uevent_loaded) | ||
541 | return 0; | ||
542 | |||
543 | util_strscpyl(filename, sizeof(filename), udev_device->syspath, "/uevent", NULL); | ||
544 | f = fopen(filename, "re"); | ||
545 | if (f == NULL) | ||
546 | return -1; | ||
547 | udev_device->uevent_loaded = true; | ||
548 | |||
549 | while (fgets(line, sizeof(line), f)) { | ||
550 | char *pos; | ||
551 | |||
552 | pos = strchr(line, '\n'); | ||
553 | if (pos == NULL) | ||
554 | continue; | ||
555 | pos[0] = '\0'; | ||
556 | |||
557 | if (strncmp(line, "DEVTYPE=", 8) == 0) { | ||
558 | udev_device_set_devtype(udev_device, &line[8]); | ||
559 | continue; | ||
560 | } | ||
561 | if (strncmp(line, "IFINDEX=", 8) == 0) { | ||
562 | udev_device_set_ifindex(udev_device, strtoull(&line[8], NULL, 10)); | ||
563 | continue; | ||
564 | } | ||
565 | if (strncmp(line, "DEVNAME=", 8) == 0) { | ||
566 | udev_device_set_devnode(udev_device, &line[8]); | ||
567 | continue; | ||
568 | } | ||
569 | |||
570 | if (strncmp(line, "MAJOR=", 6) == 0) | ||
571 | maj = strtoull(&line[6], NULL, 10); | ||
572 | else if (strncmp(line, "MINOR=", 6) == 0) | ||
573 | min = strtoull(&line[6], NULL, 10); | ||
574 | else if (strncmp(line, "DEVMODE=", 8) == 0) | ||
575 | udev_device->devnode_mode = strtoul(&line[8], NULL, 8); | ||
576 | |||
577 | udev_device_add_property_from_string(udev_device, line); | ||
578 | } | ||
579 | |||
580 | udev_device->devnum = makedev(maj, min); | ||
581 | fclose(f); | ||
582 | return 0; | ||
583 | } | ||
584 | |||
585 | void udev_device_set_info_loaded(struct udev_device *device) | ||
586 | { | ||
587 | device->info_loaded = true; | ||
588 | } | ||
589 | |||
590 | struct udev_device *udev_device_new(struct udev *udev) | ||
591 | { | ||
592 | struct udev_device *udev_device; | ||
593 | struct udev_list_entry *list_entry; | ||
594 | |||
595 | if (udev == NULL) | ||
596 | return NULL; | ||
597 | |||
598 | udev_device = calloc(1, sizeof(struct udev_device)); | ||
599 | if (udev_device == NULL) | ||
600 | return NULL; | ||
601 | udev_device->refcount = 1; | ||
602 | udev_device->udev = udev; | ||
603 | udev_list_init(udev, &udev_device->devlinks_list, true); | ||
604 | udev_list_init(udev, &udev_device->properties_list, true); | ||
605 | udev_list_init(udev, &udev_device->sysattr_value_list, true); | ||
606 | udev_list_init(udev, &udev_device->sysattr_list, false); | ||
607 | udev_list_init(udev, &udev_device->tags_list, true); | ||
608 | udev_device->watch_handle = -1; | ||
609 | /* copy global properties */ | ||
610 | udev_list_entry_foreach(list_entry, udev_get_properties_list_entry(udev)) | ||
611 | udev_device_add_property(udev_device, | ||
612 | udev_list_entry_get_name(list_entry), | ||
613 | udev_list_entry_get_value(list_entry)); | ||
614 | dbg(udev_device->udev, "udev_device: %p created\n", udev_device); | ||
615 | return udev_device; | ||
616 | } | ||
617 | |||
618 | /** | ||
619 | * udev_device_new_from_syspath: | ||
620 | * @udev: udev library context | ||
621 | * @syspath: sys device path including sys directory | ||
622 | * | ||
623 | * Create new udev device, and fill in information from the sys | ||
624 | * device and the udev database entry. The syspath is the absolute | ||
625 | * path to the device, including the sys mount point. | ||
626 | * | ||
627 | * The initial refcount is 1, and needs to be decremented to | ||
628 | * release the resources of the udev device. | ||
629 | * | ||
630 | * Returns: a new udev device, or #NULL, if it does not exist | ||
631 | **/ | ||
632 | UDEV_EXPORT struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath) | ||
633 | { | ||
634 | size_t len; | ||
635 | const char *subdir; | ||
636 | char path[UTIL_PATH_SIZE]; | ||
637 | char *pos; | ||
638 | struct stat statbuf; | ||
639 | struct udev_device *udev_device; | ||
640 | |||
641 | if (udev == NULL) | ||
642 | return NULL; | ||
643 | if (syspath == NULL) | ||
644 | return NULL; | ||
645 | |||
646 | /* path starts in sys */ | ||
647 | len = strlen(udev_get_sys_path(udev)); | ||
648 | if (strncmp(syspath, udev_get_sys_path(udev), len) != 0) { | ||
649 | info(udev, "not in sys :%s\n", syspath); | ||
650 | return NULL; | ||
651 | } | ||
652 | |||
653 | /* path is not a root directory */ | ||
654 | subdir = &syspath[len+1]; | ||
655 | pos = strrchr(subdir, '/'); | ||
656 | if (pos == NULL || pos[1] == '\0' || pos < &subdir[2]) { | ||
657 | dbg(udev, "not a subdir :%s\n", syspath); | ||
658 | return NULL; | ||
659 | } | ||
660 | |||
661 | /* resolve possible symlink to real path */ | ||
662 | util_strscpy(path, sizeof(path), syspath); | ||
663 | util_resolve_sys_link(udev, path, sizeof(path)); | ||
664 | |||
665 | if (strncmp(&path[len], "/devices/", 9) == 0) { | ||
666 | char file[UTIL_PATH_SIZE]; | ||
667 | |||
668 | /* all "devices" require a "uevent" file */ | ||
669 | util_strscpyl(file, sizeof(file), path, "/uevent", NULL); | ||
670 | if (stat(file, &statbuf) != 0) { | ||
671 | dbg(udev, "not a device: %s\n", syspath); | ||
672 | return NULL; | ||
673 | } | ||
674 | } else { | ||
675 | /* everything else just needs to be a directory */ | ||
676 | if (stat(path, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) { | ||
677 | dbg(udev, "directory not found: %s\n", syspath); | ||
678 | return NULL; | ||
679 | } | ||
680 | } | ||
681 | |||
682 | udev_device = udev_device_new(udev); | ||
683 | if (udev_device == NULL) | ||
684 | return NULL; | ||
685 | |||
686 | udev_device_set_syspath(udev_device, path); | ||
687 | info(udev, "device %p has devpath '%s'\n", udev_device, udev_device_get_devpath(udev_device)); | ||
688 | |||
689 | return udev_device; | ||
690 | } | ||
691 | |||
692 | /** | ||
693 | * udev_device_new_from_devnum: | ||
694 | * @udev: udev library context | ||
695 | * @type: char or block device | ||
696 | * @devnum: device major/minor number | ||
697 | * | ||
698 | * Create new udev device, and fill in information from the sys | ||
699 | * device and the udev database entry. The device is looked-up | ||
700 | * by its major/minor number and type. Character and block device | ||
701 | * numbers are not unique across the two types. | ||
702 | * | ||
703 | * The initial refcount is 1, and needs to be decremented to | ||
704 | * release the resources of the udev device. | ||
705 | * | ||
706 | * Returns: a new udev device, or #NULL, if it does not exist | ||
707 | **/ | ||
708 | UDEV_EXPORT struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum) | ||
709 | { | ||
710 | char path[UTIL_PATH_SIZE]; | ||
711 | const char *type_str; | ||
712 | |||
713 | if (type == 'b') | ||
714 | type_str = "block"; | ||
715 | else if (type == 'c') | ||
716 | type_str = "char"; | ||
717 | else | ||
718 | return NULL; | ||
719 | |||
720 | /* use /sys/dev/{block,char}/<maj>:<min> link */ | ||
721 | snprintf(path, sizeof(path), "%s/dev/%s/%u:%u", | ||
722 | udev_get_sys_path(udev), type_str, major(devnum), minor(devnum)); | ||
723 | return udev_device_new_from_syspath(udev, path); | ||
724 | } | ||
725 | |||
726 | /** | ||
727 | * udev_device_new_from_device_id: | ||
728 | * @udev: udev library context | ||
729 | * @id: text string identifying a kernel device | ||
730 | * | ||
731 | * Create new udev device, and fill in information from the sys | ||
732 | * device and the udev database entry. The device is looked-up | ||
733 | * by a special string: | ||
734 | * b8:2 - block device major:minor | ||
735 | * c128:1 - char device major:minor | ||
736 | * n3 - network device ifindex | ||
737 | * +sound:card29 - kernel driver core subsystem:device name | ||
738 | * | ||
739 | * The initial refcount is 1, and needs to be decremented to | ||
740 | * release the resources of the udev device. | ||
741 | * | ||
742 | * Returns: a new udev device, or #NULL, if it does not exist | ||
743 | **/ | ||
744 | UDEV_EXPORT struct udev_device *udev_device_new_from_device_id(struct udev *udev, char *id) | ||
745 | { | ||
746 | char type; | ||
747 | int maj, min; | ||
748 | char subsys[UTIL_PATH_SIZE]; | ||
749 | char *sysname; | ||
750 | |||
751 | switch(id[0]) { | ||
752 | case 'b': | ||
753 | case 'c': | ||
754 | if (sscanf(id, "%c%i:%i", &type, &maj, &min) != 3) | ||
755 | return NULL; | ||
756 | return udev_device_new_from_devnum(udev, type, makedev(maj, min)); | ||
757 | case 'n': { | ||
758 | int sk; | ||
759 | struct ifreq ifr; | ||
760 | struct udev_device *dev; | ||
761 | int ifindex; | ||
762 | |||
763 | ifindex = strtoul(&id[1], NULL, 10); | ||
764 | if (ifindex <= 0) | ||
765 | return NULL; | ||
766 | |||
767 | sk = socket(PF_INET, SOCK_DGRAM, 0); | ||
768 | if (sk < 0) | ||
769 | return NULL; | ||
770 | memset(&ifr, 0x00, sizeof(struct ifreq)); | ||
771 | ifr.ifr_ifindex = ifindex; | ||
772 | if (ioctl(sk, SIOCGIFNAME, &ifr) != 0) { | ||
773 | close(sk); | ||
774 | return NULL; | ||
775 | } | ||
776 | close(sk); | ||
777 | |||
778 | dev = udev_device_new_from_subsystem_sysname(udev, "net", ifr.ifr_name); | ||
779 | if (dev == NULL) | ||
780 | return NULL; | ||
781 | if (udev_device_get_ifindex(dev) == ifindex) | ||
782 | return dev; | ||
783 | udev_device_unref(dev); | ||
784 | return NULL; | ||
785 | } | ||
786 | case '+': | ||
787 | util_strscpy(subsys, sizeof(subsys), &id[1]); | ||
788 | sysname = strchr(subsys, ':'); | ||
789 | if (sysname == NULL) | ||
790 | return NULL; | ||
791 | sysname[0] = '\0'; | ||
792 | sysname = &sysname[1]; | ||
793 | return udev_device_new_from_subsystem_sysname(udev, subsys, sysname); | ||
794 | default: | ||
795 | return NULL; | ||
796 | } | ||
797 | } | ||
798 | |||
799 | /** | ||
800 | * udev_device_new_from_subsystem_sysname: | ||
801 | * @udev: udev library context | ||
802 | * @subsystem: the subsystem of the device | ||
803 | * @sysname: the name of the device | ||
804 | * | ||
805 | * Create new udev device, and fill in information from the sys device | ||
806 | * and the udev database entry. The device is looked up by the subsystem | ||
807 | * and name string of the device, like "mem" / "zero", or "block" / "sda". | ||
808 | * | ||
809 | * The initial refcount is 1, and needs to be decremented to | ||
810 | * release the resources of the udev device. | ||
811 | * | ||
812 | * Returns: a new udev device, or #NULL, if it does not exist | ||
813 | **/ | ||
814 | UDEV_EXPORT struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname) | ||
815 | { | ||
816 | char path_full[UTIL_PATH_SIZE]; | ||
817 | char *path; | ||
818 | size_t l; | ||
819 | struct stat statbuf; | ||
820 | |||
821 | path = path_full; | ||
822 | l = util_strpcpyl(&path, sizeof(path_full), udev_get_sys_path(udev), NULL); | ||
823 | |||
824 | if (strcmp(subsystem, "subsystem") == 0) { | ||
825 | util_strscpyl(path, l, "/subsystem/", sysname, NULL); | ||
826 | if (stat(path_full, &statbuf) == 0) | ||
827 | goto found; | ||
828 | |||
829 | util_strscpyl(path, l, "/bus/", sysname, NULL); | ||
830 | if (stat(path_full, &statbuf) == 0) | ||
831 | goto found; | ||
832 | |||
833 | util_strscpyl(path, l, "/class/", sysname, NULL); | ||
834 | if (stat(path_full, &statbuf) == 0) | ||
835 | goto found; | ||
836 | goto out; | ||
837 | } | ||
838 | |||
839 | if (strcmp(subsystem, "module") == 0) { | ||
840 | util_strscpyl(path, l, "/module/", sysname, NULL); | ||
841 | if (stat(path_full, &statbuf) == 0) | ||
842 | goto found; | ||
843 | goto out; | ||
844 | } | ||
845 | |||
846 | if (strcmp(subsystem, "drivers") == 0) { | ||
847 | char subsys[UTIL_NAME_SIZE]; | ||
848 | char *driver; | ||
849 | |||
850 | util_strscpy(subsys, sizeof(subsys), sysname); | ||
851 | driver = strchr(subsys, ':'); | ||
852 | if (driver != NULL) { | ||
853 | driver[0] = '\0'; | ||
854 | driver = &driver[1]; | ||
855 | |||
856 | util_strscpyl(path, l, "/subsystem/", subsys, "/drivers/", driver, NULL); | ||
857 | if (stat(path_full, &statbuf) == 0) | ||
858 | goto found; | ||
859 | |||
860 | util_strscpyl(path, l, "/bus/", subsys, "/drivers/", driver, NULL); | ||
861 | if (stat(path_full, &statbuf) == 0) | ||
862 | goto found; | ||
863 | } | ||
864 | goto out; | ||
865 | } | ||
866 | |||
867 | util_strscpyl(path, l, "/subsystem/", subsystem, "/devices/", sysname, NULL); | ||
868 | if (stat(path_full, &statbuf) == 0) | ||
869 | goto found; | ||
870 | |||
871 | util_strscpyl(path, l, "/bus/", subsystem, "/devices/", sysname, NULL); | ||
872 | if (stat(path_full, &statbuf) == 0) | ||
873 | goto found; | ||
874 | |||
875 | util_strscpyl(path, l, "/class/", subsystem, "/", sysname, NULL); | ||
876 | if (stat(path_full, &statbuf) == 0) | ||
877 | goto found; | ||
878 | out: | ||
879 | return NULL; | ||
880 | found: | ||
881 | return udev_device_new_from_syspath(udev, path_full); | ||
882 | } | ||
883 | |||
884 | /** | ||
885 | * udev_device_new_from_environment | ||
886 | * @udev: udev library context | ||
887 | * | ||
888 | * Create new udev device, and fill in information from the | ||
889 | * current process environment. This only works reliable if | ||
890 | * the process is called from a udev rule. It is usually used | ||
891 | * for tools executed from IMPORT= rules. | ||
892 | * | ||
893 | * The initial refcount is 1, and needs to be decremented to | ||
894 | * release the resources of the udev device. | ||
895 | * | ||
896 | * Returns: a new udev device, or #NULL, if it does not exist | ||
897 | **/ | ||
898 | UDEV_EXPORT struct udev_device *udev_device_new_from_environment(struct udev *udev) | ||
899 | { | ||
900 | int i; | ||
901 | struct udev_device *udev_device; | ||
902 | |||
903 | udev_device = udev_device_new(udev); | ||
904 | if (udev_device == NULL) | ||
905 | return NULL; | ||
906 | udev_device_set_info_loaded(udev_device); | ||
907 | |||
908 | for (i = 0; environ[i] != NULL; i++) | ||
909 | udev_device_add_property_from_string_parse(udev_device, environ[i]); | ||
910 | |||
911 | if (udev_device_add_property_from_string_parse_finish(udev_device) < 0) { | ||
912 | info(udev, "missing values, invalid device\n"); | ||
913 | udev_device_unref(udev_device); | ||
914 | udev_device = NULL; | ||
915 | } | ||
916 | |||
917 | return udev_device; | ||
918 | } | ||
919 | |||
920 | static struct udev_device *device_new_from_parent(struct udev_device *udev_device) | ||
921 | { | ||
922 | struct udev_device *udev_device_parent = NULL; | ||
923 | char path[UTIL_PATH_SIZE]; | ||
924 | const char *subdir; | ||
925 | |||
926 | util_strscpy(path, sizeof(path), udev_device->syspath); | ||
927 | subdir = &path[strlen(udev_get_sys_path(udev_device->udev))+1]; | ||
928 | for (;;) { | ||
929 | char *pos; | ||
930 | |||
931 | pos = strrchr(subdir, '/'); | ||
932 | if (pos == NULL || pos < &subdir[2]) | ||
933 | break; | ||
934 | pos[0] = '\0'; | ||
935 | udev_device_parent = udev_device_new_from_syspath(udev_device->udev, path); | ||
936 | if (udev_device_parent != NULL) | ||
937 | return udev_device_parent; | ||
938 | } | ||
939 | return NULL; | ||
940 | } | ||
941 | |||
942 | /** | ||
943 | * udev_device_get_parent: | ||
944 | * @udev_device: the device to start searching from | ||
945 | * | ||
946 | * Find the next parent device, and fill in information from the sys | ||
947 | * device and the udev database entry. | ||
948 | * | ||
949 | * The returned the device is not referenced. It is attached to the | ||
950 | * child device, and will be cleaned up when the child device | ||
951 | * is cleaned up. | ||
952 | * | ||
953 | * It is not necessarily just the upper level directory, empty or not | ||
954 | * recognized sys directories are ignored. | ||
955 | * | ||
956 | * It can be called as many times as needed, without caring about | ||
957 | * references. | ||
958 | * | ||
959 | * Returns: a new udev device, or #NULL, if it no parent exist. | ||
960 | **/ | ||
961 | UDEV_EXPORT struct udev_device *udev_device_get_parent(struct udev_device *udev_device) | ||
962 | { | ||
963 | if (udev_device == NULL) | ||
964 | return NULL; | ||
965 | if (!udev_device->parent_set) { | ||
966 | udev_device->parent_set = true; | ||
967 | udev_device->parent_device = device_new_from_parent(udev_device); | ||
968 | } | ||
969 | if (udev_device->parent_device != NULL) | ||
970 | dbg(udev_device->udev, "returning existing parent %p\n", udev_device->parent_device); | ||
971 | return udev_device->parent_device; | ||
972 | } | ||
973 | |||
974 | /** | ||
975 | * udev_device_get_parent_with_subsystem_devtype: | ||
976 | * @udev_device: udev device to start searching from | ||
977 | * @subsystem: the subsystem of the device | ||
978 | * @devtype: the type (DEVTYPE) of the device | ||
979 | * | ||
980 | * Find the next parent device, with a matching subsystem and devtype | ||
981 | * value, and fill in information from the sys device and the udev | ||
982 | * database entry. | ||
983 | * | ||
984 | * If devtype is #NULL, only subsystem is checked, and any devtype will | ||
985 | * match. | ||
986 | * | ||
987 | * The returned the device is not referenced. It is attached to the | ||
988 | * child device, and will be cleaned up when the child device | ||
989 | * is cleaned up. | ||
990 | * | ||
991 | * It can be called as many times as needed, without caring about | ||
992 | * references. | ||
993 | * | ||
994 | * Returns: a new udev device, or #NULL if no matching parent exists. | ||
995 | **/ | ||
996 | UDEV_EXPORT struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, const char *subsystem, const char *devtype) | ||
997 | { | ||
998 | struct udev_device *parent; | ||
999 | |||
1000 | if (subsystem == NULL) | ||
1001 | return NULL; | ||
1002 | |||
1003 | parent = udev_device_get_parent(udev_device); | ||
1004 | while (parent != NULL) { | ||
1005 | const char *parent_subsystem; | ||
1006 | const char *parent_devtype; | ||
1007 | |||
1008 | parent_subsystem = udev_device_get_subsystem(parent); | ||
1009 | if (parent_subsystem != NULL && strcmp(parent_subsystem, subsystem) == 0) { | ||
1010 | if (devtype == NULL) | ||
1011 | break; | ||
1012 | parent_devtype = udev_device_get_devtype(parent); | ||
1013 | if (parent_devtype != NULL && strcmp(parent_devtype, devtype) == 0) | ||
1014 | break; | ||
1015 | } | ||
1016 | parent = udev_device_get_parent(parent); | ||
1017 | } | ||
1018 | return parent; | ||
1019 | } | ||
1020 | |||
1021 | /** | ||
1022 | * udev_device_get_udev: | ||
1023 | * @udev_device: udev device | ||
1024 | * | ||
1025 | * Retrieve the udev library context the device was created with. | ||
1026 | * | ||
1027 | * Returns: the udev library context | ||
1028 | **/ | ||
1029 | UDEV_EXPORT struct udev *udev_device_get_udev(struct udev_device *udev_device) | ||
1030 | { | ||
1031 | if (udev_device == NULL) | ||
1032 | return NULL; | ||
1033 | return udev_device->udev; | ||
1034 | } | ||
1035 | |||
1036 | /** | ||
1037 | * udev_device_ref: | ||
1038 | * @udev_device: udev device | ||
1039 | * | ||
1040 | * Take a reference of a udev device. | ||
1041 | * | ||
1042 | * Returns: the passed udev device | ||
1043 | **/ | ||
1044 | UDEV_EXPORT struct udev_device *udev_device_ref(struct udev_device *udev_device) | ||
1045 | { | ||
1046 | if (udev_device == NULL) | ||
1047 | return NULL; | ||
1048 | udev_device->refcount++; | ||
1049 | return udev_device; | ||
1050 | } | ||
1051 | |||
1052 | /** | ||
1053 | * udev_device_unref: | ||
1054 | * @udev_device: udev device | ||
1055 | * | ||
1056 | * Drop a reference of a udev device. If the refcount reaches zero, | ||
1057 | * the resources of the device will be released. | ||
1058 | * | ||
1059 | **/ | ||
1060 | UDEV_EXPORT struct udev_device *udev_device_unref(struct udev_device *udev_device) | ||
1061 | { | ||
1062 | if (udev_device == NULL) | ||
1063 | return NULL; | ||
1064 | udev_device->refcount--; | ||
1065 | if (udev_device->refcount > 0) | ||
1066 | return udev_device; | ||
1067 | if (udev_device->parent_device != NULL) | ||
1068 | udev_device_unref(udev_device->parent_device); | ||
1069 | free(udev_device->syspath); | ||
1070 | free(udev_device->sysname); | ||
1071 | free(udev_device->devnode); | ||
1072 | free(udev_device->subsystem); | ||
1073 | free(udev_device->devtype); | ||
1074 | udev_list_cleanup(&udev_device->devlinks_list); | ||
1075 | udev_list_cleanup(&udev_device->properties_list); | ||
1076 | udev_list_cleanup(&udev_device->sysattr_value_list); | ||
1077 | udev_list_cleanup(&udev_device->sysattr_list); | ||
1078 | udev_list_cleanup(&udev_device->tags_list); | ||
1079 | free(udev_device->action); | ||
1080 | free(udev_device->driver); | ||
1081 | free(udev_device->devpath_old); | ||
1082 | free(udev_device->id_filename); | ||
1083 | free(udev_device->envp); | ||
1084 | free(udev_device->monitor_buf); | ||
1085 | dbg(udev_device->udev, "udev_device: %p released\n", udev_device); | ||
1086 | free(udev_device); | ||
1087 | return NULL; | ||
1088 | } | ||
1089 | |||
1090 | /** | ||
1091 | * udev_device_get_devpath: | ||
1092 | * @udev_device: udev device | ||
1093 | * | ||
1094 | * Retrieve the kernel devpath value of the udev device. The path | ||
1095 | * does not contain the sys mount point, and starts with a '/'. | ||
1096 | * | ||
1097 | * Returns: the devpath of the udev device | ||
1098 | **/ | ||
1099 | UDEV_EXPORT const char *udev_device_get_devpath(struct udev_device *udev_device) | ||
1100 | { | ||
1101 | if (udev_device == NULL) | ||
1102 | return NULL; | ||
1103 | return udev_device->devpath; | ||
1104 | } | ||
1105 | |||
1106 | /** | ||
1107 | * udev_device_get_syspath: | ||
1108 | * @udev_device: udev device | ||
1109 | * | ||
1110 | * Retrieve the sys path of the udev device. The path is an | ||
1111 | * absolute path and starts with the sys mount point. | ||
1112 | * | ||
1113 | * Returns: the sys path of the udev device | ||
1114 | **/ | ||
1115 | UDEV_EXPORT const char *udev_device_get_syspath(struct udev_device *udev_device) | ||
1116 | { | ||
1117 | if (udev_device == NULL) | ||
1118 | return NULL; | ||
1119 | return udev_device->syspath; | ||
1120 | } | ||
1121 | |||
1122 | /** | ||
1123 | * udev_device_get_sysname: | ||
1124 | * @udev_device: udev device | ||
1125 | * | ||
1126 | * Get the kernel device name in /sys. | ||
1127 | * | ||
1128 | * Returns: the name string of the device device | ||
1129 | **/ | ||
1130 | UDEV_EXPORT const char *udev_device_get_sysname(struct udev_device *udev_device) | ||
1131 | { | ||
1132 | if (udev_device == NULL) | ||
1133 | return NULL; | ||
1134 | return udev_device->sysname; | ||
1135 | } | ||
1136 | |||
1137 | /** | ||
1138 | * udev_device_get_sysnum: | ||
1139 | * @udev_device: udev device | ||
1140 | * | ||
1141 | * Get the instance number of the device. | ||
1142 | * | ||
1143 | * Returns: the trailing number string of of the device name | ||
1144 | **/ | ||
1145 | UDEV_EXPORT const char *udev_device_get_sysnum(struct udev_device *udev_device) | ||
1146 | { | ||
1147 | if (udev_device == NULL) | ||
1148 | return NULL; | ||
1149 | return udev_device->sysnum; | ||
1150 | } | ||
1151 | |||
1152 | /** | ||
1153 | * udev_device_get_devnode: | ||
1154 | * @udev_device: udev device | ||
1155 | * | ||
1156 | * Retrieve the device node file name belonging to the udev device. | ||
1157 | * The path is an absolute path, and starts with the device directory. | ||
1158 | * | ||
1159 | * Returns: the device node file name of the udev device, or #NULL if no device node exists | ||
1160 | **/ | ||
1161 | UDEV_EXPORT const char *udev_device_get_devnode(struct udev_device *udev_device) | ||
1162 | { | ||
1163 | if (udev_device == NULL) | ||
1164 | return NULL; | ||
1165 | if (udev_device->devnode != NULL) | ||
1166 | return udev_device->devnode; | ||
1167 | if (!udev_device->info_loaded) | ||
1168 | udev_device_read_uevent_file(udev_device); | ||
1169 | return udev_device->devnode; | ||
1170 | } | ||
1171 | |||
1172 | /** | ||
1173 | * udev_device_get_devlinks_list_entry: | ||
1174 | * @udev_device: udev device | ||
1175 | * | ||
1176 | * Retrieve the list of device links pointing to the device file of | ||
1177 | * the udev device. The next list entry can be retrieved with | ||
1178 | * udev_list_entry_next(), which returns #NULL if no more entries exist. | ||
1179 | * The devlink path can be retrieved from the list entry by | ||
1180 | * udev_list_entry_get_name(). The path is an absolute path, and starts with | ||
1181 | * the device directory. | ||
1182 | * | ||
1183 | * Returns: the first entry of the device node link list | ||
1184 | **/ | ||
1185 | UDEV_EXPORT struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device) | ||
1186 | { | ||
1187 | if (udev_device == NULL) | ||
1188 | return NULL; | ||
1189 | if (!udev_device->info_loaded) | ||
1190 | udev_device_read_db(udev_device, NULL); | ||
1191 | return udev_list_get_entry(&udev_device->devlinks_list); | ||
1192 | } | ||
1193 | |||
1194 | void udev_device_cleanup_devlinks_list(struct udev_device *udev_device) | ||
1195 | { | ||
1196 | udev_device->devlinks_uptodate = false; | ||
1197 | udev_list_cleanup(&udev_device->devlinks_list); | ||
1198 | } | ||
1199 | |||
1200 | /** | ||
1201 | * udev_device_get_properties_list_entry: | ||
1202 | * @udev_device: udev device | ||
1203 | * | ||
1204 | * Retrieve the list of key/value device properties of the udev | ||
1205 | * device. The next list entry can be retrieved with udev_list_entry_next(), | ||
1206 | * which returns #NULL if no more entries exist. The property name | ||
1207 | * can be retrieved from the list entry by udev_list_get_name(), | ||
1208 | * the property value by udev_list_get_value(). | ||
1209 | * | ||
1210 | * Returns: the first entry of the property list | ||
1211 | **/ | ||
1212 | UDEV_EXPORT struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device) | ||
1213 | { | ||
1214 | if (udev_device == NULL) | ||
1215 | return NULL; | ||
1216 | if (!udev_device->info_loaded) { | ||
1217 | udev_device_read_uevent_file(udev_device); | ||
1218 | udev_device_read_db(udev_device, NULL); | ||
1219 | } | ||
1220 | if (!udev_device->devlinks_uptodate) { | ||
1221 | char symlinks[UTIL_PATH_SIZE]; | ||
1222 | struct udev_list_entry *list_entry; | ||
1223 | |||
1224 | udev_device->devlinks_uptodate = true; | ||
1225 | list_entry = udev_device_get_devlinks_list_entry(udev_device); | ||
1226 | if (list_entry != NULL) { | ||
1227 | char *s; | ||
1228 | size_t l; | ||
1229 | |||
1230 | s = symlinks; | ||
1231 | l = util_strpcpyl(&s, sizeof(symlinks), udev_list_entry_get_name(list_entry), NULL); | ||
1232 | udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry)) | ||
1233 | l = util_strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry), NULL); | ||
1234 | udev_device_add_property(udev_device, "DEVLINKS", symlinks); | ||
1235 | } | ||
1236 | } | ||
1237 | if (!udev_device->tags_uptodate) { | ||
1238 | udev_device->tags_uptodate = true; | ||
1239 | if (udev_device_get_tags_list_entry(udev_device) != NULL) { | ||
1240 | char tags[UTIL_PATH_SIZE]; | ||
1241 | struct udev_list_entry *list_entry; | ||
1242 | char *s; | ||
1243 | size_t l; | ||
1244 | |||
1245 | s = tags; | ||
1246 | l = util_strpcpyl(&s, sizeof(tags), ":", NULL); | ||
1247 | udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device)) | ||
1248 | l = util_strpcpyl(&s, l, udev_list_entry_get_name(list_entry), ":", NULL); | ||
1249 | udev_device_add_property(udev_device, "TAGS", tags); | ||
1250 | } | ||
1251 | } | ||
1252 | return udev_list_get_entry(&udev_device->properties_list); | ||
1253 | } | ||
1254 | |||
1255 | /** | ||
1256 | * udev_device_get_action: | ||
1257 | * @udev_device: udev device | ||
1258 | * | ||
1259 | * This is only valid if the device was received through a monitor. Devices read from | ||
1260 | * sys do not have an action string. Usual actions are: add, remove, change, online, | ||
1261 | * offline. | ||
1262 | * | ||
1263 | * Returns: the kernel action value, or #NULL if there is no action value available. | ||
1264 | **/ | ||
1265 | UDEV_EXPORT const char *udev_device_get_action(struct udev_device *udev_device) | ||
1266 | { | ||
1267 | if (udev_device == NULL) | ||
1268 | return NULL; | ||
1269 | return udev_device->action; | ||
1270 | } | ||
1271 | |||
1272 | /** | ||
1273 | * udev_device_get_usec_since_initialized: | ||
1274 | * @udev_device: udev device | ||
1275 | * | ||
1276 | * Return the number of microseconds passed since udev set up the | ||
1277 | * device for the first time. | ||
1278 | * | ||
1279 | * This is only implemented for devices with need to store properties | ||
1280 | * in the udev database. All other devices return 0 here. | ||
1281 | * | ||
1282 | * Returns: the number of microseconds since the device was first seen. | ||
1283 | **/ | ||
1284 | UDEV_EXPORT unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device) | ||
1285 | { | ||
1286 | unsigned long long now; | ||
1287 | |||
1288 | if (udev_device == NULL) | ||
1289 | return 0; | ||
1290 | if (!udev_device->info_loaded) | ||
1291 | udev_device_read_db(udev_device, NULL); | ||
1292 | if (udev_device->usec_initialized == 0) | ||
1293 | return 0; | ||
1294 | now = now_usec(); | ||
1295 | if (now == 0) | ||
1296 | return 0; | ||
1297 | return now - udev_device->usec_initialized; | ||
1298 | } | ||
1299 | |||
1300 | unsigned long long udev_device_get_usec_initialized(struct udev_device *udev_device) | ||
1301 | { | ||
1302 | return udev_device->usec_initialized; | ||
1303 | } | ||
1304 | |||
1305 | void udev_device_set_usec_initialized(struct udev_device *udev_device, unsigned long long usec_initialized) | ||
1306 | { | ||
1307 | char num[32]; | ||
1308 | |||
1309 | udev_device->usec_initialized = usec_initialized; | ||
1310 | snprintf(num, sizeof(num), "%llu", usec_initialized); | ||
1311 | udev_device_add_property(udev_device, "USEC_INITIALIZED", num); | ||
1312 | } | ||
1313 | |||
1314 | /** | ||
1315 | * udev_device_get_sysattr_value: | ||
1316 | * @udev_device: udev device | ||
1317 | * @sysattr: attribute name | ||
1318 | * | ||
1319 | * The retrieved value is cached in the device. Repeated calls will return the same | ||
1320 | * value and not open the attribute again. | ||
1321 | * | ||
1322 | * Returns: the content of a sys attribute file, or #NULL if there is no sys attribute value. | ||
1323 | **/ | ||
1324 | UDEV_EXPORT const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr) | ||
1325 | { | ||
1326 | struct udev_list_entry *list_entry; | ||
1327 | char path[UTIL_PATH_SIZE]; | ||
1328 | char value[4096]; | ||
1329 | struct stat statbuf; | ||
1330 | int fd; | ||
1331 | ssize_t size; | ||
1332 | const char *val = NULL; | ||
1333 | |||
1334 | if (udev_device == NULL) | ||
1335 | return NULL; | ||
1336 | if (sysattr == NULL) | ||
1337 | return NULL; | ||
1338 | |||
1339 | /* look for possibly already cached result */ | ||
1340 | list_entry = udev_list_get_entry(&udev_device->sysattr_value_list); | ||
1341 | list_entry = udev_list_entry_get_by_name(list_entry, sysattr); | ||
1342 | if (list_entry != NULL) { | ||
1343 | dbg(udev_device->udev, "got '%s' (%s) from cache\n", | ||
1344 | sysattr, udev_list_entry_get_value(list_entry)); | ||
1345 | return udev_list_entry_get_value(list_entry); | ||
1346 | } | ||
1347 | |||
1348 | util_strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/", sysattr, NULL); | ||
1349 | if (lstat(path, &statbuf) != 0) { | ||
1350 | dbg(udev_device->udev, "no attribute '%s', keep negative entry\n", path); | ||
1351 | udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, NULL); | ||
1352 | goto out; | ||
1353 | } | ||
1354 | |||
1355 | if (S_ISLNK(statbuf.st_mode)) { | ||
1356 | struct udev_device *dev; | ||
1357 | |||
1358 | /* | ||
1359 | * Some core links return only the last element of the target path, | ||
1360 | * these are just values, the paths should not be exposed. | ||
1361 | */ | ||
1362 | if (strcmp(sysattr, "driver") == 0 || | ||
1363 | strcmp(sysattr, "subsystem") == 0 || | ||
1364 | strcmp(sysattr, "module") == 0) { | ||
1365 | if (util_get_sys_core_link_value(udev_device->udev, sysattr, | ||
1366 | udev_device->syspath, value, sizeof(value)) < 0) | ||
1367 | return NULL; | ||
1368 | dbg(udev_device->udev, "cache '%s' with link value '%s'\n", sysattr, value); | ||
1369 | list_entry = udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, value); | ||
1370 | val = udev_list_entry_get_value(list_entry); | ||
1371 | goto out; | ||
1372 | } | ||
1373 | |||
1374 | /* resolve link to a device and return its syspath */ | ||
1375 | util_strscpyl(path, sizeof(path), udev_device->syspath, "/", sysattr, NULL); | ||
1376 | dev = udev_device_new_from_syspath(udev_device->udev, path); | ||
1377 | if (dev != NULL) { | ||
1378 | list_entry = udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, | ||
1379 | udev_device_get_syspath(dev)); | ||
1380 | val = udev_list_entry_get_value(list_entry); | ||
1381 | udev_device_unref(dev); | ||
1382 | } | ||
1383 | |||
1384 | goto out; | ||
1385 | } | ||
1386 | |||
1387 | /* skip directories */ | ||
1388 | if (S_ISDIR(statbuf.st_mode)) | ||
1389 | goto out; | ||
1390 | |||
1391 | /* skip non-readable files */ | ||
1392 | if ((statbuf.st_mode & S_IRUSR) == 0) | ||
1393 | goto out; | ||
1394 | |||
1395 | /* read attribute value */ | ||
1396 | fd = open(path, O_RDONLY|O_CLOEXEC); | ||
1397 | if (fd < 0) { | ||
1398 | dbg(udev_device->udev, "attribute '%s' can not be opened\n", path); | ||
1399 | goto out; | ||
1400 | } | ||
1401 | size = read(fd, value, sizeof(value)); | ||
1402 | close(fd); | ||
1403 | if (size < 0) | ||
1404 | goto out; | ||
1405 | if (size == sizeof(value)) | ||
1406 | goto out; | ||
1407 | |||
1408 | /* got a valid value, store it in cache and return it */ | ||
1409 | value[size] = '\0'; | ||
1410 | util_remove_trailing_chars(value, '\n'); | ||
1411 | dbg(udev_device->udev, "'%s' has attribute value '%s'\n", path, value); | ||
1412 | list_entry = udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, value); | ||
1413 | val = udev_list_entry_get_value(list_entry); | ||
1414 | out: | ||
1415 | return val; | ||
1416 | } | ||
1417 | |||
1418 | static int udev_device_sysattr_list_read(struct udev_device *udev_device) | ||
1419 | { | ||
1420 | struct dirent *dent; | ||
1421 | DIR *dir; | ||
1422 | int num = 0; | ||
1423 | |||
1424 | if (udev_device == NULL) | ||
1425 | return -1; | ||
1426 | if (udev_device->sysattr_list_read) | ||
1427 | return 0; | ||
1428 | |||
1429 | dir = opendir(udev_device_get_syspath(udev_device)); | ||
1430 | if (!dir) { | ||
1431 | dbg(udev_device->udev, "sysfs dir '%s' can not be opened\n", | ||
1432 | udev_device_get_syspath(udev_device)); | ||
1433 | return -1; | ||
1434 | } | ||
1435 | |||
1436 | for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { | ||
1437 | char path[UTIL_PATH_SIZE]; | ||
1438 | struct stat statbuf; | ||
1439 | |||
1440 | /* only handle symlinks and regular files */ | ||
1441 | if (dent->d_type != DT_LNK && dent->d_type != DT_REG) | ||
1442 | continue; | ||
1443 | |||
1444 | util_strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/", dent->d_name, NULL); | ||
1445 | if (lstat(path, &statbuf) != 0) | ||
1446 | continue; | ||
1447 | if ((statbuf.st_mode & S_IRUSR) == 0) | ||
1448 | continue; | ||
1449 | |||
1450 | udev_list_entry_add(&udev_device->sysattr_list, dent->d_name, NULL); | ||
1451 | num++; | ||
1452 | } | ||
1453 | |||
1454 | closedir(dir); | ||
1455 | dbg(udev_device->udev, "found %d sysattrs for '%s'\n", num, udev_device_get_syspath(udev_device)); | ||
1456 | udev_device->sysattr_list_read = true; | ||
1457 | |||
1458 | return num; | ||
1459 | } | ||
1460 | |||
1461 | /** | ||
1462 | * udev_device_get_sysattr_list_entry: | ||
1463 | * @udev_device: udev device | ||
1464 | * | ||
1465 | * Retrieve the list of available sysattrs, with value being empty; | ||
1466 | * This just return all available sysfs attributes for a particular | ||
1467 | * device without reading their values. | ||
1468 | * | ||
1469 | * Returns: the first entry of the property list | ||
1470 | **/ | ||
1471 | UDEV_EXPORT struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device) | ||
1472 | { | ||
1473 | if (!udev_device->sysattr_list_read) { | ||
1474 | int ret; | ||
1475 | ret = udev_device_sysattr_list_read(udev_device); | ||
1476 | if (0 > ret) | ||
1477 | return NULL; | ||
1478 | } | ||
1479 | |||
1480 | return udev_list_get_entry(&udev_device->sysattr_list); | ||
1481 | } | ||
1482 | |||
1483 | int udev_device_set_syspath(struct udev_device *udev_device, const char *syspath) | ||
1484 | { | ||
1485 | const char *pos; | ||
1486 | size_t len; | ||
1487 | |||
1488 | free(udev_device->syspath); | ||
1489 | udev_device->syspath = strdup(syspath); | ||
1490 | if (udev_device->syspath == NULL) | ||
1491 | return -ENOMEM; | ||
1492 | udev_device->devpath = &udev_device->syspath[strlen(udev_get_sys_path(udev_device->udev))]; | ||
1493 | udev_device_add_property(udev_device, "DEVPATH", udev_device->devpath); | ||
1494 | |||
1495 | pos = strrchr(udev_device->syspath, '/'); | ||
1496 | if (pos == NULL) | ||
1497 | return -EINVAL; | ||
1498 | udev_device->sysname = strdup(&pos[1]); | ||
1499 | if (udev_device->sysname == NULL) | ||
1500 | return -ENOMEM; | ||
1501 | |||
1502 | /* some devices have '!' in their name, change that to '/' */ | ||
1503 | len = 0; | ||
1504 | while (udev_device->sysname[len] != '\0') { | ||
1505 | if (udev_device->sysname[len] == '!') | ||
1506 | udev_device->sysname[len] = '/'; | ||
1507 | len++; | ||
1508 | } | ||
1509 | |||
1510 | /* trailing number */ | ||
1511 | while (len > 0 && isdigit(udev_device->sysname[--len])) | ||
1512 | udev_device->sysnum = &udev_device->sysname[len]; | ||
1513 | |||
1514 | /* sysname is completely numeric */ | ||
1515 | if (len == 0) | ||
1516 | udev_device->sysnum = NULL; | ||
1517 | |||
1518 | return 0; | ||
1519 | } | ||
1520 | |||
1521 | int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode) | ||
1522 | { | ||
1523 | free(udev_device->devnode); | ||
1524 | if (devnode[0] != '/') { | ||
1525 | if (asprintf(&udev_device->devnode, "%s/%s", udev_get_dev_path(udev_device->udev), devnode) < 0) | ||
1526 | udev_device->devnode = NULL; | ||
1527 | } else { | ||
1528 | udev_device->devnode = strdup(devnode); | ||
1529 | } | ||
1530 | if (udev_device->devnode == NULL) | ||
1531 | return -ENOMEM; | ||
1532 | udev_device_add_property(udev_device, "DEVNAME", udev_device->devnode); | ||
1533 | return 0; | ||
1534 | } | ||
1535 | |||
1536 | int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink, int unique) | ||
1537 | { | ||
1538 | struct udev_list_entry *list_entry; | ||
1539 | |||
1540 | udev_device->devlinks_uptodate = false; | ||
1541 | list_entry = udev_list_entry_add(&udev_device->devlinks_list, devlink, NULL); | ||
1542 | if (list_entry == NULL) | ||
1543 | return -ENOMEM; | ||
1544 | if (unique) | ||
1545 | udev_list_entry_set_num(list_entry, true); | ||
1546 | return 0; | ||
1547 | } | ||
1548 | |||
1549 | const char *udev_device_get_id_filename(struct udev_device *udev_device) | ||
1550 | { | ||
1551 | if (udev_device->id_filename == NULL) { | ||
1552 | if (udev_device_get_subsystem(udev_device) == NULL) | ||
1553 | return NULL; | ||
1554 | |||
1555 | if (major(udev_device_get_devnum(udev_device)) > 0) { | ||
1556 | /* use dev_t -- b259:131072, c254:0 */ | ||
1557 | if (asprintf(&udev_device->id_filename, "%c%u:%u", | ||
1558 | strcmp(udev_device_get_subsystem(udev_device), "block") == 0 ? 'b' : 'c', | ||
1559 | major(udev_device_get_devnum(udev_device)), | ||
1560 | minor(udev_device_get_devnum(udev_device))) < 0) | ||
1561 | udev_device->id_filename = NULL; | ||
1562 | } else if (udev_device_get_ifindex(udev_device) > 0) { | ||
1563 | /* use netdev ifindex -- n3 */ | ||
1564 | if (asprintf(&udev_device->id_filename, "n%u", udev_device_get_ifindex(udev_device)) < 0) | ||
1565 | udev_device->id_filename = NULL; | ||
1566 | } else { | ||
1567 | /* | ||
1568 | * use $subsys:$syname -- pci:0000:00:1f.2 | ||
1569 | * sysname() has '!' translated, get it from devpath | ||
1570 | */ | ||
1571 | const char *sysname; | ||
1572 | sysname = strrchr(udev_device->devpath, '/'); | ||
1573 | if (sysname == NULL) | ||
1574 | return NULL; | ||
1575 | sysname = &sysname[1]; | ||
1576 | if (asprintf(&udev_device->id_filename, "+%s:%s", udev_device_get_subsystem(udev_device), sysname) < 0) | ||
1577 | udev_device->id_filename = NULL; | ||
1578 | } | ||
1579 | } | ||
1580 | return udev_device->id_filename; | ||
1581 | } | ||
1582 | |||
1583 | /** | ||
1584 | * udev_device_get_is_initialized: | ||
1585 | * @udev_device: udev device | ||
1586 | * | ||
1587 | * Check if udev has already handled the device and has set up | ||
1588 | * device node permissions and context, or has renamed a network | ||
1589 | * device. | ||
1590 | * | ||
1591 | * This is only implemented for devices with a device node | ||
1592 | * or network interfaces. All other devices return 1 here. | ||
1593 | * | ||
1594 | * Returns: 1 if the device is set up. 0 otherwise. | ||
1595 | **/ | ||
1596 | UDEV_EXPORT int udev_device_get_is_initialized(struct udev_device *udev_device) | ||
1597 | { | ||
1598 | if (!udev_device->info_loaded) | ||
1599 | udev_device_read_db(udev_device, NULL); | ||
1600 | return udev_device->is_initialized; | ||
1601 | } | ||
1602 | |||
1603 | void udev_device_set_is_initialized(struct udev_device *udev_device) | ||
1604 | { | ||
1605 | udev_device->is_initialized = true; | ||
1606 | } | ||
1607 | |||
1608 | int udev_device_add_tag(struct udev_device *udev_device, const char *tag) | ||
1609 | { | ||
1610 | if (strchr(tag, ':') != NULL || strchr(tag, ' ') != NULL) | ||
1611 | return -EINVAL; | ||
1612 | udev_device->tags_uptodate = false; | ||
1613 | if (udev_list_entry_add(&udev_device->tags_list, tag, NULL) != NULL) | ||
1614 | return 0; | ||
1615 | return -ENOMEM; | ||
1616 | } | ||
1617 | |||
1618 | void udev_device_cleanup_tags_list(struct udev_device *udev_device) | ||
1619 | { | ||
1620 | udev_device->tags_uptodate = false; | ||
1621 | udev_list_cleanup(&udev_device->tags_list); | ||
1622 | } | ||
1623 | |||
1624 | /** | ||
1625 | * udev_device_get_tags_list_entry: | ||
1626 | * @udev_device: udev device | ||
1627 | * | ||
1628 | * Retrieve the list of tags attached to the udev device. The next | ||
1629 | * list entry can be retrieved with udev_list_entry_next(), | ||
1630 | * which returns #NULL if no more entries exist. The tag string | ||
1631 | * can be retrieved from the list entry by udev_list_get_name(). | ||
1632 | * | ||
1633 | * Returns: the first entry of the tag list | ||
1634 | **/ | ||
1635 | UDEV_EXPORT struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device) | ||
1636 | { | ||
1637 | if (udev_device == NULL) | ||
1638 | return NULL; | ||
1639 | if (!udev_device->info_loaded) | ||
1640 | udev_device_read_db(udev_device, NULL); | ||
1641 | return udev_list_get_entry(&udev_device->tags_list); | ||
1642 | } | ||
1643 | |||
1644 | /** | ||
1645 | * udev_device_has_tag: | ||
1646 | * @udev_device: udev device | ||
1647 | * @tag: tag name | ||
1648 | * | ||
1649 | * Check if a given device has a certain tag associated. | ||
1650 | * | ||
1651 | * Returns: 1 if the tag is found. 0 otherwise. | ||
1652 | **/ | ||
1653 | UDEV_EXPORT int udev_device_has_tag(struct udev_device *udev_device, const char *tag) | ||
1654 | { | ||
1655 | struct udev_list_entry *list_entry; | ||
1656 | |||
1657 | if (udev_device == NULL) | ||
1658 | return false; | ||
1659 | if (!udev_device->info_loaded) | ||
1660 | udev_device_read_db(udev_device, NULL); | ||
1661 | list_entry = udev_device_get_tags_list_entry(udev_device); | ||
1662 | if (udev_list_entry_get_by_name(list_entry, tag) != NULL) | ||
1663 | return true; | ||
1664 | return false; | ||
1665 | } | ||
1666 | |||
1667 | #define ENVP_SIZE 128 | ||
1668 | #define MONITOR_BUF_SIZE 4096 | ||
1669 | static int update_envp_monitor_buf(struct udev_device *udev_device) | ||
1670 | { | ||
1671 | struct udev_list_entry *list_entry; | ||
1672 | char *s; | ||
1673 | size_t l; | ||
1674 | unsigned int i; | ||
1675 | |||
1676 | /* monitor buffer of property strings */ | ||
1677 | free(udev_device->monitor_buf); | ||
1678 | udev_device->monitor_buf_len = 0; | ||
1679 | udev_device->monitor_buf = malloc(MONITOR_BUF_SIZE); | ||
1680 | if (udev_device->monitor_buf == NULL) | ||
1681 | return -ENOMEM; | ||
1682 | |||
1683 | /* envp array, strings will point into monitor buffer */ | ||
1684 | if (udev_device->envp == NULL) | ||
1685 | udev_device->envp = malloc(sizeof(char *) * ENVP_SIZE); | ||
1686 | if (udev_device->envp == NULL) | ||
1687 | return -ENOMEM; | ||
1688 | |||
1689 | i = 0; | ||
1690 | s = udev_device->monitor_buf; | ||
1691 | l = MONITOR_BUF_SIZE; | ||
1692 | udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device)) { | ||
1693 | const char *key; | ||
1694 | |||
1695 | key = udev_list_entry_get_name(list_entry); | ||
1696 | /* skip private variables */ | ||
1697 | if (key[0] == '.') | ||
1698 | continue; | ||
1699 | |||
1700 | /* add string to envp array */ | ||
1701 | udev_device->envp[i++] = s; | ||
1702 | if (i+1 >= ENVP_SIZE) | ||
1703 | return -EINVAL; | ||
1704 | |||
1705 | /* add property string to monitor buffer */ | ||
1706 | l = util_strpcpyl(&s, l, key, "=", udev_list_entry_get_value(list_entry), NULL); | ||
1707 | if (l == 0) | ||
1708 | return -EINVAL; | ||
1709 | /* advance past the trailing '\0' that util_strpcpyl() guarantees */ | ||
1710 | s++; | ||
1711 | l--; | ||
1712 | } | ||
1713 | udev_device->envp[i] = NULL; | ||
1714 | udev_device->monitor_buf_len = s - udev_device->monitor_buf; | ||
1715 | udev_device->envp_uptodate = true; | ||
1716 | dbg(udev_device->udev, "filled envp/monitor buffer, %u properties, %zu bytes\n", | ||
1717 | i, udev_device->monitor_buf_len); | ||
1718 | return 0; | ||
1719 | } | ||
1720 | |||
1721 | char **udev_device_get_properties_envp(struct udev_device *udev_device) | ||
1722 | { | ||
1723 | if (!udev_device->envp_uptodate) | ||
1724 | if (update_envp_monitor_buf(udev_device) != 0) | ||
1725 | return NULL; | ||
1726 | return udev_device->envp; | ||
1727 | } | ||
1728 | |||
1729 | ssize_t udev_device_get_properties_monitor_buf(struct udev_device *udev_device, const char **buf) | ||
1730 | { | ||
1731 | if (!udev_device->envp_uptodate) | ||
1732 | if (update_envp_monitor_buf(udev_device) != 0) | ||
1733 | return -EINVAL; | ||
1734 | *buf = udev_device->monitor_buf; | ||
1735 | return udev_device->monitor_buf_len; | ||
1736 | } | ||
1737 | |||
1738 | int udev_device_set_action(struct udev_device *udev_device, const char *action) | ||
1739 | { | ||
1740 | free(udev_device->action); | ||
1741 | udev_device->action = strdup(action); | ||
1742 | if (udev_device->action == NULL) | ||
1743 | return -ENOMEM; | ||
1744 | udev_device_add_property(udev_device, "ACTION", udev_device->action); | ||
1745 | return 0; | ||
1746 | } | ||
1747 | |||
1748 | int udev_device_get_devlink_priority(struct udev_device *udev_device) | ||
1749 | { | ||
1750 | if (!udev_device->info_loaded) | ||
1751 | udev_device_read_db(udev_device, NULL); | ||
1752 | return udev_device->devlink_priority; | ||
1753 | } | ||
1754 | |||
1755 | int udev_device_set_devlink_priority(struct udev_device *udev_device, int prio) | ||
1756 | { | ||
1757 | udev_device->devlink_priority = prio; | ||
1758 | return 0; | ||
1759 | } | ||
1760 | |||
1761 | int udev_device_get_watch_handle(struct udev_device *udev_device) | ||
1762 | { | ||
1763 | if (!udev_device->info_loaded) | ||
1764 | udev_device_read_db(udev_device, NULL); | ||
1765 | return udev_device->watch_handle; | ||
1766 | } | ||
1767 | |||
1768 | int udev_device_set_watch_handle(struct udev_device *udev_device, int handle) | ||
1769 | { | ||
1770 | udev_device->watch_handle = handle; | ||
1771 | return 0; | ||
1772 | } | ||
1773 | |||
1774 | bool udev_device_get_db_persist(struct udev_device *udev_device) | ||
1775 | { | ||
1776 | return udev_device->db_persist; | ||
1777 | } | ||
1778 | |||
1779 | void udev_device_set_db_persist(struct udev_device *udev_device) | ||
1780 | { | ||
1781 | udev_device->db_persist = true; | ||
1782 | } |
File src/libudev-enumerate.c added (mode: 100644) (index 0000000..9a3bf52) | |||
1 | /* | ||
2 | * libudev - interface to udev device information | ||
3 | * | ||
4 | * Copyright (C) 2008-2010 Kay Sievers <kay.sievers@vrfy.org> | ||
5 | * | ||
6 | * This library is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU Lesser General Public | ||
8 | * License as published by the Free Software Foundation; either | ||
9 | * version 2.1 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <stdio.h> | ||
13 | #include <stdlib.h> | ||
14 | #include <stddef.h> | ||
15 | #include <unistd.h> | ||
16 | #include <errno.h> | ||
17 | #include <string.h> | ||
18 | #include <dirent.h> | ||
19 | #include <fnmatch.h> | ||
20 | #include <stdbool.h> | ||
21 | #include <sys/stat.h> | ||
22 | #include <sys/param.h> | ||
23 | |||
24 | #include "libudev.h" | ||
25 | #include "libudev-private.h" | ||
26 | |||
27 | /** | ||
28 | * SECTION:libudev-enumerate | ||
29 | * @short_description: lookup and sort sys devices | ||
30 | * | ||
31 | * Lookup devices in the sys filesystem, filter devices by properties, | ||
32 | * and return a sorted list of devices. | ||
33 | */ | ||
34 | |||
35 | struct syspath { | ||
36 | char *syspath; | ||
37 | size_t len; | ||
38 | }; | ||
39 | |||
40 | /** | ||
41 | * udev_enumerate: | ||
42 | * | ||
43 | * Opaque object representing one device lookup/sort context. | ||
44 | */ | ||
45 | struct udev_enumerate { | ||
46 | struct udev *udev; | ||
47 | int refcount; | ||
48 | struct udev_list sysattr_match_list; | ||
49 | struct udev_list sysattr_nomatch_list; | ||
50 | struct udev_list subsystem_match_list; | ||
51 | struct udev_list subsystem_nomatch_list; | ||
52 | struct udev_list sysname_match_list; | ||
53 | struct udev_list properties_match_list; | ||
54 | struct udev_list tags_match_list; | ||
55 | struct udev_device *parent_match; | ||
56 | struct udev_list devices_list; | ||
57 | struct syspath *devices; | ||
58 | unsigned int devices_cur; | ||
59 | unsigned int devices_max; | ||
60 | bool devices_uptodate:1; | ||
61 | bool match_is_initialized; | ||
62 | }; | ||
63 | |||
64 | /** | ||
65 | * udev_enumerate_new: | ||
66 | * @udev: udev library context | ||
67 | * | ||
68 | * Create an enumeration context to scan /sys. | ||
69 | * | ||
70 | * Returns: an enumeration context. | ||
71 | **/ | ||
72 | UDEV_EXPORT struct udev_enumerate *udev_enumerate_new(struct udev *udev) | ||
73 | { | ||
74 | struct udev_enumerate *udev_enumerate; | ||
75 | |||
76 | udev_enumerate = calloc(1, sizeof(struct udev_enumerate)); | ||
77 | if (udev_enumerate == NULL) | ||
78 | return NULL; | ||
79 | udev_enumerate->refcount = 1; | ||
80 | udev_enumerate->udev = udev; | ||
81 | udev_list_init(udev, &udev_enumerate->sysattr_match_list, false); | ||
82 | udev_list_init(udev, &udev_enumerate->sysattr_nomatch_list, false); | ||
83 | udev_list_init(udev, &udev_enumerate->subsystem_match_list, true); | ||
84 | udev_list_init(udev, &udev_enumerate->subsystem_nomatch_list, true); | ||
85 | udev_list_init(udev, &udev_enumerate->sysname_match_list, true); | ||
86 | udev_list_init(udev, &udev_enumerate->properties_match_list, false); | ||
87 | udev_list_init(udev, &udev_enumerate->tags_match_list, true); | ||
88 | udev_list_init(udev, &udev_enumerate->devices_list, false); | ||
89 | return udev_enumerate; | ||
90 | } | ||
91 | |||
92 | /** | ||
93 | * udev_enumerate_ref: | ||
94 | * @udev_enumerate: context | ||
95 | * | ||
96 | * Take a reference of a enumeration context. | ||
97 | * | ||
98 | * Returns: the passed enumeration context | ||
99 | **/ | ||
100 | UDEV_EXPORT struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate) | ||
101 | { | ||
102 | if (udev_enumerate == NULL) | ||
103 | return NULL; | ||
104 | udev_enumerate->refcount++; | ||
105 | return udev_enumerate; | ||
106 | } | ||
107 | |||
108 | /** | ||
109 | * udev_enumerate_unref: | ||
110 | * @udev_enumerate: context | ||
111 | * | ||
112 | * Drop a reference of an enumeration context. If the refcount reaches zero, | ||
113 | * all resources of the enumeration context will be released. | ||
114 | **/ | ||
115 | UDEV_EXPORT struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate) | ||
116 | { | ||
117 | unsigned int i; | ||
118 | |||
119 | if (udev_enumerate == NULL) | ||
120 | return NULL; | ||
121 | udev_enumerate->refcount--; | ||
122 | if (udev_enumerate->refcount > 0) | ||
123 | return udev_enumerate; | ||
124 | udev_list_cleanup(&udev_enumerate->sysattr_match_list); | ||
125 | udev_list_cleanup(&udev_enumerate->sysattr_nomatch_list); | ||
126 | udev_list_cleanup(&udev_enumerate->subsystem_match_list); | ||
127 | udev_list_cleanup(&udev_enumerate->subsystem_nomatch_list); | ||
128 | udev_list_cleanup(&udev_enumerate->sysname_match_list); | ||
129 | udev_list_cleanup(&udev_enumerate->properties_match_list); | ||
130 | udev_list_cleanup(&udev_enumerate->tags_match_list); | ||
131 | udev_device_unref(udev_enumerate->parent_match); | ||
132 | udev_list_cleanup(&udev_enumerate->devices_list); | ||
133 | for (i = 0; i < udev_enumerate->devices_cur; i++) | ||
134 | free(udev_enumerate->devices[i].syspath); | ||
135 | free(udev_enumerate->devices); | ||
136 | free(udev_enumerate); | ||
137 | return NULL; | ||
138 | } | ||
139 | |||
140 | /** | ||
141 | * udev_enumerate_get_udev: | ||
142 | * @udev_enumerate: context | ||
143 | * | ||
144 | * Get the udev library context. | ||
145 | * | ||
146 | * Returns: a pointer to the context. | ||
147 | */ | ||
148 | UDEV_EXPORT struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate) | ||
149 | { | ||
150 | if (udev_enumerate == NULL) | ||
151 | return NULL; | ||
152 | return udev_enumerate->udev; | ||
153 | } | ||
154 | |||
155 | static int syspath_add(struct udev_enumerate *udev_enumerate, const char *syspath) | ||
156 | { | ||
157 | char *path; | ||
158 | struct syspath *entry; | ||
159 | |||
160 | /* double array size if needed */ | ||
161 | if (udev_enumerate->devices_cur >= udev_enumerate->devices_max) { | ||
162 | struct syspath *buf; | ||
163 | unsigned int add; | ||
164 | |||
165 | add = udev_enumerate->devices_max; | ||
166 | if (add < 1024) | ||
167 | add = 1024; | ||
168 | buf = realloc(udev_enumerate->devices, (udev_enumerate->devices_max + add) * sizeof(struct syspath)); | ||
169 | if (buf == NULL) | ||
170 | return -ENOMEM; | ||
171 | udev_enumerate->devices = buf; | ||
172 | udev_enumerate->devices_max += add; | ||
173 | } | ||
174 | |||
175 | path = strdup(syspath); | ||
176 | if (path == NULL) | ||
177 | return -ENOMEM; | ||
178 | entry = &udev_enumerate->devices[udev_enumerate->devices_cur]; | ||
179 | entry->syspath = path; | ||
180 | entry->len = strlen(path); | ||
181 | udev_enumerate->devices_cur++; | ||
182 | udev_enumerate->devices_uptodate = false; | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static int syspath_cmp(const void *p1, const void *p2) | ||
187 | { | ||
188 | const struct syspath *path1 = p1; | ||
189 | const struct syspath *path2 = p2; | ||
190 | size_t len; | ||
191 | int ret; | ||
192 | |||
193 | len = MIN(path1->len, path2->len); | ||
194 | ret = memcmp(path1->syspath, path2->syspath, len); | ||
195 | if (ret == 0) { | ||
196 | if (path1->len < path2->len) | ||
197 | ret = -1; | ||
198 | else if (path1->len > path2->len) | ||
199 | ret = 1; | ||
200 | } | ||
201 | return ret; | ||
202 | } | ||
203 | |||
204 | /* For devices that should be moved to the absolute end of the list */ | ||
205 | static bool devices_delay_end(struct udev *udev, const char *syspath) | ||
206 | { | ||
207 | static const char *delay_device_list[] = { | ||
208 | "/block/md", | ||
209 | "/block/dm-", | ||
210 | NULL | ||
211 | }; | ||
212 | size_t len; | ||
213 | int i; | ||
214 | |||
215 | len = strlen(udev_get_sys_path(udev)); | ||
216 | for (i = 0; delay_device_list[i] != NULL; i++) { | ||
217 | if (strstr(&syspath[len], delay_device_list[i]) != NULL) { | ||
218 | dbg(udev, "delaying: %s\n", syspath); | ||
219 | return true; | ||
220 | } | ||
221 | } | ||
222 | return false; | ||
223 | } | ||
224 | |||
225 | /* For devices that should just be moved a little bit later, just | ||
226 | * before the point where some common path prefix changes. Returns the | ||
227 | * number of characters that make up that common prefix */ | ||
228 | static size_t devices_delay_later(struct udev *udev, const char *syspath) | ||
229 | { | ||
230 | const char *c; | ||
231 | |||
232 | /* For sound cards the control device must be enumerated last | ||
233 | * to make sure it's the final device node that gets ACLs | ||
234 | * applied. Applications rely on this fact and use ACL changes | ||
235 | * on the control node as an indicator that the ACL change of | ||
236 | * the entire sound card completed. The kernel makes this | ||
237 | * guarantee when creating those devices, and hence we should | ||
238 | * too when enumerating them. */ | ||
239 | |||
240 | if ((c = strstr(syspath, "/sound/card"))) { | ||
241 | c += 11; | ||
242 | c += strcspn(c, "/"); | ||
243 | |||
244 | if (strncmp(c, "/controlC", 9) == 0) | ||
245 | return c - syspath + 1; | ||
246 | } | ||
247 | |||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | /** | ||
252 | * udev_enumerate_get_list_entry: | ||
253 | * @udev_enumerate: context | ||
254 | * | ||
255 | * Get the first entry of the sorted list of device paths. | ||
256 | * | ||
257 | * Returns: a udev_list_entry. | ||
258 | */ | ||
259 | UDEV_EXPORT struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate) | ||
260 | { | ||
261 | if (udev_enumerate == NULL) | ||
262 | return NULL; | ||
263 | if (!udev_enumerate->devices_uptodate) { | ||
264 | unsigned int i; | ||
265 | unsigned int max; | ||
266 | struct syspath *prev = NULL, *move_later = NULL; | ||
267 | size_t move_later_prefix = 0; | ||
268 | |||
269 | udev_list_cleanup(&udev_enumerate->devices_list); | ||
270 | qsort(udev_enumerate->devices, udev_enumerate->devices_cur, sizeof(struct syspath), syspath_cmp); | ||
271 | |||
272 | max = udev_enumerate->devices_cur; | ||
273 | for (i = 0; i < max; i++) { | ||
274 | struct syspath *entry = &udev_enumerate->devices[i]; | ||
275 | |||
276 | /* skip duplicated entries */ | ||
277 | if (prev != NULL && | ||
278 | entry->len == prev->len && | ||
279 | memcmp(entry->syspath, prev->syspath, entry->len) == 0) | ||
280 | continue; | ||
281 | prev = entry; | ||
282 | |||
283 | /* skip to be delayed devices, and add them to the end of the list */ | ||
284 | if (devices_delay_end(udev_enumerate->udev, entry->syspath)) { | ||
285 | syspath_add(udev_enumerate, entry->syspath); | ||
286 | /* need to update prev here for the case realloc() gives a different address */ | ||
287 | prev = &udev_enumerate->devices[i]; | ||
288 | continue; | ||
289 | } | ||
290 | |||
291 | /* skip to be delayed devices, and move the to | ||
292 | * the point where the prefix changes. We can | ||
293 | * only move one item at a time. */ | ||
294 | if (!move_later) { | ||
295 | move_later_prefix = devices_delay_later(udev_enumerate->udev, entry->syspath); | ||
296 | |||
297 | if (move_later_prefix > 0) { | ||
298 | move_later = entry; | ||
299 | continue; | ||
300 | } | ||
301 | } | ||
302 | |||
303 | if (move_later && | ||
304 | strncmp(entry->syspath, move_later->syspath, move_later_prefix) != 0) { | ||
305 | |||
306 | udev_list_entry_add(&udev_enumerate->devices_list, move_later->syspath, NULL); | ||
307 | move_later = NULL; | ||
308 | } | ||
309 | |||
310 | udev_list_entry_add(&udev_enumerate->devices_list, entry->syspath, NULL); | ||
311 | } | ||
312 | |||
313 | if (move_later) | ||
314 | udev_list_entry_add(&udev_enumerate->devices_list, move_later->syspath, NULL); | ||
315 | |||
316 | /* add and cleanup delayed devices from end of list */ | ||
317 | for (i = max; i < udev_enumerate->devices_cur; i++) { | ||
318 | struct syspath *entry = &udev_enumerate->devices[i]; | ||
319 | |||
320 | udev_list_entry_add(&udev_enumerate->devices_list, entry->syspath, NULL); | ||
321 | free(entry->syspath); | ||
322 | } | ||
323 | udev_enumerate->devices_cur = max; | ||
324 | |||
325 | udev_enumerate->devices_uptodate = true; | ||
326 | } | ||
327 | return udev_list_get_entry(&udev_enumerate->devices_list); | ||
328 | } | ||
329 | |||
330 | /** | ||
331 | * udev_enumerate_add_match_subsystem: | ||
332 | * @udev_enumerate: context | ||
333 | * @subsystem: filter for a subsystem of the device to include in the list | ||
334 | * | ||
335 | * Match only devices belonging to a certain kernel subsystem. | ||
336 | * | ||
337 | * Returns: 0 on success, otherwise a negative error value. | ||
338 | */ | ||
339 | UDEV_EXPORT int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) | ||
340 | { | ||
341 | if (udev_enumerate == NULL) | ||
342 | return -EINVAL; | ||
343 | if (subsystem == NULL) | ||
344 | return 0; | ||
345 | if (udev_list_entry_add(&udev_enumerate->subsystem_match_list, subsystem, NULL) == NULL) | ||
346 | return -ENOMEM; | ||
347 | return 0; | ||
348 | } | ||
349 | |||
350 | /** | ||
351 | * udev_enumerate_add_nomatch_subsystem: | ||
352 | * @udev_enumerate: context | ||
353 | * @subsystem: filter for a subsystem of the device to exclude from the list | ||
354 | * | ||
355 | * Match only devices not belonging to a certain kernel subsystem. | ||
356 | * | ||
357 | * Returns: 0 on success, otherwise a negative error value. | ||
358 | */ | ||
359 | UDEV_EXPORT int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) | ||
360 | { | ||
361 | if (udev_enumerate == NULL) | ||
362 | return -EINVAL; | ||
363 | if (subsystem == NULL) | ||
364 | return 0; | ||
365 | if (udev_list_entry_add(&udev_enumerate->subsystem_nomatch_list, subsystem, NULL) == NULL) | ||
366 | return -ENOMEM; | ||
367 | return 0; | ||
368 | } | ||
369 | |||
370 | /** | ||
371 | * udev_enumerate_add_match_sysattr: | ||
372 | * @udev_enumerate: context | ||
373 | * @sysattr: filter for a sys attribute at the device to include in the list | ||
374 | * @value: optional value of the sys attribute | ||
375 | * | ||
376 | * Match only devices with a certain /sys device attribute. | ||
377 | * | ||
378 | * Returns: 0 on success, otherwise a negative error value. | ||
379 | */ | ||
380 | UDEV_EXPORT int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) | ||
381 | { | ||
382 | if (udev_enumerate == NULL) | ||
383 | return -EINVAL; | ||
384 | if (sysattr == NULL) | ||
385 | return 0; | ||
386 | if (udev_list_entry_add(&udev_enumerate->sysattr_match_list, sysattr, value) == NULL) | ||
387 | return -ENOMEM; | ||
388 | return 0; | ||
389 | } | ||
390 | |||
391 | /** | ||
392 | * udev_enumerate_add_nomatch_sysattr: | ||
393 | * @udev_enumerate: context | ||
394 | * @sysattr: filter for a sys attribute at the device to exclude from the list | ||
395 | * @value: optional value of the sys attribute | ||
396 | * | ||
397 | * Match only devices not having a certain /sys device attribute. | ||
398 | * | ||
399 | * Returns: 0 on success, otherwise a negative error value. | ||
400 | */ | ||
401 | UDEV_EXPORT int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) | ||
402 | { | ||
403 | if (udev_enumerate == NULL) | ||
404 | return -EINVAL; | ||
405 | if (sysattr == NULL) | ||
406 | return 0; | ||
407 | if (udev_list_entry_add(&udev_enumerate->sysattr_nomatch_list, sysattr, value) == NULL) | ||
408 | return -ENOMEM; | ||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | static int match_sysattr_value(struct udev_device *dev, const char *sysattr, const char *match_val) | ||
413 | { | ||
414 | const char *val = NULL; | ||
415 | bool match = false; | ||
416 | |||
417 | val = udev_device_get_sysattr_value(dev, sysattr); | ||
418 | if (val == NULL) | ||
419 | goto exit; | ||
420 | if (match_val == NULL) { | ||
421 | match = true; | ||
422 | goto exit; | ||
423 | } | ||
424 | if (fnmatch(match_val, val, 0) == 0) { | ||
425 | match = true; | ||
426 | goto exit; | ||
427 | } | ||
428 | exit: | ||
429 | return match; | ||
430 | } | ||
431 | |||
432 | /** | ||
433 | * udev_enumerate_add_match_property: | ||
434 | * @udev_enumerate: context | ||
435 | * @property: filter for a property of the device to include in the list | ||
436 | * @value: value of the property | ||
437 | * | ||
438 | * Match only devices with a certain property. | ||
439 | * | ||
440 | * Returns: 0 on success, otherwise a negative error value. | ||
441 | */ | ||
442 | UDEV_EXPORT int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value) | ||
443 | { | ||
444 | if (udev_enumerate == NULL) | ||
445 | return -EINVAL; | ||
446 | if (property == NULL) | ||
447 | return 0; | ||
448 | if (udev_list_entry_add(&udev_enumerate->properties_match_list, property, value) == NULL) | ||
449 | return -ENOMEM; | ||
450 | return 0; | ||
451 | } | ||
452 | |||
453 | /** | ||
454 | * udev_enumerate_add_match_tag: | ||
455 | * @udev_enumerate: context | ||
456 | * @tag: filter for a tag of the device to include in the list | ||
457 | * | ||
458 | * Match only devices with a certain tag. | ||
459 | * | ||
460 | * Returns: 0 on success, otherwise a negative error value. | ||
461 | */ | ||
462 | UDEV_EXPORT int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag) | ||
463 | { | ||
464 | if (udev_enumerate == NULL) | ||
465 | return -EINVAL; | ||
466 | if (tag == NULL) | ||
467 | return 0; | ||
468 | if (udev_list_entry_add(&udev_enumerate->tags_match_list, tag, NULL) == NULL) | ||
469 | return -ENOMEM; | ||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | /** | ||
474 | * udev_enumerate_add_match_parent: | ||
475 | * @udev_enumerate: context | ||
476 | * @parent: parent device where to start searching | ||
477 | * | ||
478 | * Return the devices on the subtree of one given device. The parent | ||
479 | * itself is included in the list. | ||
480 | * | ||
481 | * A reference for the device is held until the udev_enumerate context | ||
482 | * is cleaned up. | ||
483 | * | ||
484 | * Returns: 0 on success, otherwise a negative error value. | ||
485 | */ | ||
486 | UDEV_EXPORT int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent) | ||
487 | { | ||
488 | if (udev_enumerate == NULL) | ||
489 | return -EINVAL; | ||
490 | if (parent == NULL) | ||
491 | return 0; | ||
492 | if (udev_enumerate->parent_match != NULL) | ||
493 | udev_device_unref(udev_enumerate->parent_match); | ||
494 | udev_enumerate->parent_match = udev_device_ref(parent); | ||
495 | return 0; | ||
496 | } | ||
497 | |||
498 | /** | ||
499 | * udev_enumerate_add_match_is_initialized: | ||
500 | * @udev_enumerate: context | ||
501 | * | ||
502 | * Match only devices which udev has set up already. This makes | ||
503 | * sure, that the device node permissions and context are properly set | ||
504 | * and that network devices are fully renamed. | ||
505 | * | ||
506 | * Usually, devices which are found in the kernel but not already | ||
507 | * handled by udev, have still pending events. Services should subscribe | ||
508 | * to monitor events and wait for these devices to become ready, instead | ||
509 | * of using uninitialized devices. | ||
510 | * | ||
511 | * For now, this will not affect devices which do not have a device node | ||
512 | * and are not network interfaces. | ||
513 | * | ||
514 | * Returns: 0 on success, otherwise a negative error value. | ||
515 | */ | ||
516 | UDEV_EXPORT int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate) | ||
517 | { | ||
518 | if (udev_enumerate == NULL) | ||
519 | return -EINVAL; | ||
520 | udev_enumerate->match_is_initialized = true; | ||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | /** | ||
525 | * udev_enumerate_add_match_sysname: | ||
526 | * @udev_enumerate: context | ||
527 | * @sysname: filter for the name of the device to include in the list | ||
528 | * | ||
529 | * Match only devices with a given /sys device name. | ||
530 | * | ||
531 | * Returns: 0 on success, otherwise a negative error value. | ||
532 | */ | ||
533 | UDEV_EXPORT int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname) | ||
534 | { | ||
535 | if (udev_enumerate == NULL) | ||
536 | return -EINVAL; | ||
537 | if (sysname == NULL) | ||
538 | return 0; | ||
539 | if (udev_list_entry_add(&udev_enumerate->sysname_match_list, sysname, NULL) == NULL) | ||
540 | return -ENOMEM; | ||
541 | return 0; | ||
542 | } | ||
543 | |||
544 | static bool match_sysattr(struct udev_enumerate *udev_enumerate, struct udev_device *dev) | ||
545 | { | ||
546 | struct udev_list_entry *list_entry; | ||
547 | |||
548 | /* skip list */ | ||
549 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysattr_nomatch_list)) { | ||
550 | if (match_sysattr_value(dev, udev_list_entry_get_name(list_entry), | ||
551 | udev_list_entry_get_value(list_entry))) | ||
552 | return false; | ||
553 | } | ||
554 | /* include list */ | ||
555 | if (udev_list_get_entry(&udev_enumerate->sysattr_match_list) != NULL) { | ||
556 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysattr_match_list)) { | ||
557 | /* anything that does not match, will make it FALSE */ | ||
558 | if (!match_sysattr_value(dev, udev_list_entry_get_name(list_entry), | ||
559 | udev_list_entry_get_value(list_entry))) | ||
560 | return false; | ||
561 | } | ||
562 | return true; | ||
563 | } | ||
564 | return true; | ||
565 | } | ||
566 | |||
567 | static bool match_property(struct udev_enumerate *udev_enumerate, struct udev_device *dev) | ||
568 | { | ||
569 | struct udev_list_entry *list_entry; | ||
570 | bool match = false; | ||
571 | |||
572 | /* no match always matches */ | ||
573 | if (udev_list_get_entry(&udev_enumerate->properties_match_list) == NULL) | ||
574 | return true; | ||
575 | |||
576 | /* loop over matches */ | ||
577 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->properties_match_list)) { | ||
578 | const char *match_key = udev_list_entry_get_name(list_entry); | ||
579 | const char *match_value = udev_list_entry_get_value(list_entry); | ||
580 | struct udev_list_entry *property_entry; | ||
581 | |||
582 | /* loop over device properties */ | ||
583 | udev_list_entry_foreach(property_entry, udev_device_get_properties_list_entry(dev)) { | ||
584 | const char *dev_key = udev_list_entry_get_name(property_entry); | ||
585 | const char *dev_value = udev_list_entry_get_value(property_entry); | ||
586 | |||
587 | if (fnmatch(match_key, dev_key, 0) != 0) | ||
588 | continue; | ||
589 | if (match_value == NULL && dev_value == NULL) { | ||
590 | match = true; | ||
591 | goto out; | ||
592 | } | ||
593 | if (match_value == NULL || dev_value == NULL) | ||
594 | continue; | ||
595 | if (fnmatch(match_value, dev_value, 0) == 0) { | ||
596 | match = true; | ||
597 | goto out; | ||
598 | } | ||
599 | } | ||
600 | } | ||
601 | out: | ||
602 | return match; | ||
603 | } | ||
604 | |||
605 | static bool match_tag(struct udev_enumerate *udev_enumerate, struct udev_device *dev) | ||
606 | { | ||
607 | struct udev_list_entry *list_entry; | ||
608 | |||
609 | /* no match always matches */ | ||
610 | if (udev_list_get_entry(&udev_enumerate->tags_match_list) == NULL) | ||
611 | return true; | ||
612 | |||
613 | /* loop over matches */ | ||
614 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->tags_match_list)) | ||
615 | if (!udev_device_has_tag(dev, udev_list_entry_get_name(list_entry))) | ||
616 | return false; | ||
617 | |||
618 | return true; | ||
619 | } | ||
620 | |||
621 | static bool match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *dev) | ||
622 | { | ||
623 | const char *parent; | ||
624 | |||
625 | if (udev_enumerate->parent_match == NULL) | ||
626 | return true; | ||
627 | |||
628 | parent = udev_device_get_devpath(udev_enumerate->parent_match); | ||
629 | return strncmp(parent, udev_device_get_devpath(dev), strlen(parent)) == 0; | ||
630 | } | ||
631 | |||
632 | static bool match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname) | ||
633 | { | ||
634 | struct udev_list_entry *list_entry; | ||
635 | |||
636 | if (udev_list_get_entry(&udev_enumerate->sysname_match_list) == NULL) | ||
637 | return true; | ||
638 | |||
639 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysname_match_list)) { | ||
640 | if (fnmatch(udev_list_entry_get_name(list_entry), sysname, 0) != 0) | ||
641 | continue; | ||
642 | return true; | ||
643 | } | ||
644 | return false; | ||
645 | } | ||
646 | |||
647 | static int scan_dir_and_add_devices(struct udev_enumerate *udev_enumerate, | ||
648 | const char *basedir, const char *subdir1, const char *subdir2) | ||
649 | { | ||
650 | struct udev *udev = udev_enumerate_get_udev(udev_enumerate); | ||
651 | char path[UTIL_PATH_SIZE]; | ||
652 | size_t l; | ||
653 | char *s; | ||
654 | DIR *dir; | ||
655 | struct dirent *dent; | ||
656 | |||
657 | s = path; | ||
658 | l = util_strpcpyl(&s, sizeof(path), udev_get_sys_path(udev), "/", basedir, NULL); | ||
659 | if (subdir1 != NULL) | ||
660 | l = util_strpcpyl(&s, l, "/", subdir1, NULL); | ||
661 | if (subdir2 != NULL) | ||
662 | util_strpcpyl(&s, l, "/", subdir2, NULL); | ||
663 | dir = opendir(path); | ||
664 | if (dir == NULL) | ||
665 | return -ENOENT; | ||
666 | for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { | ||
667 | char syspath[UTIL_PATH_SIZE]; | ||
668 | struct udev_device *dev; | ||
669 | |||
670 | if (dent->d_name[0] == '.') | ||
671 | continue; | ||
672 | |||
673 | if (!match_sysname(udev_enumerate, dent->d_name)) | ||
674 | continue; | ||
675 | |||
676 | util_strscpyl(syspath, sizeof(syspath), path, "/", dent->d_name, NULL); | ||
677 | dev = udev_device_new_from_syspath(udev_enumerate->udev, syspath); | ||
678 | if (dev == NULL) | ||
679 | continue; | ||
680 | |||
681 | if (udev_enumerate->match_is_initialized) { | ||
682 | /* | ||
683 | * All devices with a device node or network interfaces | ||
684 | * possibly need udev to adjust the device node permission | ||
685 | * or context, or rename the interface before it can be | ||
686 | * reliably used from other processes. | ||
687 | * | ||
688 | * For now, we can only check these types of devices, we | ||
689 | * might not store a database, and have no way to find out | ||
690 | * for all other types of devices. | ||
691 | */ | ||
692 | if (!udev_device_get_is_initialized(dev) && | ||
693 | (major(udev_device_get_devnum(dev)) > 0 || udev_device_get_ifindex(dev) > 0)) | ||
694 | goto nomatch; | ||
695 | } | ||
696 | if (!match_parent(udev_enumerate, dev)) | ||
697 | goto nomatch; | ||
698 | if (!match_tag(udev_enumerate, dev)) | ||
699 | goto nomatch; | ||
700 | if (!match_property(udev_enumerate, dev)) | ||
701 | goto nomatch; | ||
702 | if (!match_sysattr(udev_enumerate, dev)) | ||
703 | goto nomatch; | ||
704 | |||
705 | syspath_add(udev_enumerate, udev_device_get_syspath(dev)); | ||
706 | nomatch: | ||
707 | udev_device_unref(dev); | ||
708 | } | ||
709 | closedir(dir); | ||
710 | return 0; | ||
711 | } | ||
712 | |||
713 | static bool match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) | ||
714 | { | ||
715 | struct udev_list_entry *list_entry; | ||
716 | |||
717 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->subsystem_nomatch_list)) { | ||
718 | if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0) | ||
719 | return false; | ||
720 | } | ||
721 | if (udev_list_get_entry(&udev_enumerate->subsystem_match_list) != NULL) { | ||
722 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->subsystem_match_list)) { | ||
723 | if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0) | ||
724 | return true; | ||
725 | } | ||
726 | return false; | ||
727 | } | ||
728 | return true; | ||
729 | } | ||
730 | |||
731 | static int scan_dir(struct udev_enumerate *udev_enumerate, const char *basedir, const char *subdir, const char *subsystem) | ||
732 | { | ||
733 | struct udev *udev = udev_enumerate_get_udev(udev_enumerate); | ||
734 | |||
735 | char path[UTIL_PATH_SIZE]; | ||
736 | DIR *dir; | ||
737 | struct dirent *dent; | ||
738 | |||
739 | util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), "/", basedir, NULL); | ||
740 | dir = opendir(path); | ||
741 | if (dir == NULL) | ||
742 | return -1; | ||
743 | for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { | ||
744 | if (dent->d_name[0] == '.') | ||
745 | continue; | ||
746 | if (!match_subsystem(udev_enumerate, subsystem != NULL ? subsystem : dent->d_name)) | ||
747 | continue; | ||
748 | scan_dir_and_add_devices(udev_enumerate, basedir, dent->d_name, subdir); | ||
749 | } | ||
750 | closedir(dir); | ||
751 | return 0; | ||
752 | } | ||
753 | |||
754 | /** | ||
755 | * udev_enumerate_add_syspath: | ||
756 | * @udev_enumerate: context | ||
757 | * @syspath: path of a device | ||
758 | * | ||
759 | * Add a device to the list of devices, to retrieve it back sorted in dependency order. | ||
760 | * | ||
761 | * Returns: 0 on success, otherwise a negative error value. | ||
762 | */ | ||
763 | UDEV_EXPORT int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath) | ||
764 | { | ||
765 | struct udev_device *udev_device; | ||
766 | |||
767 | if (udev_enumerate == NULL) | ||
768 | return -EINVAL; | ||
769 | if (syspath == NULL) | ||
770 | return 0; | ||
771 | /* resolve to real syspath */ | ||
772 | udev_device = udev_device_new_from_syspath(udev_enumerate->udev, syspath); | ||
773 | if (udev_device == NULL) | ||
774 | return -EINVAL; | ||
775 | syspath_add(udev_enumerate, udev_device_get_syspath(udev_device)); | ||
776 | udev_device_unref(udev_device); | ||
777 | return 0; | ||
778 | } | ||
779 | |||
780 | static int scan_devices_tags(struct udev_enumerate *udev_enumerate) | ||
781 | { | ||
782 | struct udev *udev = udev_enumerate_get_udev(udev_enumerate); | ||
783 | struct udev_list_entry *list_entry; | ||
784 | |||
785 | /* scan only tagged devices, use tags reverse-index, instead of searching all devices in /sys */ | ||
786 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->tags_match_list)) { | ||
787 | DIR *dir; | ||
788 | struct dirent *dent; | ||
789 | char path[UTIL_PATH_SIZE]; | ||
790 | |||
791 | util_strscpyl(path, sizeof(path), udev_get_run_path(udev), "/tags/", | ||
792 | udev_list_entry_get_name(list_entry), NULL); | ||
793 | dir = opendir(path); | ||
794 | if (dir == NULL) | ||
795 | continue; | ||
796 | for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { | ||
797 | struct udev_device *dev; | ||
798 | |||
799 | if (dent->d_name[0] == '.') | ||
800 | continue; | ||
801 | |||
802 | dev = udev_device_new_from_device_id(udev_enumerate->udev, dent->d_name); | ||
803 | if (dev == NULL) | ||
804 | continue; | ||
805 | |||
806 | if (!match_subsystem(udev_enumerate, udev_device_get_subsystem(dev))) | ||
807 | goto nomatch; | ||
808 | if (!match_sysname(udev_enumerate, udev_device_get_sysname(dev))) | ||
809 | goto nomatch; | ||
810 | if (!match_parent(udev_enumerate, dev)) | ||
811 | goto nomatch; | ||
812 | if (!match_property(udev_enumerate, dev)) | ||
813 | goto nomatch; | ||
814 | if (!match_sysattr(udev_enumerate, dev)) | ||
815 | goto nomatch; | ||
816 | |||
817 | syspath_add(udev_enumerate, udev_device_get_syspath(dev)); | ||
818 | nomatch: | ||
819 | udev_device_unref(dev); | ||
820 | } | ||
821 | closedir(dir); | ||
822 | } | ||
823 | return 0; | ||
824 | } | ||
825 | |||
826 | static int parent_add_child(struct udev_enumerate *enumerate, const char *path) | ||
827 | { | ||
828 | struct udev_device *dev; | ||
829 | |||
830 | dev = udev_device_new_from_syspath(enumerate->udev, path); | ||
831 | if (dev == NULL) | ||
832 | return -ENODEV; | ||
833 | |||
834 | if (!match_subsystem(enumerate, udev_device_get_subsystem(dev))) | ||
835 | return 0; | ||
836 | if (!match_sysname(enumerate, udev_device_get_sysname(dev))) | ||
837 | return 0; | ||
838 | if (!match_property(enumerate, dev)) | ||
839 | return 0; | ||
840 | if (!match_sysattr(enumerate, dev)) | ||
841 | return 0; | ||
842 | |||
843 | syspath_add(enumerate, udev_device_get_syspath(dev)); | ||
844 | udev_device_unref(dev); | ||
845 | return 1; | ||
846 | } | ||
847 | |||
848 | static int parent_crawl_children(struct udev_enumerate *enumerate, const char *path, int maxdepth) | ||
849 | { | ||
850 | DIR *d; | ||
851 | struct dirent *dent; | ||
852 | |||
853 | d = opendir(path); | ||
854 | if (d == NULL) | ||
855 | return -errno; | ||
856 | |||
857 | for (dent = readdir(d); dent != NULL; dent = readdir(d)) { | ||
858 | char *child; | ||
859 | |||
860 | if (dent->d_name[0] == '.') | ||
861 | continue; | ||
862 | if (dent->d_type != DT_DIR) | ||
863 | continue; | ||
864 | if (asprintf(&child, "%s/%s", path, dent->d_name) < 0) | ||
865 | continue; | ||
866 | parent_add_child(enumerate, child); | ||
867 | if (maxdepth > 0) | ||
868 | parent_crawl_children(enumerate, child, maxdepth-1); | ||
869 | free(child); | ||
870 | } | ||
871 | |||
872 | closedir(d); | ||
873 | return 0; | ||
874 | } | ||
875 | |||
876 | static int scan_devices_children(struct udev_enumerate *enumerate) | ||
877 | { | ||
878 | const char *path; | ||
879 | |||
880 | path = udev_device_get_syspath(enumerate->parent_match); | ||
881 | parent_add_child(enumerate, path); | ||
882 | return parent_crawl_children(enumerate, path, 256); | ||
883 | } | ||
884 | |||
885 | static int scan_devices_all(struct udev_enumerate *udev_enumerate) | ||
886 | { | ||
887 | struct udev *udev = udev_enumerate_get_udev(udev_enumerate); | ||
888 | char base[UTIL_PATH_SIZE]; | ||
889 | struct stat statbuf; | ||
890 | |||
891 | util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/subsystem", NULL); | ||
892 | if (stat(base, &statbuf) == 0) { | ||
893 | /* we have /subsystem/, forget all the old stuff */ | ||
894 | dbg(udev, "searching '/subsystem/*/devices/*' dir\n"); | ||
895 | scan_dir(udev_enumerate, "subsystem", "devices", NULL); | ||
896 | } else { | ||
897 | dbg(udev, "searching '/bus/*/devices/*' dir\n"); | ||
898 | scan_dir(udev_enumerate, "bus", "devices", NULL); | ||
899 | dbg(udev, "searching '/class/*' dir\n"); | ||
900 | scan_dir(udev_enumerate, "class", NULL, NULL); | ||
901 | } | ||
902 | return 0; | ||
903 | } | ||
904 | |||
905 | /** | ||
906 | * udev_enumerate_scan_devices: | ||
907 | * @udev_enumerate: udev enumeration context | ||
908 | * | ||
909 | * Scan /sys for all devices which match the given filters. No matches | ||
910 | * will return all currently available devices. | ||
911 | * | ||
912 | * Returns: 0 on success, otherwise a negative error value. | ||
913 | **/ | ||
914 | UDEV_EXPORT int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate) | ||
915 | { | ||
916 | if (udev_enumerate == NULL) | ||
917 | return -EINVAL; | ||
918 | |||
919 | /* efficiently lookup tags only, we maintain a reverse-index */ | ||
920 | if (udev_list_get_entry(&udev_enumerate->tags_match_list) != NULL) | ||
921 | return scan_devices_tags(udev_enumerate); | ||
922 | |||
923 | /* walk the subtree of one parent device only */ | ||
924 | if (udev_enumerate->parent_match != NULL) | ||
925 | return scan_devices_children(udev_enumerate); | ||
926 | |||
927 | /* scan devices of all subsystems */ | ||
928 | return scan_devices_all(udev_enumerate); | ||
929 | } | ||
930 | |||
931 | /** | ||
932 | * udev_enumerate_scan_subsystems: | ||
933 | * @udev_enumerate: udev enumeration context | ||
934 | * | ||
935 | * Scan /sys for all kernel subsystems, including buses, classes, drivers. | ||
936 | * | ||
937 | * Returns: 0 on success, otherwise a negative error value. | ||
938 | **/ | ||
939 | UDEV_EXPORT int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate) | ||
940 | { | ||
941 | struct udev *udev = udev_enumerate_get_udev(udev_enumerate); | ||
942 | char base[UTIL_PATH_SIZE]; | ||
943 | struct stat statbuf; | ||
944 | const char *subsysdir; | ||
945 | |||
946 | if (udev_enumerate == NULL) | ||
947 | return -EINVAL; | ||
948 | |||
949 | /* all kernel modules */ | ||
950 | if (match_subsystem(udev_enumerate, "module")) { | ||
951 | dbg(udev, "searching 'modules/*' dir\n"); | ||
952 | scan_dir_and_add_devices(udev_enumerate, "module", NULL, NULL); | ||
953 | } | ||
954 | |||
955 | util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/subsystem", NULL); | ||
956 | if (stat(base, &statbuf) == 0) | ||
957 | subsysdir = "subsystem"; | ||
958 | else | ||
959 | subsysdir = "bus"; | ||
960 | |||
961 | /* all subsystems (only buses support coldplug) */ | ||
962 | if (match_subsystem(udev_enumerate, "subsystem")) { | ||
963 | dbg(udev, "searching '%s/*' dir\n", subsysdir); | ||
964 | scan_dir_and_add_devices(udev_enumerate, subsysdir, NULL, NULL); | ||
965 | } | ||
966 | |||
967 | /* all subsystem drivers */ | ||
968 | if (match_subsystem(udev_enumerate, "drivers")) { | ||
969 | dbg(udev, "searching '%s/*/drivers/*' dir\n", subsysdir); | ||
970 | scan_dir(udev_enumerate, subsysdir, "drivers", "drivers"); | ||
971 | } | ||
972 | return 0; | ||
973 | } |
File src/libudev-list.c added (mode: 100644) (index 0000000..8c0296b) | |||
1 | /* | ||
2 | * libudev - interface to udev device information | ||
3 | * | ||
4 | * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org> | ||
5 | * | ||
6 | * This library is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU Lesser General Public | ||
8 | * License as published by the Free Software Foundation; either | ||
9 | * version 2.1 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <stdio.h> | ||
13 | #include <stdlib.h> | ||
14 | #include <stddef.h> | ||
15 | #include <unistd.h> | ||
16 | #include <errno.h> | ||
17 | #include <string.h> | ||
18 | |||
19 | #include "libudev.h" | ||
20 | #include "libudev-private.h" | ||
21 | |||
22 | /** | ||
23 | * SECTION:libudev-list | ||
24 | * @short_description: list operation | ||
25 | * | ||
26 | * Libudev list operations. | ||
27 | */ | ||
28 | |||
29 | /** | ||
30 | * udev_list_entry: | ||
31 | * | ||
32 | * Opaque object representing one entry in a list. An entry contains | ||
33 | * contains a name, and optionally a value. | ||
34 | */ | ||
35 | struct udev_list_entry { | ||
36 | struct udev_list_node node; | ||
37 | struct udev_list *list; | ||
38 | char *name; | ||
39 | char *value; | ||
40 | int num; | ||
41 | }; | ||
42 | |||
43 | /* the list's head points to itself if empty */ | ||
44 | void udev_list_node_init(struct udev_list_node *list) | ||
45 | { | ||
46 | list->next = list; | ||
47 | list->prev = list; | ||
48 | } | ||
49 | |||
50 | int udev_list_node_is_empty(struct udev_list_node *list) | ||
51 | { | ||
52 | return list->next == list; | ||
53 | } | ||
54 | |||
55 | static void udev_list_node_insert_between(struct udev_list_node *new, | ||
56 | struct udev_list_node *prev, | ||
57 | struct udev_list_node *next) | ||
58 | { | ||
59 | next->prev = new; | ||
60 | new->next = next; | ||
61 | new->prev = prev; | ||
62 | prev->next = new; | ||
63 | } | ||
64 | |||
65 | void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list) | ||
66 | { | ||
67 | udev_list_node_insert_between(new, list->prev, list); | ||
68 | } | ||
69 | |||
70 | void udev_list_node_remove(struct udev_list_node *entry) | ||
71 | { | ||
72 | struct udev_list_node *prev = entry->prev; | ||
73 | struct udev_list_node *next = entry->next; | ||
74 | |||
75 | next->prev = prev; | ||
76 | prev->next = next; | ||
77 | |||
78 | entry->prev = NULL; | ||
79 | entry->next = NULL; | ||
80 | } | ||
81 | |||
82 | /* return list entry which embeds this node */ | ||
83 | static struct udev_list_entry *list_node_to_entry(struct udev_list_node *node) | ||
84 | { | ||
85 | char *list; | ||
86 | |||
87 | list = (char *)node; | ||
88 | list -= offsetof(struct udev_list_entry, node); | ||
89 | return (struct udev_list_entry *)list; | ||
90 | } | ||
91 | |||
92 | void udev_list_init(struct udev *udev, struct udev_list *list, bool unique) | ||
93 | { | ||
94 | memset(list, 0x00, sizeof(struct udev_list)); | ||
95 | list->udev = udev; | ||
96 | list->unique = unique; | ||
97 | udev_list_node_init(&list->node); | ||
98 | } | ||
99 | |||
100 | /* insert entry into a list as the last element */ | ||
101 | void udev_list_entry_append(struct udev_list_entry *new, struct udev_list *list) | ||
102 | { | ||
103 | /* inserting before the list head make the node the last node in the list */ | ||
104 | udev_list_node_insert_between(&new->node, list->node.prev, &list->node); | ||
105 | new->list = list; | ||
106 | } | ||
107 | |||
108 | /* insert entry into a list, before a given existing entry */ | ||
109 | void udev_list_entry_insert_before(struct udev_list_entry *new, struct udev_list_entry *entry) | ||
110 | { | ||
111 | udev_list_node_insert_between(&new->node, entry->node.prev, &entry->node); | ||
112 | new->list = entry->list; | ||
113 | } | ||
114 | |||
115 | /* binary search in sorted array */ | ||
116 | static int list_search(struct udev_list *list, const char *name) | ||
117 | { | ||
118 | unsigned int first, last; | ||
119 | |||
120 | first = 0; | ||
121 | last = list->entries_cur; | ||
122 | while (first < last) { | ||
123 | unsigned int i; | ||
124 | int cmp; | ||
125 | |||
126 | i = (first + last)/2; | ||
127 | cmp = strcmp(name, list->entries[i]->name); | ||
128 | if (cmp < 0) | ||
129 | last = i; | ||
130 | else if (cmp > 0) | ||
131 | first = i+1; | ||
132 | else | ||
133 | return i; | ||
134 | } | ||
135 | |||
136 | /* not found, return negative insertion-index+1 */ | ||
137 | return -(first+1); | ||
138 | } | ||
139 | |||
140 | struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *name, const char *value) | ||
141 | { | ||
142 | struct udev_list_entry *entry; | ||
143 | int i = 0; | ||
144 | |||
145 | if (list->unique) { | ||
146 | /* lookup existing name or insertion-index */ | ||
147 | i = list_search(list, name); | ||
148 | if (i >= 0) { | ||
149 | entry = list->entries[i]; | ||
150 | |||
151 | dbg(list->udev, "'%s' is already in the list\n", name); | ||
152 | free(entry->value); | ||
153 | if (value == NULL) { | ||
154 | entry->value = NULL; | ||
155 | dbg(list->udev, "'%s' value unset\n", name); | ||
156 | return entry; | ||
157 | } | ||
158 | entry->value = strdup(value); | ||
159 | if (entry->value == NULL) | ||
160 | return NULL; | ||
161 | dbg(list->udev, "'%s' value replaced with '%s'\n", name, value); | ||
162 | return entry; | ||
163 | } | ||
164 | } | ||
165 | |||
166 | /* add new name */ | ||
167 | entry = calloc(1, sizeof(struct udev_list_entry)); | ||
168 | if (entry == NULL) | ||
169 | return NULL; | ||
170 | entry->name = strdup(name); | ||
171 | if (entry->name == NULL) { | ||
172 | free(entry); | ||
173 | return NULL; | ||
174 | } | ||
175 | if (value != NULL) { | ||
176 | entry->value = strdup(value); | ||
177 | if (entry->value == NULL) { | ||
178 | free(entry->name); | ||
179 | free(entry); | ||
180 | return NULL; | ||
181 | } | ||
182 | } | ||
183 | |||
184 | if (list->unique) { | ||
185 | /* allocate or enlarge sorted array if needed */ | ||
186 | if (list->entries_cur >= list->entries_max) { | ||
187 | unsigned int add; | ||
188 | |||
189 | add = list->entries_max; | ||
190 | if (add < 1) | ||
191 | add = 64; | ||
192 | list->entries = realloc(list->entries, (list->entries_max + add) * sizeof(struct udev_list_entry *)); | ||
193 | if (list->entries == NULL) { | ||
194 | free(entry->name); | ||
195 | free(entry->value); | ||
196 | free(entry); | ||
197 | return NULL; | ||
198 | } | ||
199 | list->entries_max += add; | ||
200 | } | ||
201 | |||
202 | /* the negative i returned the insertion index */ | ||
203 | i = (-i)-1; | ||
204 | |||
205 | /* insert into sorted list */ | ||
206 | if ((unsigned int)i < list->entries_cur) | ||
207 | udev_list_entry_insert_before(entry, list->entries[i]); | ||
208 | else | ||
209 | udev_list_entry_append(entry, list); | ||
210 | |||
211 | /* insert into sorted array */ | ||
212 | memmove(&list->entries[i+1], &list->entries[i], | ||
213 | (list->entries_cur - i) * sizeof(struct udev_list_entry *)); | ||
214 | list->entries[i] = entry; | ||
215 | list->entries_cur++; | ||
216 | } else { | ||
217 | udev_list_entry_append(entry, list); | ||
218 | } | ||
219 | |||
220 | dbg(list->udev, "'%s=%s' added\n", entry->name, entry->value); | ||
221 | return entry; | ||
222 | } | ||
223 | |||
224 | void udev_list_entry_delete(struct udev_list_entry *entry) | ||
225 | { | ||
226 | if (entry->list->entries != NULL) { | ||
227 | int i; | ||
228 | struct udev_list *list = entry->list; | ||
229 | |||
230 | /* remove entry from sorted array */ | ||
231 | i = list_search(list, entry->name); | ||
232 | if (i >= 0) { | ||
233 | memmove(&list->entries[i], &list->entries[i+1], | ||
234 | ((list->entries_cur-1) - i) * sizeof(struct udev_list_entry *)); | ||
235 | list->entries_cur--; | ||
236 | } | ||
237 | } | ||
238 | |||
239 | udev_list_node_remove(&entry->node); | ||
240 | free(entry->name); | ||
241 | free(entry->value); | ||
242 | free(entry); | ||
243 | } | ||
244 | |||
245 | void udev_list_cleanup(struct udev_list *list) | ||
246 | { | ||
247 | struct udev_list_entry *entry_loop; | ||
248 | struct udev_list_entry *entry_tmp; | ||
249 | |||
250 | free(list->entries); | ||
251 | list->entries = NULL; | ||
252 | list->entries_cur = 0; | ||
253 | list->entries_max = 0; | ||
254 | udev_list_entry_foreach_safe(entry_loop, entry_tmp, udev_list_get_entry(list)) | ||
255 | udev_list_entry_delete(entry_loop); | ||
256 | } | ||
257 | |||
258 | struct udev_list_entry *udev_list_get_entry(struct udev_list *list) | ||
259 | { | ||
260 | if (udev_list_node_is_empty(&list->node)) | ||
261 | return NULL; | ||
262 | return list_node_to_entry(list->node.next); | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * udev_list_entry_get_next: | ||
267 | * @list_entry: current entry | ||
268 | * | ||
269 | * Get the next entry from the list. | ||
270 | * | ||
271 | * Returns: udev_list_entry, #NULL if no more entries are available. | ||
272 | */ | ||
273 | UDEV_EXPORT struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry) | ||
274 | { | ||
275 | struct udev_list_node *next; | ||
276 | |||
277 | if (list_entry == NULL) | ||
278 | return NULL; | ||
279 | next = list_entry->node.next; | ||
280 | /* empty list or no more entries */ | ||
281 | if (next == &list_entry->list->node) | ||
282 | return NULL; | ||
283 | return list_node_to_entry(next); | ||
284 | } | ||
285 | |||
286 | /** | ||
287 | * udev_list_entry_get_by_name: | ||
288 | * @list_entry: current entry | ||
289 | * @name: name string to match | ||
290 | * | ||
291 | * Lookup an entry in the list with a certain name. | ||
292 | * | ||
293 | * Returns: udev_list_entry, #NULL if no matching entry is found. | ||
294 | */ | ||
295 | UDEV_EXPORT struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name) | ||
296 | { | ||
297 | int i; | ||
298 | |||
299 | if (list_entry == NULL) | ||
300 | return NULL; | ||
301 | |||
302 | if (!list_entry->list->unique) | ||
303 | return NULL; | ||
304 | |||
305 | i = list_search(list_entry->list, name); | ||
306 | if (i < 0) | ||
307 | return NULL; | ||
308 | return list_entry->list->entries[i]; | ||
309 | } | ||
310 | |||
311 | /** | ||
312 | * udev_list_entry_get_name: | ||
313 | * @list_entry: current entry | ||
314 | * | ||
315 | * Get the name of a list entry. | ||
316 | * | ||
317 | * Returns: the name string of this entry. | ||
318 | */ | ||
319 | UDEV_EXPORT const char *udev_list_entry_get_name(struct udev_list_entry *list_entry) | ||
320 | { | ||
321 | if (list_entry == NULL) | ||
322 | return NULL; | ||
323 | return list_entry->name; | ||
324 | } | ||
325 | |||
326 | /** | ||
327 | * udev_list_entry_get_value: | ||
328 | * @list_entry: current entry | ||
329 | * | ||
330 | * Get the value of list entry. | ||
331 | * | ||
332 | * Returns: the value string of this entry. | ||
333 | */ | ||
334 | UDEV_EXPORT const char *udev_list_entry_get_value(struct udev_list_entry *list_entry) | ||
335 | { | ||
336 | if (list_entry == NULL) | ||
337 | return NULL; | ||
338 | return list_entry->value; | ||
339 | } | ||
340 | |||
341 | int udev_list_entry_get_num(struct udev_list_entry *list_entry) | ||
342 | { | ||
343 | if (list_entry == NULL) | ||
344 | return -EINVAL; | ||
345 | return list_entry->num; | ||
346 | } | ||
347 | |||
348 | void udev_list_entry_set_num(struct udev_list_entry *list_entry, int num) | ||
349 | { | ||
350 | if (list_entry == NULL) | ||
351 | return; | ||
352 | list_entry->num = num; | ||
353 | } |
File src/libudev-monitor.c added (mode: 100644) (index 0000000..7029585) | |||
1 | /* | ||
2 | * libudev - interface to udev device information | ||
3 | * | ||
4 | * Copyright (C) 2008-2010 Kay Sievers <kay.sievers@vrfy.org> | ||
5 | * | ||
6 | * This library is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU Lesser General Public | ||
8 | * License as published by the Free Software Foundation; either | ||
9 | * version 2.1 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <stdio.h> | ||
13 | #include <stdlib.h> | ||
14 | #include <stddef.h> | ||
15 | #include <unistd.h> | ||
16 | #include <errno.h> | ||
17 | #include <string.h> | ||
18 | #include <dirent.h> | ||
19 | #include <sys/poll.h> | ||
20 | #include <sys/stat.h> | ||
21 | #include <sys/socket.h> | ||
22 | #include <sys/un.h> | ||
23 | #include <arpa/inet.h> | ||
24 | #include <linux/netlink.h> | ||
25 | #include <linux/filter.h> | ||
26 | |||
27 | #include "libudev.h" | ||
28 | #include "libudev-private.h" | ||
29 | |||
30 | /** | ||
31 | * SECTION:libudev-monitor | ||
32 | * @short_description: device event source | ||
33 | * | ||
34 | * Connects to a device event source. | ||
35 | */ | ||
36 | |||
37 | /** | ||
38 | * udev_monitor: | ||
39 | * | ||
40 | * Opaque object handling an event source. | ||
41 | */ | ||
42 | struct udev_monitor { | ||
43 | struct udev *udev; | ||
44 | int refcount; | ||
45 | int sock; | ||
46 | struct sockaddr_nl snl; | ||
47 | struct sockaddr_nl snl_trusted_sender; | ||
48 | struct sockaddr_nl snl_destination; | ||
49 | struct sockaddr_un sun; | ||
50 | socklen_t addrlen; | ||
51 | struct udev_list filter_subsystem_list; | ||
52 | struct udev_list filter_tag_list; | ||
53 | bool bound; | ||
54 | }; | ||
55 | |||
56 | enum udev_monitor_netlink_group { | ||
57 | UDEV_MONITOR_NONE, | ||
58 | UDEV_MONITOR_KERNEL, | ||
59 | UDEV_MONITOR_UDEV, | ||
60 | }; | ||
61 | |||
62 | #define UDEV_MONITOR_MAGIC 0xfeedcafe | ||
63 | struct udev_monitor_netlink_header { | ||
64 | /* "libudev" prefix to distinguish libudev and kernel messages */ | ||
65 | char prefix[8]; | ||
66 | /* | ||
67 | * magic to protect against daemon <-> library message format mismatch | ||
68 | * used in the kernel from socket filter rules; needs to be stored in network order | ||
69 | */ | ||
70 | unsigned int magic; | ||
71 | /* total length of header structure known to the sender */ | ||
72 | unsigned int header_size; | ||
73 | /* properties string buffer */ | ||
74 | unsigned int properties_off; | ||
75 | unsigned int properties_len; | ||
76 | /* | ||
77 | * hashes of primary device properties strings, to let libudev subscribers | ||
78 | * use in-kernel socket filters; values need to be stored in network order | ||
79 | */ | ||
80 | unsigned int filter_subsystem_hash; | ||
81 | unsigned int filter_devtype_hash; | ||
82 | unsigned int filter_tag_bloom_hi; | ||
83 | unsigned int filter_tag_bloom_lo; | ||
84 | }; | ||
85 | |||
86 | static struct udev_monitor *udev_monitor_new(struct udev *udev) | ||
87 | { | ||
88 | struct udev_monitor *udev_monitor; | ||
89 | |||
90 | udev_monitor = calloc(1, sizeof(struct udev_monitor)); | ||
91 | if (udev_monitor == NULL) | ||
92 | return NULL; | ||
93 | udev_monitor->refcount = 1; | ||
94 | udev_monitor->udev = udev; | ||
95 | udev_list_init(udev, &udev_monitor->filter_subsystem_list, false); | ||
96 | udev_list_init(udev, &udev_monitor->filter_tag_list, true); | ||
97 | return udev_monitor; | ||
98 | } | ||
99 | |||
100 | /** | ||
101 | * udev_monitor_new_from_socket: | ||
102 | * @udev: udev library context | ||
103 | * @socket_path: unix socket path | ||
104 | * | ||
105 | * This function should not be used in any new application. The | ||
106 | * kernel's netlink socket multiplexes messages to all interested | ||
107 | * clients. Creating custom sockets from udev to applications | ||
108 | * should be avoided. | ||
109 | * | ||
110 | * Create a new udev monitor and connect to a specified socket. The | ||
111 | * path to a socket either points to an existing socket file, or if | ||
112 | * the socket path starts with a '@' character, an abstract namespace | ||
113 | * socket will be used. | ||
114 | * | ||
115 | * A socket file will not be created. If it does not already exist, | ||
116 | * it will fall-back and connect to an abstract namespace socket with | ||
117 | * the given path. The permissions adjustment of a socket file, as | ||
118 | * well as the later cleanup, needs to be done by the caller. | ||
119 | * | ||
120 | * The initial refcount is 1, and needs to be decremented to | ||
121 | * release the resources of the udev monitor. | ||
122 | * | ||
123 | * Returns: a new udev monitor, or #NULL, in case of an error | ||
124 | **/ | ||
125 | UDEV_EXPORT struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path) | ||
126 | { | ||
127 | struct udev_monitor *udev_monitor; | ||
128 | struct stat statbuf; | ||
129 | |||
130 | if (udev == NULL) | ||
131 | return NULL; | ||
132 | if (socket_path == NULL) | ||
133 | return NULL; | ||
134 | udev_monitor = udev_monitor_new(udev); | ||
135 | if (udev_monitor == NULL) | ||
136 | return NULL; | ||
137 | |||
138 | udev_monitor->sun.sun_family = AF_LOCAL; | ||
139 | if (socket_path[0] == '@') { | ||
140 | /* translate leading '@' to abstract namespace */ | ||
141 | util_strscpy(udev_monitor->sun.sun_path, sizeof(udev_monitor->sun.sun_path), socket_path); | ||
142 | udev_monitor->sun.sun_path[0] = '\0'; | ||
143 | udev_monitor->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(socket_path); | ||
144 | } else if (stat(socket_path, &statbuf) == 0 && S_ISSOCK(statbuf.st_mode)) { | ||
145 | /* existing socket file */ | ||
146 | util_strscpy(udev_monitor->sun.sun_path, sizeof(udev_monitor->sun.sun_path), socket_path); | ||
147 | udev_monitor->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(socket_path); | ||
148 | } else { | ||
149 | /* no socket file, assume abstract namespace socket */ | ||
150 | util_strscpy(&udev_monitor->sun.sun_path[1], sizeof(udev_monitor->sun.sun_path)-1, socket_path); | ||
151 | udev_monitor->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(socket_path)+1; | ||
152 | } | ||
153 | udev_monitor->sock = socket(AF_LOCAL, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); | ||
154 | if (udev_monitor->sock == -1) { | ||
155 | err(udev, "error getting socket: %m\n"); | ||
156 | free(udev_monitor); | ||
157 | return NULL; | ||
158 | } | ||
159 | |||
160 | dbg(udev, "monitor %p created with '%s'\n", udev_monitor, socket_path); | ||
161 | return udev_monitor; | ||
162 | } | ||
163 | |||
164 | struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd) | ||
165 | { | ||
166 | struct udev_monitor *udev_monitor; | ||
167 | unsigned int group; | ||
168 | |||
169 | if (udev == NULL) | ||
170 | return NULL; | ||
171 | |||
172 | if (name == NULL) | ||
173 | group = UDEV_MONITOR_NONE; | ||
174 | else if (strcmp(name, "udev") == 0) | ||
175 | group = UDEV_MONITOR_UDEV; | ||
176 | else if (strcmp(name, "kernel") == 0) | ||
177 | group = UDEV_MONITOR_KERNEL; | ||
178 | else | ||
179 | return NULL; | ||
180 | |||
181 | udev_monitor = udev_monitor_new(udev); | ||
182 | if (udev_monitor == NULL) | ||
183 | return NULL; | ||
184 | |||
185 | if (fd < 0) { | ||
186 | udev_monitor->sock = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT); | ||
187 | if (udev_monitor->sock == -1) { | ||
188 | err(udev, "error getting socket: %m\n"); | ||
189 | free(udev_monitor); | ||
190 | return NULL; | ||
191 | } | ||
192 | } else { | ||
193 | udev_monitor->bound = true; | ||
194 | udev_monitor->sock = fd; | ||
195 | } | ||
196 | |||
197 | udev_monitor->snl.nl_family = AF_NETLINK; | ||
198 | udev_monitor->snl.nl_groups = group; | ||
199 | |||
200 | /* default destination for sending */ | ||
201 | udev_monitor->snl_destination.nl_family = AF_NETLINK; | ||
202 | udev_monitor->snl_destination.nl_groups = UDEV_MONITOR_UDEV; | ||
203 | |||
204 | dbg(udev, "monitor %p created with NETLINK_KOBJECT_UEVENT (%u)\n", udev_monitor, group); | ||
205 | return udev_monitor; | ||
206 | } | ||
207 | |||
208 | /** | ||
209 | * udev_monitor_new_from_netlink: | ||
210 | * @udev: udev library context | ||
211 | * @name: name of event source | ||
212 | * | ||
213 | * Create new udev monitor and connect to a specified event | ||
214 | * source. Valid sources identifiers are "udev" and "kernel". | ||
215 | * | ||
216 | * Applications should usually not connect directly to the | ||
217 | * "kernel" events, because the devices might not be useable | ||
218 | * at that time, before udev has configured them, and created | ||
219 | * device nodes. Accessing devices at the same time as udev, | ||
220 | * might result in unpredictable behavior. The "udev" events | ||
221 | * are sent out after udev has finished its event processing, | ||
222 | * all rules have been processed, and needed device nodes are | ||
223 | * created. | ||
224 | * | ||
225 | * The initial refcount is 1, and needs to be decremented to | ||
226 | * release the resources of the udev monitor. | ||
227 | * | ||
228 | * Returns: a new udev monitor, or #NULL, in case of an error | ||
229 | **/ | ||
230 | UDEV_EXPORT struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name) | ||
231 | { | ||
232 | return udev_monitor_new_from_netlink_fd(udev, name, -1); | ||
233 | } | ||
234 | |||
235 | static inline void bpf_stmt(struct sock_filter *inss, unsigned int *i, | ||
236 | unsigned short code, unsigned int data) | ||
237 | { | ||
238 | struct sock_filter *ins = &inss[*i]; | ||
239 | |||
240 | ins->code = code; | ||
241 | ins->k = data; | ||
242 | (*i)++; | ||
243 | } | ||
244 | |||
245 | static inline void bpf_jmp(struct sock_filter *inss, unsigned int *i, | ||
246 | unsigned short code, unsigned int data, | ||
247 | unsigned short jt, unsigned short jf) | ||
248 | { | ||
249 | struct sock_filter *ins = &inss[*i]; | ||
250 | |||
251 | ins->code = code; | ||
252 | ins->jt = jt; | ||
253 | ins->jf = jf; | ||
254 | ins->k = data; | ||
255 | (*i)++; | ||
256 | } | ||
257 | |||
258 | /** | ||
259 | * udev_monitor_filter_update: | ||
260 | * @udev_monitor: monitor | ||
261 | * | ||
262 | * Update the installed socket filter. This is only needed, | ||
263 | * if the filter was removed or changed. | ||
264 | * | ||
265 | * Returns: 0 on success, otherwise a negative error value. | ||
266 | */ | ||
267 | UDEV_EXPORT int udev_monitor_filter_update(struct udev_monitor *udev_monitor) | ||
268 | { | ||
269 | struct sock_filter ins[512]; | ||
270 | struct sock_fprog filter; | ||
271 | unsigned int i; | ||
272 | struct udev_list_entry *list_entry; | ||
273 | int err; | ||
274 | |||
275 | if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL && | ||
276 | udev_list_get_entry(&udev_monitor->filter_tag_list) == NULL) | ||
277 | return 0; | ||
278 | |||
279 | memset(ins, 0x00, sizeof(ins)); | ||
280 | i = 0; | ||
281 | |||
282 | /* load magic in A */ | ||
283 | bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, magic)); | ||
284 | /* jump if magic matches */ | ||
285 | bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, UDEV_MONITOR_MAGIC, 1, 0); | ||
286 | /* wrong magic, pass packet */ | ||
287 | bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff); | ||
288 | |||
289 | if (udev_list_get_entry(&udev_monitor->filter_tag_list) != NULL) { | ||
290 | int tag_matches; | ||
291 | |||
292 | /* count tag matches, to calculate end of tag match block */ | ||
293 | tag_matches = 0; | ||
294 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) | ||
295 | tag_matches++; | ||
296 | |||
297 | /* add all tags matches */ | ||
298 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) { | ||
299 | uint64_t tag_bloom_bits = util_string_bloom64(udev_list_entry_get_name(list_entry)); | ||
300 | uint32_t tag_bloom_hi = tag_bloom_bits >> 32; | ||
301 | uint32_t tag_bloom_lo = tag_bloom_bits & 0xffffffff; | ||
302 | |||
303 | /* load device bloom bits in A */ | ||
304 | bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_tag_bloom_hi)); | ||
305 | /* clear bits (tag bits & bloom bits) */ | ||
306 | bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_hi); | ||
307 | /* jump to next tag if it does not match */ | ||
308 | bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_hi, 0, 3); | ||
309 | |||
310 | /* load device bloom bits in A */ | ||
311 | bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_tag_bloom_lo)); | ||
312 | /* clear bits (tag bits & bloom bits) */ | ||
313 | bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_lo); | ||
314 | /* jump behind end of tag match block if tag matches */ | ||
315 | tag_matches--; | ||
316 | bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_lo, 1 + (tag_matches * 6), 0); | ||
317 | } | ||
318 | |||
319 | /* nothing matched, drop packet */ | ||
320 | bpf_stmt(ins, &i, BPF_RET|BPF_K, 0); | ||
321 | } | ||
322 | |||
323 | /* add all subsystem matches */ | ||
324 | if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) != NULL) { | ||
325 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) { | ||
326 | unsigned int hash = util_string_hash32(udev_list_entry_get_name(list_entry)); | ||
327 | |||
328 | /* load device subsystem value in A */ | ||
329 | bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_subsystem_hash)); | ||
330 | if (udev_list_entry_get_value(list_entry) == NULL) { | ||
331 | /* jump if subsystem does not match */ | ||
332 | bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1); | ||
333 | } else { | ||
334 | /* jump if subsystem does not match */ | ||
335 | bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 3); | ||
336 | |||
337 | /* load device devtype value in A */ | ||
338 | bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_devtype_hash)); | ||
339 | /* jump if value does not match */ | ||
340 | hash = util_string_hash32(udev_list_entry_get_value(list_entry)); | ||
341 | bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1); | ||
342 | } | ||
343 | |||
344 | /* matched, pass packet */ | ||
345 | bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff); | ||
346 | |||
347 | if (i+1 >= ARRAY_SIZE(ins)) | ||
348 | return -1; | ||
349 | } | ||
350 | |||
351 | /* nothing matched, drop packet */ | ||
352 | bpf_stmt(ins, &i, BPF_RET|BPF_K, 0); | ||
353 | } | ||
354 | |||
355 | /* matched, pass packet */ | ||
356 | bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff); | ||
357 | |||
358 | /* install filter */ | ||
359 | memset(&filter, 0x00, sizeof(filter)); | ||
360 | filter.len = i; | ||
361 | filter.filter = ins; | ||
362 | err = setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)); | ||
363 | return err; | ||
364 | } | ||
365 | |||
366 | int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender) | ||
367 | { | ||
368 | udev_monitor->snl_trusted_sender.nl_pid = sender->snl.nl_pid; | ||
369 | return 0; | ||
370 | } | ||
371 | /** | ||
372 | * udev_monitor_enable_receiving: | ||
373 | * @udev_monitor: the monitor which should receive events | ||
374 | * | ||
375 | * Binds the @udev_monitor socket to the event source. | ||
376 | * | ||
377 | * Returns: 0 on success, otherwise a negative error value. | ||
378 | */ | ||
379 | UDEV_EXPORT int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) | ||
380 | { | ||
381 | int err = 0; | ||
382 | const int on = 1; | ||
383 | |||
384 | if (udev_monitor->sun.sun_family != 0) { | ||
385 | if (!udev_monitor->bound) { | ||
386 | err = bind(udev_monitor->sock, | ||
387 | (struct sockaddr *)&udev_monitor->sun, udev_monitor->addrlen); | ||
388 | if (err == 0) | ||
389 | udev_monitor->bound = true; | ||
390 | } | ||
391 | } else if (udev_monitor->snl.nl_family != 0) { | ||
392 | udev_monitor_filter_update(udev_monitor); | ||
393 | if (!udev_monitor->bound) { | ||
394 | err = bind(udev_monitor->sock, | ||
395 | (struct sockaddr *)&udev_monitor->snl, sizeof(struct sockaddr_nl)); | ||
396 | if (err == 0) | ||
397 | udev_monitor->bound = true; | ||
398 | } | ||
399 | if (err == 0) { | ||
400 | struct sockaddr_nl snl; | ||
401 | socklen_t addrlen; | ||
402 | |||
403 | /* | ||
404 | * get the address the kernel has assigned us | ||
405 | * it is usually, but not necessarily the pid | ||
406 | */ | ||
407 | addrlen = sizeof(struct sockaddr_nl); | ||
408 | err = getsockname(udev_monitor->sock, (struct sockaddr *)&snl, &addrlen); | ||
409 | if (err == 0) | ||
410 | udev_monitor->snl.nl_pid = snl.nl_pid; | ||
411 | } | ||
412 | } else { | ||
413 | return -EINVAL; | ||
414 | } | ||
415 | |||
416 | if (err < 0) { | ||
417 | err(udev_monitor->udev, "bind failed: %m\n"); | ||
418 | return err; | ||
419 | } | ||
420 | |||
421 | /* enable receiving of sender credentials */ | ||
422 | setsockopt(udev_monitor->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); | ||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | /** | ||
427 | * udev_monitor_set_receive_buffer_size: | ||
428 | * @udev_monitor: the monitor which should receive events | ||
429 | * @size: the size in bytes | ||
430 | * | ||
431 | * Set the size of the kernel socket buffer. This call needs the | ||
432 | * appropriate privileges to succeed. | ||
433 | * | ||
434 | * Returns: 0 on success, otherwise -1 on error. | ||
435 | */ | ||
436 | UDEV_EXPORT int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size) | ||
437 | { | ||
438 | if (udev_monitor == NULL) | ||
439 | return -1; | ||
440 | return setsockopt(udev_monitor->sock, SOL_SOCKET, SO_RCVBUFFORCE, &size, sizeof(size)); | ||
441 | } | ||
442 | |||
443 | int udev_monitor_disconnect(struct udev_monitor *udev_monitor) | ||
444 | { | ||
445 | int err; | ||
446 | |||
447 | err = close(udev_monitor->sock); | ||
448 | udev_monitor->sock = -1; | ||
449 | return err; | ||
450 | } | ||
451 | |||
452 | /** | ||
453 | * udev_monitor_ref: | ||
454 | * @udev_monitor: udev monitor | ||
455 | * | ||
456 | * Take a reference of a udev monitor. | ||
457 | * | ||
458 | * Returns: the passed udev monitor | ||
459 | **/ | ||
460 | UDEV_EXPORT struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor) | ||
461 | { | ||
462 | if (udev_monitor == NULL) | ||
463 | return NULL; | ||
464 | udev_monitor->refcount++; | ||
465 | return udev_monitor; | ||
466 | } | ||
467 | |||
468 | /** | ||
469 | * udev_monitor_unref: | ||
470 | * @udev_monitor: udev monitor | ||
471 | * | ||
472 | * Drop a reference of a udev monitor. If the refcount reaches zero, | ||
473 | * the bound socket will be closed, and the resources of the monitor | ||
474 | * will be released. | ||
475 | * | ||
476 | **/ | ||
477 | UDEV_EXPORT struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor) | ||
478 | { | ||
479 | if (udev_monitor == NULL) | ||
480 | return NULL; | ||
481 | udev_monitor->refcount--; | ||
482 | if (udev_monitor->refcount > 0) | ||
483 | return udev_monitor; | ||
484 | if (udev_monitor->sock >= 0) | ||
485 | close(udev_monitor->sock); | ||
486 | udev_list_cleanup(&udev_monitor->filter_subsystem_list); | ||
487 | udev_list_cleanup(&udev_monitor->filter_tag_list); | ||
488 | dbg(udev_monitor->udev, "monitor %p released\n", udev_monitor); | ||
489 | free(udev_monitor); | ||
490 | return NULL; | ||
491 | } | ||
492 | |||
493 | /** | ||
494 | * udev_monitor_get_udev: | ||
495 | * @udev_monitor: udev monitor | ||
496 | * | ||
497 | * Retrieve the udev library context the monitor was created with. | ||
498 | * | ||
499 | * Returns: the udev library context | ||
500 | **/ | ||
501 | UDEV_EXPORT struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor) | ||
502 | { | ||
503 | if (udev_monitor == NULL) | ||
504 | return NULL; | ||
505 | return udev_monitor->udev; | ||
506 | } | ||
507 | |||
508 | /** | ||
509 | * udev_monitor_get_fd: | ||
510 | * @udev_monitor: udev monitor | ||
511 | * | ||
512 | * Retrieve the socket file descriptor associated with the monitor. | ||
513 | * | ||
514 | * Returns: the socket file descriptor | ||
515 | **/ | ||
516 | UDEV_EXPORT int udev_monitor_get_fd(struct udev_monitor *udev_monitor) | ||
517 | { | ||
518 | if (udev_monitor == NULL) | ||
519 | return -1; | ||
520 | return udev_monitor->sock; | ||
521 | } | ||
522 | |||
523 | static int passes_filter(struct udev_monitor *udev_monitor, struct udev_device *udev_device) | ||
524 | { | ||
525 | struct udev_list_entry *list_entry; | ||
526 | |||
527 | if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL) | ||
528 | goto tag; | ||
529 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) { | ||
530 | const char *subsys = udev_list_entry_get_name(list_entry); | ||
531 | const char *dsubsys = udev_device_get_subsystem(udev_device); | ||
532 | const char *devtype; | ||
533 | const char *ddevtype; | ||
534 | |||
535 | if (strcmp(dsubsys, subsys) != 0) | ||
536 | continue; | ||
537 | |||
538 | devtype = udev_list_entry_get_value(list_entry); | ||
539 | if (devtype == NULL) | ||
540 | goto tag; | ||
541 | ddevtype = udev_device_get_devtype(udev_device); | ||
542 | if (ddevtype == NULL) | ||
543 | continue; | ||
544 | if (strcmp(ddevtype, devtype) == 0) | ||
545 | goto tag; | ||
546 | } | ||
547 | return 0; | ||
548 | |||
549 | tag: | ||
550 | if (udev_list_get_entry(&udev_monitor->filter_tag_list) == NULL) | ||
551 | return 1; | ||
552 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) { | ||
553 | const char *tag = udev_list_entry_get_name(list_entry); | ||
554 | |||
555 | if (udev_device_has_tag(udev_device, tag)) | ||
556 | return 1; | ||
557 | } | ||
558 | return 0; | ||
559 | } | ||
560 | |||
561 | /** | ||
562 | * udev_monitor_receive_device: | ||
563 | * @udev_monitor: udev monitor | ||
564 | * | ||
565 | * Receive data from the udev monitor socket, allocate a new udev | ||
566 | * device, fill in the received data, and return the device. | ||
567 | * | ||
568 | * Only socket connections with uid=0 are accepted. | ||
569 | * | ||
570 | * The monitor socket is by default set to NONBLOCK. A variant of poll() on | ||
571 | * the file descriptor returned by udev_monitor_get_fd() should to be used to | ||
572 | * wake up when new devices arrive, or alternatively the file descriptor | ||
573 | * switched into blocking mode. | ||
574 | * | ||
575 | * The initial refcount is 1, and needs to be decremented to | ||
576 | * release the resources of the udev device. | ||
577 | * | ||
578 | * Returns: a new udev device, or #NULL, in case of an error | ||
579 | **/ | ||
580 | UDEV_EXPORT struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor) | ||
581 | { | ||
582 | struct udev_device *udev_device; | ||
583 | struct msghdr smsg; | ||
584 | struct iovec iov; | ||
585 | char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; | ||
586 | struct cmsghdr *cmsg; | ||
587 | struct sockaddr_nl snl; | ||
588 | struct ucred *cred; | ||
589 | char buf[8192]; | ||
590 | ssize_t buflen; | ||
591 | ssize_t bufpos; | ||
592 | struct udev_monitor_netlink_header *nlh; | ||
593 | |||
594 | retry: | ||
595 | if (udev_monitor == NULL) | ||
596 | return NULL; | ||
597 | iov.iov_base = &buf; | ||
598 | iov.iov_len = sizeof(buf); | ||
599 | memset (&smsg, 0x00, sizeof(struct msghdr)); | ||
600 | smsg.msg_iov = &iov; | ||
601 | smsg.msg_iovlen = 1; | ||
602 | smsg.msg_control = cred_msg; | ||
603 | smsg.msg_controllen = sizeof(cred_msg); | ||
604 | |||
605 | if (udev_monitor->snl.nl_family != 0) { | ||
606 | smsg.msg_name = &snl; | ||
607 | smsg.msg_namelen = sizeof(snl); | ||
608 | } | ||
609 | |||
610 | buflen = recvmsg(udev_monitor->sock, &smsg, 0); | ||
611 | if (buflen < 0) { | ||
612 | if (errno != EINTR) | ||
613 | info(udev_monitor->udev, "unable to receive message\n"); | ||
614 | return NULL; | ||
615 | } | ||
616 | |||
617 | if (buflen < 32 || (size_t)buflen >= sizeof(buf)) { | ||
618 | info(udev_monitor->udev, "invalid message length\n"); | ||
619 | return NULL; | ||
620 | } | ||
621 | |||
622 | if (udev_monitor->snl.nl_family != 0) { | ||
623 | if (snl.nl_groups == 0) { | ||
624 | /* unicast message, check if we trust the sender */ | ||
625 | if (udev_monitor->snl_trusted_sender.nl_pid == 0 || | ||
626 | snl.nl_pid != udev_monitor->snl_trusted_sender.nl_pid) { | ||
627 | info(udev_monitor->udev, "unicast netlink message ignored\n"); | ||
628 | return NULL; | ||
629 | } | ||
630 | } else if (snl.nl_groups == UDEV_MONITOR_KERNEL) { | ||
631 | if (snl.nl_pid > 0) { | ||
632 | info(udev_monitor->udev, "multicast kernel netlink message from pid %d ignored\n", | ||
633 | snl.nl_pid); | ||
634 | return NULL; | ||
635 | } | ||
636 | } | ||
637 | } | ||
638 | |||
639 | cmsg = CMSG_FIRSTHDR(&smsg); | ||
640 | if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { | ||
641 | info(udev_monitor->udev, "no sender credentials received, message ignored\n"); | ||
642 | return NULL; | ||
643 | } | ||
644 | |||
645 | cred = (struct ucred *)CMSG_DATA(cmsg); | ||
646 | if (cred->uid != 0) { | ||
647 | info(udev_monitor->udev, "sender uid=%d, message ignored\n", cred->uid); | ||
648 | return NULL; | ||
649 | } | ||
650 | |||
651 | if (memcmp(buf, "libudev", 8) == 0) { | ||
652 | /* udev message needs proper version magic */ | ||
653 | nlh = (struct udev_monitor_netlink_header *) buf; | ||
654 | if (nlh->magic != htonl(UDEV_MONITOR_MAGIC)) { | ||
655 | err(udev_monitor->udev, "unrecognized message signature (%x != %x)\n", | ||
656 | nlh->magic, htonl(UDEV_MONITOR_MAGIC)); | ||
657 | return NULL; | ||
658 | } | ||
659 | if (nlh->properties_off+32 > buflen) | ||
660 | return NULL; | ||
661 | bufpos = nlh->properties_off; | ||
662 | } else { | ||
663 | /* kernel message with header */ | ||
664 | bufpos = strlen(buf) + 1; | ||
665 | if ((size_t)bufpos < sizeof("a@/d") || bufpos >= buflen) { | ||
666 | info(udev_monitor->udev, "invalid message length\n"); | ||
667 | return NULL; | ||
668 | } | ||
669 | |||
670 | /* check message header */ | ||
671 | if (strstr(buf, "@/") == NULL) { | ||
672 | info(udev_monitor->udev, "unrecognized message header\n"); | ||
673 | return NULL; | ||
674 | } | ||
675 | } | ||
676 | |||
677 | udev_device = udev_device_new(udev_monitor->udev); | ||
678 | if (udev_device == NULL) | ||
679 | return NULL; | ||
680 | udev_device_set_info_loaded(udev_device); | ||
681 | |||
682 | while (bufpos < buflen) { | ||
683 | char *key; | ||
684 | size_t keylen; | ||
685 | |||
686 | key = &buf[bufpos]; | ||
687 | keylen = strlen(key); | ||
688 | if (keylen == 0) | ||
689 | break; | ||
690 | bufpos += keylen + 1; | ||
691 | udev_device_add_property_from_string_parse(udev_device, key); | ||
692 | } | ||
693 | |||
694 | if (udev_device_add_property_from_string_parse_finish(udev_device) < 0) { | ||
695 | info(udev_monitor->udev, "missing values, invalid device\n"); | ||
696 | udev_device_unref(udev_device); | ||
697 | return NULL; | ||
698 | } | ||
699 | |||
700 | /* skip device, if it does not pass the current filter */ | ||
701 | if (!passes_filter(udev_monitor, udev_device)) { | ||
702 | struct pollfd pfd[1]; | ||
703 | int rc; | ||
704 | |||
705 | udev_device_unref(udev_device); | ||
706 | |||
707 | /* if something is queued, get next device */ | ||
708 | pfd[0].fd = udev_monitor->sock; | ||
709 | pfd[0].events = POLLIN; | ||
710 | rc = poll(pfd, 1, 0); | ||
711 | if (rc > 0) | ||
712 | goto retry; | ||
713 | return NULL; | ||
714 | } | ||
715 | |||
716 | return udev_device; | ||
717 | } | ||
718 | |||
719 | int udev_monitor_send_device(struct udev_monitor *udev_monitor, | ||
720 | struct udev_monitor *destination, struct udev_device *udev_device) | ||
721 | { | ||
722 | const char *buf; | ||
723 | ssize_t blen; | ||
724 | ssize_t count; | ||
725 | |||
726 | blen = udev_device_get_properties_monitor_buf(udev_device, &buf); | ||
727 | if (blen < 32) | ||
728 | return -EINVAL; | ||
729 | |||
730 | if (udev_monitor->sun.sun_family != 0) { | ||
731 | struct msghdr smsg; | ||
732 | struct iovec iov[2]; | ||
733 | const char *action; | ||
734 | char header[2048]; | ||
735 | char *s; | ||
736 | |||
737 | /* header <action>@<devpath> */ | ||
738 | action = udev_device_get_action(udev_device); | ||
739 | if (action == NULL) | ||
740 | return -EINVAL; | ||
741 | s = header; | ||
742 | if (util_strpcpyl(&s, sizeof(header), action, "@", udev_device_get_devpath(udev_device), NULL) == 0) | ||
743 | return -EINVAL; | ||
744 | iov[0].iov_base = header; | ||
745 | iov[0].iov_len = (s - header)+1; | ||
746 | |||
747 | /* add properties list */ | ||
748 | iov[1].iov_base = (char *)buf; | ||
749 | iov[1].iov_len = blen; | ||
750 | |||
751 | memset(&smsg, 0x00, sizeof(struct msghdr)); | ||
752 | smsg.msg_iov = iov; | ||
753 | smsg.msg_iovlen = 2; | ||
754 | smsg.msg_name = &udev_monitor->sun; | ||
755 | smsg.msg_namelen = udev_monitor->addrlen; | ||
756 | count = sendmsg(udev_monitor->sock, &smsg, 0); | ||
757 | info(udev_monitor->udev, "passed %zi bytes to socket monitor %p\n", count, udev_monitor); | ||
758 | return count; | ||
759 | } | ||
760 | |||
761 | if (udev_monitor->snl.nl_family != 0) { | ||
762 | struct msghdr smsg; | ||
763 | struct iovec iov[2]; | ||
764 | const char *val; | ||
765 | struct udev_monitor_netlink_header nlh; | ||
766 | struct udev_list_entry *list_entry; | ||
767 | uint64_t tag_bloom_bits; | ||
768 | |||
769 | /* add versioned header */ | ||
770 | memset(&nlh, 0x00, sizeof(struct udev_monitor_netlink_header)); | ||
771 | memcpy(nlh.prefix, "libudev", 8); | ||
772 | nlh.magic = htonl(UDEV_MONITOR_MAGIC); | ||
773 | nlh.header_size = sizeof(struct udev_monitor_netlink_header); | ||
774 | val = udev_device_get_subsystem(udev_device); | ||
775 | nlh.filter_subsystem_hash = htonl(util_string_hash32(val)); | ||
776 | val = udev_device_get_devtype(udev_device); | ||
777 | if (val != NULL) | ||
778 | nlh.filter_devtype_hash = htonl(util_string_hash32(val)); | ||
779 | iov[0].iov_base = &nlh; | ||
780 | iov[0].iov_len = sizeof(struct udev_monitor_netlink_header); | ||
781 | |||
782 | /* add tag bloom filter */ | ||
783 | tag_bloom_bits = 0; | ||
784 | udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device)) | ||
785 | tag_bloom_bits |= util_string_bloom64(udev_list_entry_get_name(list_entry)); | ||
786 | if (tag_bloom_bits > 0) { | ||
787 | nlh.filter_tag_bloom_hi = htonl(tag_bloom_bits >> 32); | ||
788 | nlh.filter_tag_bloom_lo = htonl(tag_bloom_bits & 0xffffffff); | ||
789 | } | ||
790 | |||
791 | /* add properties list */ | ||
792 | nlh.properties_off = iov[0].iov_len; | ||
793 | nlh.properties_len = blen; | ||
794 | iov[1].iov_base = (char *)buf; | ||
795 | iov[1].iov_len = blen; | ||
796 | |||
797 | memset(&smsg, 0x00, sizeof(struct msghdr)); | ||
798 | smsg.msg_iov = iov; | ||
799 | smsg.msg_iovlen = 2; | ||
800 | /* | ||
801 | * Use custom address for target, or the default one. | ||
802 | * | ||
803 | * If we send to a multicast group, we will get | ||
804 | * ECONNREFUSED, which is expected. | ||
805 | */ | ||
806 | if (destination != NULL) | ||
807 | smsg.msg_name = &destination->snl; | ||
808 | else | ||
809 | smsg.msg_name = &udev_monitor->snl_destination; | ||
810 | smsg.msg_namelen = sizeof(struct sockaddr_nl); | ||
811 | count = sendmsg(udev_monitor->sock, &smsg, 0); | ||
812 | info(udev_monitor->udev, "passed %zi bytes to netlink monitor %p\n", count, udev_monitor); | ||
813 | return count; | ||
814 | } | ||
815 | |||
816 | return -EINVAL; | ||
817 | } | ||
818 | |||
819 | /** | ||
820 | * udev_monitor_filter_add_match_subsystem_devtype: | ||
821 | * @udev_monitor: the monitor | ||
822 | * @subsystem: the subsystem value to match the incoming devices against | ||
823 | * @devtype: the devtype value to match the incoming devices against | ||
824 | * | ||
825 | * This filter is efficiently executed inside the kernel, and libudev subscribers | ||
826 | * will usually not be woken up for devices which do not match. | ||
827 | * | ||
828 | * The filter must be installed before the monitor is switched to listening mode. | ||
829 | * | ||
830 | * Returns: 0 on success, otherwise a negative error value. | ||
831 | */ | ||
832 | UDEV_EXPORT int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype) | ||
833 | { | ||
834 | if (udev_monitor == NULL) | ||
835 | return -EINVAL; | ||
836 | if (subsystem == NULL) | ||
837 | return -EINVAL; | ||
838 | if (udev_list_entry_add(&udev_monitor->filter_subsystem_list, subsystem, devtype) == NULL) | ||
839 | return -ENOMEM; | ||
840 | return 0; | ||
841 | } | ||
842 | |||
843 | /** | ||
844 | * udev_monitor_filter_add_match_tag: | ||
845 | * @udev_monitor: the monitor | ||
846 | * @tag: the name of a tag | ||
847 | * | ||
848 | * This filter is efficiently executed inside the kernel, and libudev subscribers | ||
849 | * will usually not be woken up for devices which do not match. | ||
850 | * | ||
851 | * The filter must be installed before the monitor is switched to listening mode. | ||
852 | * | ||
853 | * Returns: 0 on success, otherwise a negative error value. | ||
854 | */ | ||
855 | UDEV_EXPORT int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag) | ||
856 | { | ||
857 | if (udev_monitor == NULL) | ||
858 | return -EINVAL; | ||
859 | if (tag == NULL) | ||
860 | return -EINVAL; | ||
861 | if (udev_list_entry_add(&udev_monitor->filter_tag_list, tag, NULL) == NULL) | ||
862 | return -ENOMEM; | ||
863 | return 0; | ||
864 | } | ||
865 | |||
866 | /** | ||
867 | * udev_monitor_filter_remove: | ||
868 | * @udev_monitor: monitor | ||
869 | * | ||
870 | * Remove all filters from monitor. | ||
871 | * | ||
872 | * Returns: 0 on success, otherwise a negative error value. | ||
873 | */ | ||
874 | UDEV_EXPORT int udev_monitor_filter_remove(struct udev_monitor *udev_monitor) | ||
875 | { | ||
876 | static struct sock_fprog filter = { 0, NULL }; | ||
877 | |||
878 | udev_list_cleanup(&udev_monitor->filter_subsystem_list); | ||
879 | return setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)); | ||
880 | } |
File src/libudev-private.h added (mode: 100644) (index 0000000..4b95ac2) | |||
1 | /* | ||
2 | * libudev - interface to udev device information | ||
3 | * | ||
4 | * Copyright (C) 2008-2010 Kay Sievers <kay.sievers@vrfy.org> | ||
5 | * | ||
6 | * This library is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU Lesser General Public | ||
8 | * License as published by the Free Software Foundation; either | ||
9 | * version 2.1 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #ifndef _LIBUDEV_PRIVATE_H_ | ||
13 | #define _LIBUDEV_PRIVATE_H_ | ||
14 | |||
15 | #include <syslog.h> | ||
16 | #include <signal.h> | ||
17 | #include <stdint.h> | ||
18 | #include <stdbool.h> | ||
19 | #include "libudev.h" | ||
20 | |||
21 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) | ||
22 | #define READ_END 0 | ||
23 | #define WRITE_END 1 | ||
24 | |||
25 | static inline void __attribute__((always_inline, format(printf, 2, 3))) | ||
26 | udev_log_null(struct udev *udev, const char *format, ...) {} | ||
27 | |||
28 | #define udev_log_cond(udev, prio, arg...) \ | ||
29 | do { \ | ||
30 | if (udev_get_log_priority(udev) >= prio) \ | ||
31 | udev_log(udev, prio, __FILE__, __LINE__, __FUNCTION__, ## arg); \ | ||
32 | } while (0) | ||
33 | |||
34 | #ifdef ENABLE_LOGGING | ||
35 | # ifdef ENABLE_DEBUG | ||
36 | # define dbg(udev, arg...) udev_log_cond(udev, LOG_DEBUG, ## arg) | ||
37 | # else | ||
38 | # define dbg(udev, arg...) udev_log_null(udev, ## arg) | ||
39 | # endif | ||
40 | # define info(udev, arg...) udev_log_cond(udev, LOG_INFO, ## arg) | ||
41 | # define err(udev, arg...) udev_log_cond(udev, LOG_ERR, ## arg) | ||
42 | #else | ||
43 | # define dbg(udev, arg...) udev_log_null(udev, ## arg) | ||
44 | # define info(udev, arg...) udev_log_null(udev, ## arg) | ||
45 | # define err(udev, arg...) udev_log_null(udev, ## arg) | ||
46 | #endif | ||
47 | |||
48 | #define UDEV_EXPORT __attribute__ ((visibility("default"))) | ||
49 | |||
50 | static inline void udev_log_init(const char *program_name) | ||
51 | { | ||
52 | openlog(program_name, LOG_PID | LOG_CONS, LOG_DAEMON); | ||
53 | } | ||
54 | |||
55 | static inline void udev_log_close(void) | ||
56 | { | ||
57 | closelog(); | ||
58 | } | ||
59 | |||
60 | /* libudev.c */ | ||
61 | void udev_log(struct udev *udev, | ||
62 | int priority, const char *file, int line, const char *fn, | ||
63 | const char *format, ...) | ||
64 | __attribute__((format(printf, 6, 7))); | ||
65 | int udev_get_rules_path(struct udev *udev, char **path[], unsigned long long *ts_usec[]); | ||
66 | struct udev_list_entry *udev_add_property(struct udev *udev, const char *key, const char *value); | ||
67 | struct udev_list_entry *udev_get_properties_list_entry(struct udev *udev); | ||
68 | |||
69 | /* libudev-device.c */ | ||
70 | struct udev_device *udev_device_new(struct udev *udev); | ||
71 | mode_t udev_device_get_devnode_mode(struct udev_device *udev_device); | ||
72 | int udev_device_set_syspath(struct udev_device *udev_device, const char *syspath); | ||
73 | int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode); | ||
74 | int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink, int unique); | ||
75 | void udev_device_cleanup_devlinks_list(struct udev_device *udev_device); | ||
76 | struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value); | ||
77 | void udev_device_add_property_from_string_parse(struct udev_device *udev_device, const char *property); | ||
78 | int udev_device_add_property_from_string_parse_finish(struct udev_device *udev_device); | ||
79 | char **udev_device_get_properties_envp(struct udev_device *udev_device); | ||
80 | ssize_t udev_device_get_properties_monitor_buf(struct udev_device *udev_device, const char **buf); | ||
81 | int udev_device_read_db(struct udev_device *udev_device, const char *dbfile); | ||
82 | int udev_device_read_uevent_file(struct udev_device *udev_device); | ||
83 | int udev_device_set_action(struct udev_device *udev_device, const char *action); | ||
84 | const char *udev_device_get_devpath_old(struct udev_device *udev_device); | ||
85 | const char *udev_device_get_id_filename(struct udev_device *udev_device); | ||
86 | void udev_device_set_is_initialized(struct udev_device *udev_device); | ||
87 | int udev_device_add_tag(struct udev_device *udev_device, const char *tag); | ||
88 | void udev_device_cleanup_tags_list(struct udev_device *udev_device); | ||
89 | unsigned long long udev_device_get_usec_initialized(struct udev_device *udev_device); | ||
90 | void udev_device_set_usec_initialized(struct udev_device *udev_device, unsigned long long usec_initialized); | ||
91 | int udev_device_get_devlink_priority(struct udev_device *udev_device); | ||
92 | int udev_device_set_devlink_priority(struct udev_device *udev_device, int prio); | ||
93 | int udev_device_get_watch_handle(struct udev_device *udev_device); | ||
94 | int udev_device_set_watch_handle(struct udev_device *udev_device, int handle); | ||
95 | int udev_device_get_ifindex(struct udev_device *udev_device); | ||
96 | void udev_device_set_info_loaded(struct udev_device *device); | ||
97 | bool udev_device_get_db_persist(struct udev_device *udev_device); | ||
98 | void udev_device_set_db_persist(struct udev_device *udev_device); | ||
99 | |||
100 | /* libudev-device-private.c */ | ||
101 | int udev_device_update_db(struct udev_device *udev_device); | ||
102 | int udev_device_delete_db(struct udev_device *udev_device); | ||
103 | int udev_device_tag_index(struct udev_device *dev, struct udev_device *dev_old, bool add); | ||
104 | |||
105 | /* libudev-monitor.c - netlink/unix socket communication */ | ||
106 | int udev_monitor_disconnect(struct udev_monitor *udev_monitor); | ||
107 | int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender); | ||
108 | int udev_monitor_send_device(struct udev_monitor *udev_monitor, | ||
109 | struct udev_monitor *destination, struct udev_device *udev_device); | ||
110 | struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd); | ||
111 | |||
112 | /* libudev-list.c */ | ||
113 | struct udev_list_node { | ||
114 | struct udev_list_node *next, *prev; | ||
115 | }; | ||
116 | struct udev_list { | ||
117 | struct udev *udev; | ||
118 | struct udev_list_node node; | ||
119 | struct udev_list_entry **entries; | ||
120 | unsigned int entries_cur; | ||
121 | unsigned int entries_max; | ||
122 | bool unique; | ||
123 | }; | ||
124 | #define UDEV_LIST(list) struct udev_list_node list = { &(list), &(list) } | ||
125 | void udev_list_node_init(struct udev_list_node *list); | ||
126 | int udev_list_node_is_empty(struct udev_list_node *list); | ||
127 | void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list); | ||
128 | void udev_list_node_remove(struct udev_list_node *entry); | ||
129 | #define udev_list_node_foreach(node, list) \ | ||
130 | for (node = (list)->next; \ | ||
131 | node != list; \ | ||
132 | node = (node)->next) | ||
133 | #define udev_list_node_foreach_safe(node, tmp, list) \ | ||
134 | for (node = (list)->next, tmp = (node)->next; \ | ||
135 | node != list; \ | ||
136 | node = tmp, tmp = (tmp)->next) | ||
137 | void udev_list_init(struct udev *udev, struct udev_list *list, bool unique); | ||
138 | void udev_list_cleanup(struct udev_list *list); | ||
139 | struct udev_list_entry *udev_list_get_entry(struct udev_list *list); | ||
140 | struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *name, const char *value); | ||
141 | void udev_list_entry_delete(struct udev_list_entry *entry); | ||
142 | void udev_list_entry_insert_before(struct udev_list_entry *new, struct udev_list_entry *entry); | ||
143 | void udev_list_entry_append(struct udev_list_entry *new, struct udev_list *list); | ||
144 | int udev_list_entry_get_num(struct udev_list_entry *list_entry); | ||
145 | void udev_list_entry_set_num(struct udev_list_entry *list_entry, int num); | ||
146 | #define udev_list_entry_foreach_safe(entry, tmp, first) \ | ||
147 | for (entry = first, tmp = udev_list_entry_get_next(entry); \ | ||
148 | entry != NULL; \ | ||
149 | entry = tmp, tmp = udev_list_entry_get_next(tmp)) | ||
150 | |||
151 | /* libudev-queue.c */ | ||
152 | unsigned long long int udev_get_kernel_seqnum(struct udev *udev); | ||
153 | int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum); | ||
154 | ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size); | ||
155 | ssize_t udev_queue_skip_devpath(FILE *queue_file); | ||
156 | |||
157 | /* libudev-queue-private.c */ | ||
158 | struct udev_queue_export *udev_queue_export_new(struct udev *udev); | ||
159 | struct udev_queue_export *udev_queue_export_unref(struct udev_queue_export *udev_queue_export); | ||
160 | void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export); | ||
161 | int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device); | ||
162 | int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device); | ||
163 | |||
164 | /* libudev-util.c */ | ||
165 | #define UTIL_PATH_SIZE 1024 | ||
166 | #define UTIL_NAME_SIZE 512 | ||
167 | #define UTIL_LINE_SIZE 16384 | ||
168 | #define UDEV_ALLOWED_CHARS_INPUT "/ $%?," | ||
169 | ssize_t util_get_sys_core_link_value(struct udev *udev, const char *slink, const char *syspath, char *value, size_t size); | ||
170 | int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size); | ||
171 | int util_log_priority(const char *priority); | ||
172 | size_t util_path_encode(const char *src, char *dest, size_t size); | ||
173 | size_t util_path_decode(char *s); | ||
174 | void util_remove_trailing_chars(char *path, char c); | ||
175 | size_t util_strpcpy(char **dest, size_t size, const char *src); | ||
176 | size_t util_strpcpyl(char **dest, size_t size, const char *src, ...) __attribute__((sentinel)); | ||
177 | size_t util_strscpy(char *dest, size_t size, const char *src); | ||
178 | size_t util_strscpyl(char *dest, size_t size, const char *src, ...) __attribute__((sentinel)); | ||
179 | int util_replace_whitespace(const char *str, char *to, size_t len); | ||
180 | int util_replace_chars(char *str, const char *white); | ||
181 | unsigned int util_string_hash32(const char *key); | ||
182 | uint64_t util_string_bloom64(const char *str); | ||
183 | |||
184 | /* libudev-util-private.c */ | ||
185 | int util_create_path(struct udev *udev, const char *path); | ||
186 | int util_create_path_selinux(struct udev *udev, const char *path); | ||
187 | int util_delete_path(struct udev *udev, const char *path); | ||
188 | uid_t util_lookup_user(struct udev *udev, const char *user); | ||
189 | gid_t util_lookup_group(struct udev *udev, const char *group); | ||
190 | int util_resolve_subsys_kernel(struct udev *udev, const char *string, | ||
191 | char *result, size_t maxsize, int read_value); | ||
192 | unsigned long long ts_usec(const struct timespec *ts); | ||
193 | unsigned long long now_usec(void); | ||
194 | ssize_t print_kmsg(const char *fmt, ...) __attribute__((format(printf, 1, 2))); | ||
195 | |||
196 | /* libudev-selinux-private.c */ | ||
197 | #ifndef WITH_SELINUX | ||
198 | static inline void udev_selinux_init(struct udev *udev) {} | ||
199 | static inline void udev_selinux_exit(struct udev *udev) {} | ||
200 | static inline void udev_selinux_lsetfilecon(struct udev *udev, const char *file, unsigned int mode) {} | ||
201 | static inline void udev_selinux_setfscreatecon(struct udev *udev, const char *file, unsigned int mode) {} | ||
202 | static inline void udev_selinux_setfscreateconat(struct udev *udev, int dfd, const char *file, unsigned int mode) {} | ||
203 | static inline void udev_selinux_resetfscreatecon(struct udev *udev) {} | ||
204 | #else | ||
205 | void udev_selinux_init(struct udev *udev); | ||
206 | void udev_selinux_exit(struct udev *udev); | ||
207 | void udev_selinux_lsetfilecon(struct udev *udev, const char *file, unsigned int mode); | ||
208 | void udev_selinux_setfscreatecon(struct udev *udev, const char *file, unsigned int mode); | ||
209 | void udev_selinux_setfscreateconat(struct udev *udev, int dfd, const char *file, unsigned int mode); | ||
210 | void udev_selinux_resetfscreatecon(struct udev *udev); | ||
211 | #endif | ||
212 | |||
213 | #endif |
File src/libudev-queue-private.c added (mode: 100644) (index 0000000..d3e09e8) | |||
1 | /* | ||
2 | * libudev - interface to udev device information | ||
3 | * | ||
4 | * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org> | ||
5 | * Copyright (C) 2009 Alan Jenkins <alan-jenkins@tuffmail.co.uk> | ||
6 | * | ||
7 | * This library is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | /* | ||
14 | * DISCLAIMER - The file format mentioned here is private to udev/libudev, | ||
15 | * and may be changed without notice. | ||
16 | * | ||
17 | * The udev event queue is exported as a binary log file. | ||
18 | * Each log record consists of a sequence number followed by the device path. | ||
19 | * | ||
20 | * When a new event is queued, its details are appended to the log. | ||
21 | * When the event finishes, a second record is appended to the log | ||
22 | * with the same sequence number but a devpath len of 0. | ||
23 | * | ||
24 | * Example: | ||
25 | * { 0x0000000000000001 } | ||
26 | * { 0x0000000000000001, 0x0019, "/devices/virtual/mem/null" }, | ||
27 | * { 0x0000000000000002, 0x001b, "/devices/virtual/mem/random" }, | ||
28 | * { 0x0000000000000001, 0x0000 }, | ||
29 | * { 0x0000000000000003, 0x0019, "/devices/virtual/mem/zero" }, | ||
30 | * | ||
31 | * Events 2 and 3 are still queued, but event 1 has finished. | ||
32 | * | ||
33 | * The queue does not grow indefinitely. It is periodically re-created | ||
34 | * to remove finished events. Atomic rename() makes this transparent to readers. | ||
35 | * | ||
36 | * The queue file starts with a single sequence number which specifies the | ||
37 | * minimum sequence number in the log that follows. Any events prior to this | ||
38 | * sequence number have already finished. | ||
39 | */ | ||
40 | |||
41 | #include <stdio.h> | ||
42 | #include <stdlib.h> | ||
43 | #include <string.h> | ||
44 | #include <unistd.h> | ||
45 | #include <fcntl.h> | ||
46 | #include <dirent.h> | ||
47 | #include <limits.h> | ||
48 | #include <errno.h> | ||
49 | #include <sys/stat.h> | ||
50 | #include <sys/types.h> | ||
51 | |||
52 | #include "libudev.h" | ||
53 | #include "libudev-private.h" | ||
54 | |||
55 | static int rebuild_queue_file(struct udev_queue_export *udev_queue_export); | ||
56 | |||
57 | struct udev_queue_export { | ||
58 | struct udev *udev; | ||
59 | int queued_count; /* number of unfinished events exported in queue file */ | ||
60 | FILE *queue_file; | ||
61 | unsigned long long int seqnum_max; /* earliest sequence number in queue file */ | ||
62 | unsigned long long int seqnum_min; /* latest sequence number in queue file */ | ||
63 | int waste_bytes; /* queue file bytes wasted on finished events */ | ||
64 | }; | ||
65 | |||
66 | struct udev_queue_export *udev_queue_export_new(struct udev *udev) | ||
67 | { | ||
68 | struct udev_queue_export *udev_queue_export; | ||
69 | unsigned long long int initial_seqnum; | ||
70 | |||
71 | if (udev == NULL) | ||
72 | return NULL; | ||
73 | |||
74 | udev_queue_export = calloc(1, sizeof(struct udev_queue_export)); | ||
75 | if (udev_queue_export == NULL) | ||
76 | return NULL; | ||
77 | udev_queue_export->udev = udev; | ||
78 | |||
79 | initial_seqnum = udev_get_kernel_seqnum(udev); | ||
80 | udev_queue_export->seqnum_min = initial_seqnum; | ||
81 | udev_queue_export->seqnum_max = initial_seqnum; | ||
82 | |||
83 | udev_queue_export_cleanup(udev_queue_export); | ||
84 | if (rebuild_queue_file(udev_queue_export) != 0) { | ||
85 | free(udev_queue_export); | ||
86 | return NULL; | ||
87 | } | ||
88 | |||
89 | return udev_queue_export; | ||
90 | } | ||
91 | |||
92 | struct udev_queue_export *udev_queue_export_unref(struct udev_queue_export *udev_queue_export) | ||
93 | { | ||
94 | if (udev_queue_export == NULL) | ||
95 | return NULL; | ||
96 | if (udev_queue_export->queue_file != NULL) | ||
97 | fclose(udev_queue_export->queue_file); | ||
98 | free(udev_queue_export); | ||
99 | return NULL; | ||
100 | } | ||
101 | |||
102 | void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export) | ||
103 | { | ||
104 | char filename[UTIL_PATH_SIZE]; | ||
105 | |||
106 | if (udev_queue_export == NULL) | ||
107 | return; | ||
108 | util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue_export->udev), "/queue.tmp", NULL); | ||
109 | unlink(filename); | ||
110 | util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue_export->udev), "/queue.bin", NULL); | ||
111 | unlink(filename); | ||
112 | } | ||
113 | |||
114 | static int skip_to(FILE *file, long offset) | ||
115 | { | ||
116 | long old_offset; | ||
117 | |||
118 | /* fseek may drop buffered data, avoid it for small seeks */ | ||
119 | old_offset = ftell(file); | ||
120 | if (offset > old_offset && offset - old_offset <= BUFSIZ) { | ||
121 | size_t skip_bytes = offset - old_offset; | ||
122 | char *buf = alloca(skip_bytes); | ||
123 | |||
124 | if (fread(buf, skip_bytes, 1, file) != skip_bytes) | ||
125 | return -1; | ||
126 | } | ||
127 | |||
128 | return fseek(file, offset, SEEK_SET); | ||
129 | } | ||
130 | |||
131 | struct queue_devpaths { | ||
132 | unsigned int devpaths_first; /* index of first queued event */ | ||
133 | unsigned int devpaths_size; | ||
134 | long devpaths[]; /* seqnum -> offset of devpath in queue file (or 0) */ | ||
135 | }; | ||
136 | |||
137 | /* | ||
138 | * Returns a table mapping seqnum to devpath file offset for currently queued events. | ||
139 | * devpaths[i] represents the event with seqnum = i + udev_queue_export->seqnum_min. | ||
140 | */ | ||
141 | static struct queue_devpaths *build_index(struct udev_queue_export *udev_queue_export) | ||
142 | { | ||
143 | struct queue_devpaths *devpaths; | ||
144 | unsigned long long int range; | ||
145 | long devpath_offset; | ||
146 | ssize_t devpath_len; | ||
147 | unsigned long long int seqnum; | ||
148 | unsigned long long int n; | ||
149 | unsigned int i; | ||
150 | |||
151 | /* seek to the first event in the file */ | ||
152 | rewind(udev_queue_export->queue_file); | ||
153 | udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum); | ||
154 | |||
155 | /* allocate the table */ | ||
156 | range = udev_queue_export->seqnum_min - udev_queue_export->seqnum_max; | ||
157 | if (range - 1 > INT_MAX) { | ||
158 | err(udev_queue_export->udev, "queue file overflow\n"); | ||
159 | return NULL; | ||
160 | } | ||
161 | devpaths = calloc(1, sizeof(struct queue_devpaths) + (range + 1) * sizeof(long)); | ||
162 | if (devpaths == NULL) | ||
163 | return NULL; | ||
164 | devpaths->devpaths_size = range + 1; | ||
165 | |||
166 | /* read all records and populate the table */ | ||
167 | for (;;) { | ||
168 | if (udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum) < 0) | ||
169 | break; | ||
170 | n = seqnum - udev_queue_export->seqnum_max; | ||
171 | if (n >= devpaths->devpaths_size) | ||
172 | goto read_error; | ||
173 | |||
174 | devpath_offset = ftell(udev_queue_export->queue_file); | ||
175 | devpath_len = udev_queue_skip_devpath(udev_queue_export->queue_file); | ||
176 | if (devpath_len < 0) | ||
177 | goto read_error; | ||
178 | |||
179 | if (devpath_len > 0) | ||
180 | devpaths->devpaths[n] = devpath_offset; | ||
181 | else | ||
182 | devpaths->devpaths[n] = 0; | ||
183 | } | ||
184 | |||
185 | /* find first queued event */ | ||
186 | for (i = 0; i < devpaths->devpaths_size; i++) { | ||
187 | if (devpaths->devpaths[i] != 0) | ||
188 | break; | ||
189 | } | ||
190 | devpaths->devpaths_first = i; | ||
191 | |||
192 | return devpaths; | ||
193 | |||
194 | read_error: | ||
195 | err(udev_queue_export->udev, "queue file corrupted\n"); | ||
196 | free(devpaths); | ||
197 | return NULL; | ||
198 | } | ||
199 | |||
200 | static int rebuild_queue_file(struct udev_queue_export *udev_queue_export) | ||
201 | { | ||
202 | unsigned long long int seqnum; | ||
203 | struct queue_devpaths *devpaths = NULL; | ||
204 | char filename[UTIL_PATH_SIZE]; | ||
205 | char filename_tmp[UTIL_PATH_SIZE]; | ||
206 | FILE *new_queue_file = NULL; | ||
207 | unsigned int i; | ||
208 | |||
209 | /* read old queue file */ | ||
210 | if (udev_queue_export->queue_file != NULL) { | ||
211 | dbg(udev_queue_export->udev, "compacting queue file, freeing %d bytes\n", | ||
212 | udev_queue_export->waste_bytes); | ||
213 | |||
214 | devpaths = build_index(udev_queue_export); | ||
215 | if (devpaths != NULL) | ||
216 | udev_queue_export->seqnum_max += devpaths->devpaths_first; | ||
217 | } | ||
218 | if (devpaths == NULL) { | ||
219 | dbg(udev_queue_export->udev, "creating empty queue file\n"); | ||
220 | udev_queue_export->queued_count = 0; | ||
221 | udev_queue_export->seqnum_max = udev_queue_export->seqnum_min; | ||
222 | } | ||
223 | |||
224 | /* create new queue file */ | ||
225 | util_strscpyl(filename_tmp, sizeof(filename_tmp), udev_get_run_path(udev_queue_export->udev), "/queue.tmp", NULL); | ||
226 | new_queue_file = fopen(filename_tmp, "w+"); | ||
227 | if (new_queue_file == NULL) | ||
228 | goto error; | ||
229 | seqnum = udev_queue_export->seqnum_max; | ||
230 | fwrite(&seqnum, 1, sizeof(unsigned long long int), new_queue_file); | ||
231 | |||
232 | /* copy unfinished events only to the new file */ | ||
233 | if (devpaths != NULL) { | ||
234 | for (i = devpaths->devpaths_first; i < devpaths->devpaths_size; i++) { | ||
235 | char devpath[UTIL_PATH_SIZE]; | ||
236 | int err; | ||
237 | unsigned short devpath_len; | ||
238 | |||
239 | if (devpaths->devpaths[i] != 0) | ||
240 | { | ||
241 | skip_to(udev_queue_export->queue_file, devpaths->devpaths[i]); | ||
242 | err = udev_queue_read_devpath(udev_queue_export->queue_file, devpath, sizeof(devpath)); | ||
243 | devpath_len = err; | ||
244 | |||
245 | fwrite(&seqnum, sizeof(unsigned long long int), 1, new_queue_file); | ||
246 | fwrite(&devpath_len, sizeof(unsigned short), 1, new_queue_file); | ||
247 | fwrite(devpath, 1, devpath_len, new_queue_file); | ||
248 | } | ||
249 | seqnum++; | ||
250 | } | ||
251 | free(devpaths); | ||
252 | devpaths = NULL; | ||
253 | } | ||
254 | fflush(new_queue_file); | ||
255 | if (ferror(new_queue_file)) | ||
256 | goto error; | ||
257 | |||
258 | /* rename the new file on top of the old one */ | ||
259 | util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue_export->udev), "/queue.bin", NULL); | ||
260 | if (rename(filename_tmp, filename) != 0) | ||
261 | goto error; | ||
262 | |||
263 | if (udev_queue_export->queue_file != NULL) | ||
264 | fclose(udev_queue_export->queue_file); | ||
265 | udev_queue_export->queue_file = new_queue_file; | ||
266 | udev_queue_export->waste_bytes = 0; | ||
267 | |||
268 | return 0; | ||
269 | |||
270 | error: | ||
271 | err(udev_queue_export->udev, "failed to create queue file: %m\n"); | ||
272 | udev_queue_export_cleanup(udev_queue_export); | ||
273 | |||
274 | if (udev_queue_export->queue_file != NULL) { | ||
275 | fclose(udev_queue_export->queue_file); | ||
276 | udev_queue_export->queue_file = NULL; | ||
277 | } | ||
278 | if (new_queue_file != NULL) | ||
279 | fclose(new_queue_file); | ||
280 | |||
281 | if (devpaths != NULL) | ||
282 | free(devpaths); | ||
283 | udev_queue_export->queued_count = 0; | ||
284 | udev_queue_export->waste_bytes = 0; | ||
285 | udev_queue_export->seqnum_max = udev_queue_export->seqnum_min; | ||
286 | |||
287 | return -1; | ||
288 | } | ||
289 | |||
290 | static int write_queue_record(struct udev_queue_export *udev_queue_export, | ||
291 | unsigned long long int seqnum, const char *devpath, size_t devpath_len) | ||
292 | { | ||
293 | unsigned short len; | ||
294 | |||
295 | if (udev_queue_export->queue_file == NULL) { | ||
296 | dbg(udev_queue_export->udev, "can't record event: queue file not available\n"); | ||
297 | return -1; | ||
298 | } | ||
299 | |||
300 | if (fwrite(&seqnum, sizeof(unsigned long long int), 1, udev_queue_export->queue_file) != 1) | ||
301 | goto write_error; | ||
302 | |||
303 | len = (devpath_len < USHRT_MAX) ? devpath_len : USHRT_MAX; | ||
304 | if (fwrite(&len, sizeof(unsigned short), 1, udev_queue_export->queue_file) != 1) | ||
305 | goto write_error; | ||
306 | if (len > 0) { | ||
307 | if (fwrite(devpath, 1, len, udev_queue_export->queue_file) != len) | ||
308 | goto write_error; | ||
309 | } | ||
310 | |||
311 | /* *must* flush output; caller may fork */ | ||
312 | if (fflush(udev_queue_export->queue_file) != 0) | ||
313 | goto write_error; | ||
314 | |||
315 | return 0; | ||
316 | |||
317 | write_error: | ||
318 | /* if we failed half way through writing a record to a file, | ||
319 | we should not try to write any further records to it. */ | ||
320 | err(udev_queue_export->udev, "error writing to queue file: %m\n"); | ||
321 | fclose(udev_queue_export->queue_file); | ||
322 | udev_queue_export->queue_file = NULL; | ||
323 | |||
324 | return -1; | ||
325 | } | ||
326 | |||
327 | enum device_state { | ||
328 | DEVICE_QUEUED, | ||
329 | DEVICE_FINISHED, | ||
330 | }; | ||
331 | |||
332 | static inline size_t queue_record_size(size_t devpath_len) | ||
333 | { | ||
334 | return sizeof(unsigned long long int) + sizeof(unsigned short int) + devpath_len; | ||
335 | } | ||
336 | |||
337 | static int update_queue(struct udev_queue_export *udev_queue_export, | ||
338 | struct udev_device *udev_device, enum device_state state) | ||
339 | { | ||
340 | unsigned long long int seqnum = udev_device_get_seqnum(udev_device); | ||
341 | const char *devpath = NULL; | ||
342 | size_t devpath_len = 0; | ||
343 | int bytes; | ||
344 | int err; | ||
345 | |||
346 | /* FINISHED records have a zero length devpath */ | ||
347 | if (state == DEVICE_QUEUED) { | ||
348 | devpath = udev_device_get_devpath(udev_device); | ||
349 | devpath_len = strlen(devpath); | ||
350 | } | ||
351 | |||
352 | /* recover from an earlier failed rebuild */ | ||
353 | if (udev_queue_export->queue_file == NULL) { | ||
354 | if (rebuild_queue_file(udev_queue_export) != 0) | ||
355 | return -1; | ||
356 | } | ||
357 | |||
358 | /* if we're removing the last event from the queue, that's the best time to rebuild it */ | ||
359 | if (state != DEVICE_QUEUED && udev_queue_export->queued_count == 1) { | ||
360 | /* we don't need to read the old queue file */ | ||
361 | fclose(udev_queue_export->queue_file); | ||
362 | udev_queue_export->queue_file = NULL; | ||
363 | rebuild_queue_file(udev_queue_export); | ||
364 | return 0; | ||
365 | } | ||
366 | |||
367 | /* try to rebuild the queue files before they grow larger than one page. */ | ||
368 | bytes = ftell(udev_queue_export->queue_file) + queue_record_size(devpath_len); | ||
369 | if ((udev_queue_export->waste_bytes > bytes / 2) && bytes > 4096) | ||
370 | rebuild_queue_file(udev_queue_export); | ||
371 | |||
372 | /* don't record a finished event, if we already dropped the event in a failed rebuild */ | ||
373 | if (seqnum < udev_queue_export->seqnum_max) | ||
374 | return 0; | ||
375 | |||
376 | /* now write to the queue */ | ||
377 | if (state == DEVICE_QUEUED) { | ||
378 | udev_queue_export->queued_count++; | ||
379 | udev_queue_export->seqnum_min = seqnum; | ||
380 | } else { | ||
381 | udev_queue_export->waste_bytes += queue_record_size(devpath_len) + queue_record_size(0); | ||
382 | udev_queue_export->queued_count--; | ||
383 | } | ||
384 | err = write_queue_record(udev_queue_export, seqnum, devpath, devpath_len); | ||
385 | |||
386 | /* try to handle ENOSPC */ | ||
387 | if (err != 0 && udev_queue_export->queued_count == 0) { | ||
388 | udev_queue_export_cleanup(udev_queue_export); | ||
389 | err = rebuild_queue_file(udev_queue_export); | ||
390 | } | ||
391 | |||
392 | return err; | ||
393 | } | ||
394 | |||
395 | static int update(struct udev_queue_export *udev_queue_export, | ||
396 | struct udev_device *udev_device, enum device_state state) | ||
397 | { | ||
398 | if (update_queue(udev_queue_export, udev_device, state) != 0) | ||
399 | return -1; | ||
400 | |||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device) | ||
405 | { | ||
406 | return update(udev_queue_export, udev_device, DEVICE_QUEUED); | ||
407 | } | ||
408 | |||
409 | int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device) | ||
410 | { | ||
411 | return update(udev_queue_export, udev_device, DEVICE_FINISHED); | ||
412 | } |
File src/libudev-queue.c added (mode: 100644) (index 0000000..03722ac) | |||
1 | /* | ||
2 | * libudev - interface to udev device information | ||
3 | * | ||
4 | * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org> | ||
5 | * Copyright (C) 2009 Alan Jenkins <alan-jenkins@tuffmail.co.uk> | ||
6 | * | ||
7 | * This library is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <stdio.h> | ||
14 | #include <stdlib.h> | ||
15 | #include <stddef.h> | ||
16 | #include <unistd.h> | ||
17 | #include <errno.h> | ||
18 | #include <string.h> | ||
19 | #include <dirent.h> | ||
20 | #include <fcntl.h> | ||
21 | #include <limits.h> | ||
22 | #include <sys/stat.h> | ||
23 | |||
24 | #include "libudev.h" | ||
25 | #include "libudev-private.h" | ||
26 | |||
27 | /** | ||
28 | * SECTION:libudev-queue | ||
29 | * @short_description: access to currently active events | ||
30 | * | ||
31 | * The udev daemon processes events asynchronously. All events which do not have | ||
32 | * interdependencies run in parallel. This exports the current state of the | ||
33 | * event processing queue, and the current event sequence numbers from the kernel | ||
34 | * and the udev daemon. | ||
35 | */ | ||
36 | |||
37 | /** | ||
38 | * udev_queue: | ||
39 | * | ||
40 | * Opaque object representing the current event queue in the udev daemon. | ||
41 | */ | ||
42 | struct udev_queue { | ||
43 | struct udev *udev; | ||
44 | int refcount; | ||
45 | struct udev_list queue_list; | ||
46 | }; | ||
47 | |||
48 | /** | ||
49 | * udev_queue_new: | ||
50 | * @udev: udev library context | ||
51 | * | ||
52 | * The initial refcount is 1, and needs to be decremented to | ||
53 | * release the resources of the udev queue context. | ||
54 | * | ||
55 | * Returns: the udev queue context, or #NULL on error. | ||
56 | **/ | ||
57 | UDEV_EXPORT struct udev_queue *udev_queue_new(struct udev *udev) | ||
58 | { | ||
59 | struct udev_queue *udev_queue; | ||
60 | |||
61 | if (udev == NULL) | ||
62 | return NULL; | ||
63 | |||
64 | udev_queue = calloc(1, sizeof(struct udev_queue)); | ||
65 | if (udev_queue == NULL) | ||
66 | return NULL; | ||
67 | udev_queue->refcount = 1; | ||
68 | udev_queue->udev = udev; | ||
69 | udev_list_init(udev, &udev_queue->queue_list, false); | ||
70 | return udev_queue; | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * udev_queue_ref: | ||
75 | * @udev_queue: udev queue context | ||
76 | * | ||
77 | * Take a reference of a udev queue context. | ||
78 | * | ||
79 | * Returns: the same udev queue context. | ||
80 | **/ | ||
81 | UDEV_EXPORT struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue) | ||
82 | { | ||
83 | if (udev_queue == NULL) | ||
84 | return NULL; | ||
85 | udev_queue->refcount++; | ||
86 | return udev_queue; | ||
87 | } | ||
88 | |||
89 | /** | ||
90 | * udev_queue_unref: | ||
91 | * @udev_queue: udev queue context | ||
92 | * | ||
93 | * Drop a reference of a udev queue context. If the refcount reaches zero, | ||
94 | * the resources of the queue context will be released. | ||
95 | **/ | ||
96 | UDEV_EXPORT struct udev_queue *udev_queue_unref(struct udev_queue *udev_queue) | ||
97 | { | ||
98 | if (udev_queue == NULL) | ||
99 | return NULL; | ||
100 | udev_queue->refcount--; | ||
101 | if (udev_queue->refcount > 0) | ||
102 | return udev_queue; | ||
103 | udev_list_cleanup(&udev_queue->queue_list); | ||
104 | free(udev_queue); | ||
105 | return NULL; | ||
106 | } | ||
107 | |||
108 | /** | ||
109 | * udev_queue_get_udev: | ||
110 | * @udev_queue: udev queue context | ||
111 | * | ||
112 | * Retrieve the udev library context the queue context was created with. | ||
113 | * | ||
114 | * Returns: the udev library context. | ||
115 | **/ | ||
116 | UDEV_EXPORT struct udev *udev_queue_get_udev(struct udev_queue *udev_queue) | ||
117 | { | ||
118 | if (udev_queue == NULL) | ||
119 | return NULL; | ||
120 | return udev_queue->udev; | ||
121 | } | ||
122 | |||
123 | unsigned long long int udev_get_kernel_seqnum(struct udev *udev) | ||
124 | { | ||
125 | char filename[UTIL_PATH_SIZE]; | ||
126 | unsigned long long int seqnum; | ||
127 | int fd; | ||
128 | char buf[32]; | ||
129 | ssize_t len; | ||
130 | |||
131 | util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), "/kernel/uevent_seqnum", NULL); | ||
132 | fd = open(filename, O_RDONLY|O_CLOEXEC); | ||
133 | if (fd < 0) | ||
134 | return 0; | ||
135 | len = read(fd, buf, sizeof(buf)); | ||
136 | close(fd); | ||
137 | if (len <= 2) | ||
138 | return 0; | ||
139 | buf[len-1] = '\0'; | ||
140 | seqnum = strtoull(buf, NULL, 10); | ||
141 | return seqnum; | ||
142 | } | ||
143 | |||
144 | /** | ||
145 | * udev_queue_get_kernel_seqnum: | ||
146 | * @udev_queue: udev queue context | ||
147 | * | ||
148 | * Get the current kernel event sequence number. | ||
149 | * | ||
150 | * Returns: the sequence number. | ||
151 | **/ | ||
152 | UDEV_EXPORT unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue) | ||
153 | { | ||
154 | unsigned long long int seqnum; | ||
155 | |||
156 | if (udev_queue == NULL) | ||
157 | return -EINVAL; | ||
158 | |||
159 | seqnum = udev_get_kernel_seqnum(udev_queue->udev); | ||
160 | dbg(udev_queue->udev, "seqnum=%llu\n", seqnum); | ||
161 | return seqnum; | ||
162 | } | ||
163 | |||
164 | int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum) | ||
165 | { | ||
166 | if (fread(seqnum, sizeof(unsigned long long int), 1, queue_file) != 1) | ||
167 | return -1; | ||
168 | |||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | ssize_t udev_queue_skip_devpath(FILE *queue_file) | ||
173 | { | ||
174 | unsigned short int len; | ||
175 | |||
176 | if (fread(&len, sizeof(unsigned short int), 1, queue_file) == 1) { | ||
177 | char *devpath = alloca(len); | ||
178 | |||
179 | /* use fread to skip, fseek might drop buffered data */ | ||
180 | if (fread(devpath, 1, len, queue_file) == len) | ||
181 | return len; | ||
182 | } | ||
183 | |||
184 | return -1; | ||
185 | } | ||
186 | |||
187 | ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size) | ||
188 | { | ||
189 | unsigned short int read_bytes = 0; | ||
190 | unsigned short int len; | ||
191 | |||
192 | if (fread(&len, sizeof(unsigned short int), 1, queue_file) != 1) | ||
193 | return -1; | ||
194 | |||
195 | read_bytes = (len < size - 1) ? len : size - 1; | ||
196 | if (fread(devpath, 1, read_bytes, queue_file) != read_bytes) | ||
197 | return -1; | ||
198 | devpath[read_bytes] = '\0'; | ||
199 | |||
200 | /* if devpath was too long, skip unread characters */ | ||
201 | if (read_bytes != len) { | ||
202 | unsigned short int skip_bytes = len - read_bytes; | ||
203 | char *buf = alloca(skip_bytes); | ||
204 | |||
205 | if (fread(buf, 1, skip_bytes, queue_file) != skip_bytes) | ||
206 | return -1; | ||
207 | } | ||
208 | |||
209 | return read_bytes; | ||
210 | } | ||
211 | |||
212 | static FILE *open_queue_file(struct udev_queue *udev_queue, unsigned long long int *seqnum_start) | ||
213 | { | ||
214 | char filename[UTIL_PATH_SIZE]; | ||
215 | FILE *queue_file; | ||
216 | |||
217 | util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue->udev), "/queue.bin", NULL); | ||
218 | queue_file = fopen(filename, "re"); | ||
219 | if (queue_file == NULL) | ||
220 | return NULL; | ||
221 | |||
222 | if (udev_queue_read_seqnum(queue_file, seqnum_start) < 0) { | ||
223 | err(udev_queue->udev, "corrupt queue file\n"); | ||
224 | fclose(queue_file); | ||
225 | return NULL; | ||
226 | } | ||
227 | |||
228 | return queue_file; | ||
229 | } | ||
230 | |||
231 | /** | ||
232 | * udev_queue_get_udev_seqnum: | ||
233 | * @udev_queue: udev queue context | ||
234 | * | ||
235 | * Get the last known udev event sequence number. | ||
236 | * | ||
237 | * Returns: the sequence number. | ||
238 | **/ | ||
239 | UDEV_EXPORT unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue) | ||
240 | { | ||
241 | unsigned long long int seqnum_udev; | ||
242 | FILE *queue_file; | ||
243 | |||
244 | queue_file = open_queue_file(udev_queue, &seqnum_udev); | ||
245 | if (queue_file == NULL) | ||
246 | return 0; | ||
247 | |||
248 | for (;;) { | ||
249 | unsigned long long int seqnum; | ||
250 | ssize_t devpath_len; | ||
251 | |||
252 | if (udev_queue_read_seqnum(queue_file, &seqnum) < 0) | ||
253 | break; | ||
254 | devpath_len = udev_queue_skip_devpath(queue_file); | ||
255 | if (devpath_len < 0) | ||
256 | break; | ||
257 | if (devpath_len > 0) | ||
258 | seqnum_udev = seqnum; | ||
259 | } | ||
260 | |||
261 | fclose(queue_file); | ||
262 | return seqnum_udev; | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * udev_queue_get_udev_is_active: | ||
267 | * @udev_queue: udev queue context | ||
268 | * | ||
269 | * Check if udev is active on the system. | ||
270 | * | ||
271 | * Returns: a flag indicating if udev is active. | ||
272 | **/ | ||
273 | UDEV_EXPORT int udev_queue_get_udev_is_active(struct udev_queue *udev_queue) | ||
274 | { | ||
275 | unsigned long long int seqnum_start; | ||
276 | FILE *queue_file; | ||
277 | |||
278 | queue_file = open_queue_file(udev_queue, &seqnum_start); | ||
279 | if (queue_file == NULL) | ||
280 | return 0; | ||
281 | |||
282 | fclose(queue_file); | ||
283 | return 1; | ||
284 | } | ||
285 | |||
286 | /** | ||
287 | * udev_queue_get_queue_is_empty: | ||
288 | * @udev_queue: udev queue context | ||
289 | * | ||
290 | * Check if udev is currently processing any events. | ||
291 | * | ||
292 | * Returns: a flag indicating if udev is currently handling events. | ||
293 | **/ | ||
294 | UDEV_EXPORT int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue) | ||
295 | { | ||
296 | unsigned long long int seqnum_kernel; | ||
297 | unsigned long long int seqnum_udev = 0; | ||
298 | int queued = 0; | ||
299 | int is_empty = 0; | ||
300 | FILE *queue_file; | ||
301 | |||
302 | if (udev_queue == NULL) | ||
303 | return -EINVAL; | ||
304 | queue_file = open_queue_file(udev_queue, &seqnum_udev); | ||
305 | if (queue_file == NULL) | ||
306 | return 1; | ||
307 | |||
308 | for (;;) { | ||
309 | unsigned long long int seqnum; | ||
310 | ssize_t devpath_len; | ||
311 | |||
312 | if (udev_queue_read_seqnum(queue_file, &seqnum) < 0) | ||
313 | break; | ||
314 | devpath_len = udev_queue_skip_devpath(queue_file); | ||
315 | if (devpath_len < 0) | ||
316 | break; | ||
317 | |||
318 | if (devpath_len > 0) { | ||
319 | queued++; | ||
320 | seqnum_udev = seqnum; | ||
321 | } else { | ||
322 | queued--; | ||
323 | } | ||
324 | } | ||
325 | |||
326 | if (queued > 0) { | ||
327 | dbg(udev_queue->udev, "queue is not empty\n"); | ||
328 | goto out; | ||
329 | } | ||
330 | |||
331 | seqnum_kernel = udev_queue_get_kernel_seqnum(udev_queue); | ||
332 | if (seqnum_udev < seqnum_kernel) { | ||
333 | dbg(udev_queue->udev, "queue is empty but kernel events still pending [%llu]<->[%llu]\n", | ||
334 | seqnum_kernel, seqnum_udev); | ||
335 | goto out; | ||
336 | } | ||
337 | |||
338 | dbg(udev_queue->udev, "queue is empty\n"); | ||
339 | is_empty = 1; | ||
340 | |||
341 | out: | ||
342 | fclose(queue_file); | ||
343 | return is_empty; | ||
344 | } | ||
345 | |||
346 | /** | ||
347 | * udev_queue_get_seqnum_sequence_is_finished: | ||
348 | * @udev_queue: udev queue context | ||
349 | * @start: first event sequence number | ||
350 | * @end: last event sequence number | ||
351 | * | ||
352 | * Check if udev is currently processing any events in a given sequence number range. | ||
353 | * | ||
354 | * Returns: a flag indicating if any of the sequence numbers in the given range is currently active. | ||
355 | **/ | ||
356 | UDEV_EXPORT int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue, | ||
357 | unsigned long long int start, unsigned long long int end) | ||
358 | { | ||
359 | unsigned long long int seqnum; | ||
360 | ssize_t devpath_len; | ||
361 | int unfinished; | ||
362 | FILE *queue_file; | ||
363 | |||
364 | if (udev_queue == NULL) | ||
365 | return -EINVAL; | ||
366 | queue_file = open_queue_file(udev_queue, &seqnum); | ||
367 | if (queue_file == NULL) | ||
368 | return 1; | ||
369 | if (start < seqnum) | ||
370 | start = seqnum; | ||
371 | if (start > end) { | ||
372 | fclose(queue_file); | ||
373 | return 1; | ||
374 | } | ||
375 | if (end - start > INT_MAX - 1) { | ||
376 | fclose(queue_file); | ||
377 | return -EOVERFLOW; | ||
378 | } | ||
379 | |||
380 | /* | ||
381 | * we might start with 0, and handle the initial seqnum | ||
382 | * only when we find an entry in the queue file | ||
383 | **/ | ||
384 | unfinished = end - start; | ||
385 | |||
386 | do { | ||
387 | if (udev_queue_read_seqnum(queue_file, &seqnum) < 0) | ||
388 | break; | ||
389 | devpath_len = udev_queue_skip_devpath(queue_file); | ||
390 | if (devpath_len < 0) | ||
391 | break; | ||
392 | |||
393 | /* | ||
394 | * we might start with an empty or re-build queue file, where | ||
395 | * the initial seqnum is not recorded as finished | ||
396 | */ | ||
397 | if (start == seqnum && devpath_len > 0) | ||
398 | unfinished++; | ||
399 | |||
400 | if (devpath_len == 0) { | ||
401 | if (seqnum >= start && seqnum <= end) | ||
402 | unfinished--; | ||
403 | } | ||
404 | } while (unfinished > 0); | ||
405 | |||
406 | fclose(queue_file); | ||
407 | |||
408 | return (unfinished == 0); | ||
409 | } | ||
410 | |||
411 | /** | ||
412 | * udev_queue_get_seqnum_is_finished: | ||
413 | * @udev_queue: udev queue context | ||
414 | * @seqnum: sequence number | ||
415 | * | ||
416 | * Check if udev is currently processing a given sequence number. | ||
417 | * | ||
418 | * Returns: a flag indicating if the given sequence number is currently active. | ||
419 | **/ | ||
420 | UDEV_EXPORT int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum) | ||
421 | { | ||
422 | if (!udev_queue_get_seqnum_sequence_is_finished(udev_queue, seqnum, seqnum)) | ||
423 | return 0; | ||
424 | |||
425 | dbg(udev_queue->udev, "seqnum: %llu finished\n", seqnum); | ||
426 | return 1; | ||
427 | } | ||
428 | |||
429 | /** | ||
430 | * udev_queue_get_queued_list_entry: | ||
431 | * @udev_queue: udev queue context | ||
432 | * | ||
433 | * Get the first entry of the list of queued events. | ||
434 | * | ||
435 | * Returns: a udev_list_entry. | ||
436 | **/ | ||
437 | UDEV_EXPORT struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue) | ||
438 | { | ||
439 | unsigned long long int seqnum; | ||
440 | FILE *queue_file; | ||
441 | |||
442 | if (udev_queue == NULL) | ||
443 | return NULL; | ||
444 | udev_list_cleanup(&udev_queue->queue_list); | ||
445 | |||
446 | queue_file = open_queue_file(udev_queue, &seqnum); | ||
447 | if (queue_file == NULL) | ||
448 | return NULL; | ||
449 | |||
450 | for (;;) { | ||
451 | char syspath[UTIL_PATH_SIZE]; | ||
452 | char *s; | ||
453 | size_t l; | ||
454 | ssize_t len; | ||
455 | char seqnum_str[32]; | ||
456 | struct udev_list_entry *list_entry; | ||
457 | |||
458 | if (udev_queue_read_seqnum(queue_file, &seqnum) < 0) | ||
459 | break; | ||
460 | snprintf(seqnum_str, sizeof(seqnum_str), "%llu", seqnum); | ||
461 | |||
462 | s = syspath; | ||
463 | l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev_queue->udev), NULL); | ||
464 | len = udev_queue_read_devpath(queue_file, s, l); | ||
465 | if (len < 0) | ||
466 | break; | ||
467 | |||
468 | if (len > 0) { | ||
469 | udev_list_entry_add(&udev_queue->queue_list, syspath, seqnum_str); | ||
470 | } else { | ||
471 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_queue->queue_list)) { | ||
472 | if (strcmp(seqnum_str, udev_list_entry_get_value(list_entry)) == 0) { | ||
473 | udev_list_entry_delete(list_entry); | ||
474 | break; | ||
475 | } | ||
476 | } | ||
477 | } | ||
478 | } | ||
479 | fclose(queue_file); | ||
480 | |||
481 | return udev_list_get_entry(&udev_queue->queue_list); | ||
482 | } | ||
483 | |||
484 | struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue); | ||
485 | UDEV_EXPORT struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue) | ||
486 | { | ||
487 | errno = ENOSYS; | ||
488 | return NULL; | ||
489 | } |
File src/libudev-util-private.c added (mode: 100644) (index 0000000..f764ab4) | |||
1 | /* | ||
2 | * libudev - interface to udev device information | ||
3 | * | ||
4 | * Copyright (C) 2003-2009 Kay Sievers <kay.sievers@vrfy.org> | ||
5 | * | ||
6 | * This library is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU Lesser General Public | ||
8 | * License as published by the Free Software Foundation; either | ||
9 | * version 2.1 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <stdlib.h> | ||
13 | #include <stdio.h> | ||
14 | #include <stddef.h> | ||
15 | #include <unistd.h> | ||
16 | #include <string.h> | ||
17 | #include <fcntl.h> | ||
18 | #include <errno.h> | ||
19 | #include <ctype.h> | ||
20 | #include <pwd.h> | ||
21 | #include <grp.h> | ||
22 | #include <sys/param.h> | ||
23 | |||
24 | #include "libudev.h" | ||
25 | #include "libudev-private.h" | ||
26 | |||
27 | static int create_path(struct udev *udev, const char *path, bool selinux) | ||
28 | { | ||
29 | char p[UTIL_PATH_SIZE]; | ||
30 | char *pos; | ||
31 | struct stat stats; | ||
32 | int err; | ||
33 | |||
34 | util_strscpy(p, sizeof(p), path); | ||
35 | pos = strrchr(p, '/'); | ||
36 | if (pos == NULL) | ||
37 | return 0; | ||
38 | while (pos != p && pos[-1] == '/') | ||
39 | pos--; | ||
40 | if (pos == p) | ||
41 | return 0; | ||
42 | pos[0] = '\0'; | ||
43 | |||
44 | dbg(udev, "stat '%s'\n", p); | ||
45 | if (stat(p, &stats) == 0) { | ||
46 | if ((stats.st_mode & S_IFMT) == S_IFDIR) | ||
47 | return 0; | ||
48 | else | ||
49 | return -ENOTDIR; | ||
50 | } | ||
51 | |||
52 | err = util_create_path(udev, p); | ||
53 | if (err != 0) | ||
54 | return err; | ||
55 | |||
56 | dbg(udev, "mkdir '%s'\n", p); | ||
57 | if (selinux) | ||
58 | udev_selinux_setfscreatecon(udev, p, S_IFDIR|0755); | ||
59 | err = mkdir(p, 0755); | ||
60 | if (err != 0) { | ||
61 | err = -errno; | ||
62 | if (err == -EEXIST && stat(p, &stats) == 0) { | ||
63 | if ((stats.st_mode & S_IFMT) == S_IFDIR) | ||
64 | err = 0; | ||
65 | else | ||
66 | err = -ENOTDIR; | ||
67 | } | ||
68 | } | ||
69 | if (selinux) | ||
70 | udev_selinux_resetfscreatecon(udev); | ||
71 | return err; | ||
72 | } | ||
73 | |||
74 | int util_create_path(struct udev *udev, const char *path) | ||
75 | { | ||
76 | return create_path(udev, path, false); | ||
77 | } | ||
78 | |||
79 | int util_create_path_selinux(struct udev *udev, const char *path) | ||
80 | { | ||
81 | return create_path(udev, path, true); | ||
82 | } | ||
83 | |||
84 | int util_delete_path(struct udev *udev, const char *path) | ||
85 | { | ||
86 | char p[UTIL_PATH_SIZE]; | ||
87 | char *pos; | ||
88 | int err = 0; | ||
89 | |||
90 | if (path[0] == '/') | ||
91 | while(path[1] == '/') | ||
92 | path++; | ||
93 | util_strscpy(p, sizeof(p), path); | ||
94 | pos = strrchr(p, '/'); | ||
95 | if (pos == p || pos == NULL) | ||
96 | return 0; | ||
97 | |||
98 | for (;;) { | ||
99 | *pos = '\0'; | ||
100 | pos = strrchr(p, '/'); | ||
101 | |||
102 | /* don't remove the last one */ | ||
103 | if ((pos == p) || (pos == NULL)) | ||
104 | break; | ||
105 | |||
106 | err = rmdir(p); | ||
107 | if (err < 0) { | ||
108 | if (errno == ENOENT) | ||
109 | err = 0; | ||
110 | break; | ||
111 | } | ||
112 | } | ||
113 | return err; | ||
114 | } | ||
115 | |||
116 | uid_t util_lookup_user(struct udev *udev, const char *user) | ||
117 | { | ||
118 | char *endptr; | ||
119 | struct passwd pwbuf; | ||
120 | struct passwd *pw; | ||
121 | uid_t uid; | ||
122 | size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX); | ||
123 | char *buf = alloca(buflen); | ||
124 | |||
125 | if (strcmp(user, "root") == 0) | ||
126 | return 0; | ||
127 | uid = strtoul(user, &endptr, 10); | ||
128 | if (endptr[0] == '\0') | ||
129 | return uid; | ||
130 | |||
131 | errno = getpwnam_r(user, &pwbuf, buf, buflen, &pw); | ||
132 | if (pw != NULL) | ||
133 | return pw->pw_uid; | ||
134 | if (errno == 0 || errno == ENOENT || errno == ESRCH) | ||
135 | err(udev, "specified user '%s' unknown\n", user); | ||
136 | else | ||
137 | err(udev, "error resolving user '%s': %m\n", user); | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | gid_t util_lookup_group(struct udev *udev, const char *group) | ||
142 | { | ||
143 | char *endptr; | ||
144 | struct group grbuf; | ||
145 | struct group *gr; | ||
146 | gid_t gid = 0; | ||
147 | size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX); | ||
148 | char *buf = alloca(buflen); | ||
149 | |||
150 | if (strcmp(group, "root") == 0) | ||
151 | return 0; | ||
152 | gid = strtoul(group, &endptr, 10); | ||
153 | if (endptr[0] == '\0') | ||
154 | return gid; | ||
155 | buf = NULL; | ||
156 | gid = 0; | ||
157 | for (;;) { | ||
158 | char *newbuf; | ||
159 | |||
160 | newbuf = realloc(buf, buflen); | ||
161 | if (!newbuf) | ||
162 | break; | ||
163 | buf = newbuf; | ||
164 | errno = getgrnam_r(group, &grbuf, buf, buflen, &gr); | ||
165 | if (gr != NULL) { | ||
166 | gid = gr->gr_gid; | ||
167 | } else if (errno == ERANGE) { | ||
168 | buflen *= 2; | ||
169 | continue; | ||
170 | } else if (errno == 0 || errno == ENOENT || errno == ESRCH) { | ||
171 | err(udev, "specified group '%s' unknown\n", group); | ||
172 | } else { | ||
173 | err(udev, "error resolving group '%s': %m\n", group); | ||
174 | } | ||
175 | break; | ||
176 | } | ||
177 | free(buf); | ||
178 | return gid; | ||
179 | } | ||
180 | |||
181 | /* handle "[<SUBSYSTEM>/<KERNEL>]<attribute>" format */ | ||
182 | int util_resolve_subsys_kernel(struct udev *udev, const char *string, | ||
183 | char *result, size_t maxsize, int read_value) | ||
184 | { | ||
185 | char temp[UTIL_PATH_SIZE]; | ||
186 | char *subsys; | ||
187 | char *sysname; | ||
188 | struct udev_device *dev; | ||
189 | char *attr; | ||
190 | |||
191 | if (string[0] != '[') | ||
192 | return -1; | ||
193 | |||
194 | util_strscpy(temp, sizeof(temp), string); | ||
195 | |||
196 | subsys = &temp[1]; | ||
197 | |||
198 | sysname = strchr(subsys, '/'); | ||
199 | if (sysname == NULL) | ||
200 | return -1; | ||
201 | sysname[0] = '\0'; | ||
202 | sysname = &sysname[1]; | ||
203 | |||
204 | attr = strchr(sysname, ']'); | ||
205 | if (attr == NULL) | ||
206 | return -1; | ||
207 | attr[0] = '\0'; | ||
208 | attr = &attr[1]; | ||
209 | if (attr[0] == '/') | ||
210 | attr = &attr[1]; | ||
211 | if (attr[0] == '\0') | ||
212 | attr = NULL; | ||
213 | |||
214 | if (read_value && attr == NULL) | ||
215 | return -1; | ||
216 | |||
217 | dev = udev_device_new_from_subsystem_sysname(udev, subsys, sysname); | ||
218 | if (dev == NULL) | ||
219 | return -1; | ||
220 | |||
221 | if (read_value) { | ||
222 | const char *val; | ||
223 | |||
224 | val = udev_device_get_sysattr_value(dev, attr); | ||
225 | if (val != NULL) | ||
226 | util_strscpy(result, maxsize, val); | ||
227 | else | ||
228 | result[0] = '\0'; | ||
229 | info(udev, "value '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result); | ||
230 | } else { | ||
231 | size_t l; | ||
232 | char *s; | ||
233 | |||
234 | s = result; | ||
235 | l = util_strpcpyl(&s, maxsize, udev_device_get_syspath(dev), NULL); | ||
236 | if (attr != NULL) | ||
237 | util_strpcpyl(&s, l, "/", attr, NULL); | ||
238 | info(udev, "path '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result); | ||
239 | } | ||
240 | udev_device_unref(dev); | ||
241 | return 0; | ||
242 | } |
File src/libudev-util.c added (mode: 100644) (index 0000000..b0ee33e) | |||
1 | /* | ||
2 | * libudev - interface to udev device information | ||
3 | * | ||
4 | * Copyright (C) 2008-2011 Kay Sievers <kay.sievers@vrfy.org> | ||
5 | * | ||
6 | * This library is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU Lesser General Public | ||
8 | * License as published by the Free Software Foundation; either | ||
9 | * version 2.1 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <stdio.h> | ||
13 | #include <stdlib.h> | ||
14 | #include <stddef.h> | ||
15 | #include <unistd.h> | ||
16 | #include <errno.h> | ||
17 | #include <string.h> | ||
18 | #include <dirent.h> | ||
19 | #include <ctype.h> | ||
20 | #include <fcntl.h> | ||
21 | #include <time.h> | ||
22 | #include <sys/stat.h> | ||
23 | |||
24 | #include "libudev.h" | ||
25 | #include "libudev-private.h" | ||
26 | |||
27 | /** | ||
28 | * SECTION:libudev-util | ||
29 | * @short_description: utils | ||
30 | */ | ||
31 | |||
32 | ssize_t util_get_sys_core_link_value(struct udev *udev, const char *slink, const char *syspath, char *value, size_t size) | ||
33 | { | ||
34 | char path[UTIL_PATH_SIZE]; | ||
35 | char target[UTIL_PATH_SIZE]; | ||
36 | ssize_t len; | ||
37 | const char *pos; | ||
38 | |||
39 | util_strscpyl(path, sizeof(path), syspath, "/", slink, NULL); | ||
40 | len = readlink(path, target, sizeof(target)); | ||
41 | if (len <= 0 || len == (ssize_t)sizeof(target)) | ||
42 | return -1; | ||
43 | target[len] = '\0'; | ||
44 | pos = strrchr(target, '/'); | ||
45 | if (pos == NULL) | ||
46 | return -1; | ||
47 | pos = &pos[1]; | ||
48 | dbg(udev, "resolved link to: '%s'\n", pos); | ||
49 | return util_strscpy(value, size, pos); | ||
50 | } | ||
51 | |||
52 | int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size) | ||
53 | { | ||
54 | char link_target[UTIL_PATH_SIZE]; | ||
55 | |||
56 | ssize_t len; | ||
57 | int i; | ||
58 | int back; | ||
59 | char *base = NULL; | ||
60 | |||
61 | len = readlink(syspath, link_target, sizeof(link_target)); | ||
62 | if (len <= 0 || len == (ssize_t)sizeof(link_target)) | ||
63 | return -1; | ||
64 | link_target[len] = '\0'; | ||
65 | dbg(udev, "path link '%s' points to '%s'\n", syspath, link_target); | ||
66 | |||
67 | for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++) | ||
68 | ; | ||
69 | dbg(udev, "base '%s', tail '%s', back %i\n", syspath, &link_target[back * 3], back); | ||
70 | for (i = 0; i <= back; i++) { | ||
71 | base = strrchr(syspath, '/'); | ||
72 | if (base == NULL) | ||
73 | return -EINVAL; | ||
74 | base[0] = '\0'; | ||
75 | } | ||
76 | if (base == NULL) | ||
77 | return -EINVAL; | ||
78 | dbg(udev, "after moving back '%s'\n", syspath); | ||
79 | util_strscpyl(base, size - (base - syspath), "/", &link_target[back * 3], NULL); | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | int util_log_priority(const char *priority) | ||
84 | { | ||
85 | char *endptr; | ||
86 | int prio; | ||
87 | |||
88 | prio = strtol(priority, &endptr, 10); | ||
89 | if (endptr[0] == '\0' || isspace(endptr[0])) | ||
90 | return prio; | ||
91 | if (strncmp(priority, "err", 3) == 0) | ||
92 | return LOG_ERR; | ||
93 | if (strncmp(priority, "info", 4) == 0) | ||
94 | return LOG_INFO; | ||
95 | if (strncmp(priority, "debug", 5) == 0) | ||
96 | return LOG_DEBUG; | ||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | size_t util_path_encode(const char *src, char *dest, size_t size) | ||
101 | { | ||
102 | size_t i, j; | ||
103 | |||
104 | for (i = 0, j = 0; src[i] != '\0'; i++) { | ||
105 | if (src[i] == '/') { | ||
106 | if (j+4 >= size) { | ||
107 | j = 0; | ||
108 | break; | ||
109 | } | ||
110 | memcpy(&dest[j], "\\x2f", 4); | ||
111 | j += 4; | ||
112 | } else if (src[i] == '\\') { | ||
113 | if (j+4 >= size) { | ||
114 | j = 0; | ||
115 | break; | ||
116 | } | ||
117 | memcpy(&dest[j], "\\x5c", 4); | ||
118 | j += 4; | ||
119 | } else { | ||
120 | if (j+1 >= size) { | ||
121 | j = 0; | ||
122 | break; | ||
123 | } | ||
124 | dest[j] = src[i]; | ||
125 | j++; | ||
126 | } | ||
127 | } | ||
128 | dest[j] = '\0'; | ||
129 | return j; | ||
130 | } | ||
131 | |||
132 | size_t util_path_decode(char *s) | ||
133 | { | ||
134 | size_t i, j; | ||
135 | |||
136 | for (i = 0, j = 0; s[i] != '\0'; j++) { | ||
137 | if (memcmp(&s[i], "\\x2f", 4) == 0) { | ||
138 | s[j] = '/'; | ||
139 | i += 4; | ||
140 | } else if (memcmp(&s[i], "\\x5c", 4) == 0) { | ||
141 | s[j] = '\\'; | ||
142 | i += 4; | ||
143 | } else { | ||
144 | s[j] = s[i]; | ||
145 | i++; | ||
146 | } | ||
147 | } | ||
148 | s[j] = '\0'; | ||
149 | return j; | ||
150 | } | ||
151 | |||
152 | void util_remove_trailing_chars(char *path, char c) | ||
153 | { | ||
154 | size_t len; | ||
155 | |||
156 | if (path == NULL) | ||
157 | return; | ||
158 | len = strlen(path); | ||
159 | while (len > 0 && path[len-1] == c) | ||
160 | path[--len] = '\0'; | ||
161 | } | ||
162 | |||
163 | /* | ||
164 | * Concatenates strings. In any case, terminates in _all_ cases with '\0' | ||
165 | * and moves the @dest pointer forward to the added '\0'. Returns the | ||
166 | * remaining size, and 0 if the string was truncated. | ||
167 | */ | ||
168 | size_t util_strpcpy(char **dest, size_t size, const char *src) | ||
169 | { | ||
170 | size_t len; | ||
171 | |||
172 | len = strlen(src); | ||
173 | if (len >= size) { | ||
174 | if (size > 1) | ||
175 | *dest = mempcpy(*dest, src, size-1); | ||
176 | size = 0; | ||
177 | *dest[0] = '\0'; | ||
178 | } else { | ||
179 | if (len > 0) { | ||
180 | *dest = mempcpy(*dest, src, len); | ||
181 | size -= len; | ||
182 | } | ||
183 | *dest[0] = '\0'; | ||
184 | } | ||
185 | return size; | ||
186 | } | ||
187 | |||
188 | /* concatenates list of strings, moves dest forward */ | ||
189 | size_t util_strpcpyl(char **dest, size_t size, const char *src, ...) | ||
190 | { | ||
191 | va_list va; | ||
192 | |||
193 | va_start(va, src); | ||
194 | do { | ||
195 | size = util_strpcpy(dest, size, src); | ||
196 | src = va_arg(va, char *); | ||
197 | } while (src != NULL); | ||
198 | va_end(va); | ||
199 | |||
200 | return size; | ||
201 | } | ||
202 | |||
203 | /* copies string */ | ||
204 | size_t util_strscpy(char *dest, size_t size, const char *src) | ||
205 | { | ||
206 | char *s; | ||
207 | |||
208 | s = dest; | ||
209 | return util_strpcpy(&s, size, src); | ||
210 | } | ||
211 | |||
212 | /* concatenates list of strings */ | ||
213 | size_t util_strscpyl(char *dest, size_t size, const char *src, ...) | ||
214 | { | ||
215 | va_list va; | ||
216 | char *s; | ||
217 | |||
218 | va_start(va, src); | ||
219 | s = dest; | ||
220 | do { | ||
221 | size = util_strpcpy(&s, size, src); | ||
222 | src = va_arg(va, char *); | ||
223 | } while (src != NULL); | ||
224 | va_end(va); | ||
225 | |||
226 | return size; | ||
227 | } | ||
228 | |||
229 | /* count of characters used to encode one unicode char */ | ||
230 | static int utf8_encoded_expected_len(const char *str) | ||
231 | { | ||
232 | unsigned char c = (unsigned char)str[0]; | ||
233 | |||
234 | if (c < 0x80) | ||
235 | return 1; | ||
236 | if ((c & 0xe0) == 0xc0) | ||
237 | return 2; | ||
238 | if ((c & 0xf0) == 0xe0) | ||
239 | return 3; | ||
240 | if ((c & 0xf8) == 0xf0) | ||
241 | return 4; | ||
242 | if ((c & 0xfc) == 0xf8) | ||
243 | return 5; | ||
244 | if ((c & 0xfe) == 0xfc) | ||
245 | return 6; | ||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | /* decode one unicode char */ | ||
250 | static int utf8_encoded_to_unichar(const char *str) | ||
251 | { | ||
252 | int unichar; | ||
253 | int len; | ||
254 | int i; | ||
255 | |||
256 | len = utf8_encoded_expected_len(str); | ||
257 | switch (len) { | ||
258 | case 1: | ||
259 | return (int)str[0]; | ||
260 | case 2: | ||
261 | unichar = str[0] & 0x1f; | ||
262 | break; | ||
263 | case 3: | ||
264 | unichar = (int)str[0] & 0x0f; | ||
265 | break; | ||
266 | case 4: | ||
267 | unichar = (int)str[0] & 0x07; | ||
268 | break; | ||
269 | case 5: | ||
270 | unichar = (int)str[0] & 0x03; | ||
271 | break; | ||
272 | case 6: | ||
273 | unichar = (int)str[0] & 0x01; | ||
274 | break; | ||
275 | default: | ||
276 | return -1; | ||
277 | } | ||
278 | |||
279 | for (i = 1; i < len; i++) { | ||
280 | if (((int)str[i] & 0xc0) != 0x80) | ||
281 | return -1; | ||
282 | unichar <<= 6; | ||
283 | unichar |= (int)str[i] & 0x3f; | ||
284 | } | ||
285 | |||
286 | return unichar; | ||
287 | } | ||
288 | |||
289 | /* expected size used to encode one unicode char */ | ||
290 | static int utf8_unichar_to_encoded_len(int unichar) | ||
291 | { | ||
292 | if (unichar < 0x80) | ||
293 | return 1; | ||
294 | if (unichar < 0x800) | ||
295 | return 2; | ||
296 | if (unichar < 0x10000) | ||
297 | return 3; | ||
298 | if (unichar < 0x200000) | ||
299 | return 4; | ||
300 | if (unichar < 0x4000000) | ||
301 | return 5; | ||
302 | return 6; | ||
303 | } | ||
304 | |||
305 | /* check if unicode char has a valid numeric range */ | ||
306 | static int utf8_unichar_valid_range(int unichar) | ||
307 | { | ||
308 | if (unichar > 0x10ffff) | ||
309 | return 0; | ||
310 | if ((unichar & 0xfffff800) == 0xd800) | ||
311 | return 0; | ||
312 | if ((unichar > 0xfdcf) && (unichar < 0xfdf0)) | ||
313 | return 0; | ||
314 | if ((unichar & 0xffff) == 0xffff) | ||
315 | return 0; | ||
316 | return 1; | ||
317 | } | ||
318 | |||
319 | /* validate one encoded unicode char and return its length */ | ||
320 | static int utf8_encoded_valid_unichar(const char *str) | ||
321 | { | ||
322 | int len; | ||
323 | int unichar; | ||
324 | int i; | ||
325 | |||
326 | len = utf8_encoded_expected_len(str); | ||
327 | if (len == 0) | ||
328 | return -1; | ||
329 | |||
330 | /* ascii is valid */ | ||
331 | if (len == 1) | ||
332 | return 1; | ||
333 | |||
334 | /* check if expected encoded chars are available */ | ||
335 | for (i = 0; i < len; i++) | ||
336 | if ((str[i] & 0x80) != 0x80) | ||
337 | return -1; | ||
338 | |||
339 | unichar = utf8_encoded_to_unichar(str); | ||
340 | |||
341 | /* check if encoded length matches encoded value */ | ||
342 | if (utf8_unichar_to_encoded_len(unichar) != len) | ||
343 | return -1; | ||
344 | |||
345 | /* check if value has valid range */ | ||
346 | if (!utf8_unichar_valid_range(unichar)) | ||
347 | return -1; | ||
348 | |||
349 | return len; | ||
350 | } | ||
351 | |||
352 | int util_replace_whitespace(const char *str, char *to, size_t len) | ||
353 | { | ||
354 | size_t i, j; | ||
355 | |||
356 | /* strip trailing whitespace */ | ||
357 | len = strnlen(str, len); | ||
358 | while (len && isspace(str[len-1])) | ||
359 | len--; | ||
360 | |||
361 | /* strip leading whitespace */ | ||
362 | i = 0; | ||
363 | while (isspace(str[i]) && (i < len)) | ||
364 | i++; | ||
365 | |||
366 | j = 0; | ||
367 | while (i < len) { | ||
368 | /* substitute multiple whitespace with a single '_' */ | ||
369 | if (isspace(str[i])) { | ||
370 | while (isspace(str[i])) | ||
371 | i++; | ||
372 | to[j++] = '_'; | ||
373 | } | ||
374 | to[j++] = str[i++]; | ||
375 | } | ||
376 | to[j] = '\0'; | ||
377 | return 0; | ||
378 | } | ||
379 | |||
380 | static int is_whitelisted(char c, const char *white) | ||
381 | { | ||
382 | if ((c >= '0' && c <= '9') || | ||
383 | (c >= 'A' && c <= 'Z') || | ||
384 | (c >= 'a' && c <= 'z') || | ||
385 | strchr("#+-.:=@_", c) != NULL || | ||
386 | (white != NULL && strchr(white, c) != NULL)) | ||
387 | return 1; | ||
388 | return 0; | ||
389 | } | ||
390 | |||
391 | /* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */ | ||
392 | int util_replace_chars(char *str, const char *white) | ||
393 | { | ||
394 | size_t i = 0; | ||
395 | int replaced = 0; | ||
396 | |||
397 | while (str[i] != '\0') { | ||
398 | int len; | ||
399 | |||
400 | if (is_whitelisted(str[i], white)) { | ||
401 | i++; | ||
402 | continue; | ||
403 | } | ||
404 | |||
405 | /* accept hex encoding */ | ||
406 | if (str[i] == '\\' && str[i+1] == 'x') { | ||
407 | i += 2; | ||
408 | continue; | ||
409 | } | ||
410 | |||
411 | /* accept valid utf8 */ | ||
412 | len = utf8_encoded_valid_unichar(&str[i]); | ||
413 | if (len > 1) { | ||
414 | i += len; | ||
415 | continue; | ||
416 | } | ||
417 | |||
418 | /* if space is allowed, replace whitespace with ordinary space */ | ||
419 | if (isspace(str[i]) && white != NULL && strchr(white, ' ') != NULL) { | ||
420 | str[i] = ' '; | ||
421 | i++; | ||
422 | replaced++; | ||
423 | continue; | ||
424 | } | ||
425 | |||
426 | /* everything else is replaced with '_' */ | ||
427 | str[i] = '_'; | ||
428 | i++; | ||
429 | replaced++; | ||
430 | } | ||
431 | return replaced; | ||
432 | } | ||
433 | |||
434 | /** | ||
435 | * udev_util_encode_string: | ||
436 | * @str: input string to be encoded | ||
437 | * @str_enc: output string to store the encoded input string | ||
438 | * @len: maximum size of the output string, which may be | ||
439 | * four times as long as the input string | ||
440 | * | ||
441 | * Encode all potentially unsafe characters of a string to the | ||
442 | * corresponding 2 char hex value prefixed by '\x'. | ||
443 | * | ||
444 | * Returns: 0 if the entire string was copied, non-zero otherwise. | ||
445 | **/ | ||
446 | UDEV_EXPORT int udev_util_encode_string(const char *str, char *str_enc, size_t len) | ||
447 | { | ||
448 | size_t i, j; | ||
449 | |||
450 | if (str == NULL || str_enc == NULL) | ||
451 | return -1; | ||
452 | |||
453 | for (i = 0, j = 0; str[i] != '\0'; i++) { | ||
454 | int seqlen; | ||
455 | |||
456 | seqlen = utf8_encoded_valid_unichar(&str[i]); | ||
457 | if (seqlen > 1) { | ||
458 | if (len-j < (size_t)seqlen) | ||
459 | goto err; | ||
460 | memcpy(&str_enc[j], &str[i], seqlen); | ||
461 | j += seqlen; | ||
462 | i += (seqlen-1); | ||
463 | } else if (str[i] == '\\' || !is_whitelisted(str[i], NULL)) { | ||
464 | if (len-j < 4) | ||
465 | goto err; | ||
466 | sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]); | ||
467 | j += 4; | ||
468 | } else { | ||
469 | if (len-j < 1) | ||
470 | goto err; | ||
471 | str_enc[j] = str[i]; | ||
472 | j++; | ||
473 | } | ||
474 | } | ||
475 | if (len-j < 1) | ||
476 | goto err; | ||
477 | str_enc[j] = '\0'; | ||
478 | return 0; | ||
479 | err: | ||
480 | return -1; | ||
481 | } | ||
482 | |||
483 | /* | ||
484 | * http://sites.google.com/site/murmurhash/ | ||
485 | * | ||
486 | * All code is released to the public domain. For business purposes, | ||
487 | * Murmurhash is under the MIT license. | ||
488 | * | ||
489 | */ | ||
490 | static unsigned int murmur_hash2(const char *key, int len, unsigned int seed) | ||
491 | { | ||
492 | /* | ||
493 | * 'm' and 'r' are mixing constants generated offline. | ||
494 | * They're not really 'magic', they just happen to work well. | ||
495 | */ | ||
496 | const unsigned int m = 0x5bd1e995; | ||
497 | const int r = 24; | ||
498 | |||
499 | /* initialize the hash to a 'random' value */ | ||
500 | unsigned int h = seed ^ len; | ||
501 | |||
502 | /* mix 4 bytes at a time into the hash */ | ||
503 | const unsigned char * data = (const unsigned char *)key; | ||
504 | |||
505 | while(len >= 4) { | ||
506 | unsigned int k = *(unsigned int *)data; | ||
507 | |||
508 | k *= m; | ||
509 | k ^= k >> r; | ||
510 | k *= m; | ||
511 | h *= m; | ||
512 | h ^= k; | ||
513 | |||
514 | data += 4; | ||
515 | len -= 4; | ||
516 | } | ||
517 | |||
518 | /* handle the last few bytes of the input array */ | ||
519 | switch(len) { | ||
520 | case 3: | ||
521 | h ^= data[2] << 16; | ||
522 | case 2: | ||
523 | h ^= data[1] << 8; | ||
524 | case 1: | ||
525 | h ^= data[0]; | ||
526 | h *= m; | ||
527 | }; | ||
528 | |||
529 | /* do a few final mixes of the hash to ensure the last few bytes are well-incorporated */ | ||
530 | h ^= h >> 13; | ||
531 | h *= m; | ||
532 | h ^= h >> 15; | ||
533 | |||
534 | return h; | ||
535 | } | ||
536 | |||
537 | unsigned int util_string_hash32(const char *str) | ||
538 | { | ||
539 | return murmur_hash2(str, strlen(str), 0); | ||
540 | } | ||
541 | |||
542 | /* get a bunch of bit numbers out of the hash, and set the bits in our bit field */ | ||
543 | uint64_t util_string_bloom64(const char *str) | ||
544 | { | ||
545 | uint64_t bits = 0; | ||
546 | unsigned int hash = util_string_hash32(str); | ||
547 | |||
548 | bits |= 1LLU << (hash & 63); | ||
549 | bits |= 1LLU << ((hash >> 6) & 63); | ||
550 | bits |= 1LLU << ((hash >> 12) & 63); | ||
551 | bits |= 1LLU << ((hash >> 18) & 63); | ||
552 | return bits; | ||
553 | } | ||
554 | |||
555 | #define USEC_PER_SEC 1000000ULL | ||
556 | #define NSEC_PER_USEC 1000ULL | ||
557 | unsigned long long ts_usec(const struct timespec *ts) | ||
558 | { | ||
559 | return (unsigned long long) ts->tv_sec * USEC_PER_SEC + | ||
560 | (unsigned long long) ts->tv_nsec / NSEC_PER_USEC; | ||
561 | } | ||
562 | |||
563 | unsigned long long now_usec(void) | ||
564 | { | ||
565 | struct timespec ts; | ||
566 | |||
567 | if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) | ||
568 | return 0; | ||
569 | return ts_usec(&ts); | ||
570 | } | ||
571 | |||
572 | ssize_t print_kmsg(const char *fmt, ...) | ||
573 | { | ||
574 | int fd; | ||
575 | va_list ap; | ||
576 | char text[1024]; | ||
577 | ssize_t len; | ||
578 | ssize_t ret; | ||
579 | |||
580 | fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC); | ||
581 | if (fd < 0) | ||
582 | return -errno; | ||
583 | |||
584 | len = snprintf(text, sizeof(text), "<30>udevd[%u]: ", getpid()); | ||
585 | |||
586 | va_start(ap, fmt); | ||
587 | len += vsnprintf(text + len, sizeof(text) - len, fmt, ap); | ||
588 | va_end(ap); | ||
589 | |||
590 | ret = write(fd, text, len); | ||
591 | if (ret < 0) | ||
592 | ret = -errno; | ||
593 | close(fd); | ||
594 | return ret; | ||
595 | } |
File src/libudev.c added (mode: 100644) (index 0000000..9443a88) | |||
1 | /* | ||
2 | * libudev - interface to udev device information | ||
3 | * | ||
4 | * Copyright (C) 2008-2010 Kay Sievers <kay.sievers@vrfy.org> | ||
5 | * | ||
6 | * This library is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU Lesser General Public | ||
8 | * License as published by the Free Software Foundation; either | ||
9 | * version 2.1 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <stdio.h> | ||
13 | #include <stdlib.h> | ||
14 | #include <stddef.h> | ||
15 | #include <stdarg.h> | ||
16 | #include <unistd.h> | ||
17 | #include <errno.h> | ||
18 | #include <string.h> | ||
19 | #include <ctype.h> | ||
20 | #include <time.h> | ||
21 | |||
22 | #include "libudev.h" | ||
23 | #include "libudev-private.h" | ||
24 | |||
25 | /** | ||
26 | * SECTION:libudev | ||
27 | * @short_description: libudev context | ||
28 | * | ||
29 | * The context contains the default values read from the udev config file, | ||
30 | * and is passed to all library operations. | ||
31 | */ | ||
32 | |||
33 | /** | ||
34 | * udev: | ||
35 | * | ||
36 | * Opaque object representing the library context. | ||
37 | */ | ||
38 | struct udev { | ||
39 | int refcount; | ||
40 | void (*log_fn)(struct udev *udev, | ||
41 | int priority, const char *file, int line, const char *fn, | ||
42 | const char *format, va_list args); | ||
43 | void *userdata; | ||
44 | char *sys_path; | ||
45 | char *dev_path; | ||
46 | char *rules_path[4]; | ||
47 | unsigned long long rules_path_ts[4]; | ||
48 | int rules_path_count; | ||
49 | char *run_path; | ||
50 | struct udev_list properties_list; | ||
51 | int log_priority; | ||
52 | }; | ||
53 | |||
54 | void udev_log(struct udev *udev, | ||
55 | int priority, const char *file, int line, const char *fn, | ||
56 | const char *format, ...) | ||
57 | { | ||
58 | va_list args; | ||
59 | |||
60 | va_start(args, format); | ||
61 | udev->log_fn(udev, priority, file, line, fn, format, args); | ||
62 | va_end(args); | ||
63 | } | ||
64 | |||
65 | static void log_stderr(struct udev *udev, | ||
66 | int priority, const char *file, int line, const char *fn, | ||
67 | const char *format, va_list args) | ||
68 | { | ||
69 | fprintf(stderr, "libudev: %s: ", fn); | ||
70 | vfprintf(stderr, format, args); | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * udev_get_userdata: | ||
75 | * @udev: udev library context | ||
76 | * | ||
77 | * Retrieve stored data pointer from library context. This might be useful | ||
78 | * to access from callbacks like a custom logging function. | ||
79 | * | ||
80 | * Returns: stored userdata | ||
81 | **/ | ||
82 | UDEV_EXPORT void *udev_get_userdata(struct udev *udev) | ||
83 | { | ||
84 | if (udev == NULL) | ||
85 | return NULL; | ||
86 | return udev->userdata; | ||
87 | } | ||
88 | |||
89 | /** | ||
90 | * udev_set_userdata: | ||
91 | * @udev: udev library context | ||
92 | * @userdata: data pointer | ||
93 | * | ||
94 | * Store custom @userdata in the library context. | ||
95 | **/ | ||
96 | UDEV_EXPORT void udev_set_userdata(struct udev *udev, void *userdata) | ||
97 | { | ||
98 | if (udev == NULL) | ||
99 | return; | ||
100 | udev->userdata = userdata; | ||
101 | } | ||
102 | |||
103 | static char *set_value(char **s, const char *v) | ||
104 | { | ||
105 | free(*s); | ||
106 | *s = strdup(v); | ||
107 | util_remove_trailing_chars(*s, '/'); | ||
108 | return *s; | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * udev_new: | ||
113 | * | ||
114 | * Create udev library context. This reads the udev configuration | ||
115 | * file, and fills in the default values. | ||
116 | * | ||
117 | * The initial refcount is 1, and needs to be decremented to | ||
118 | * release the resources of the udev library context. | ||
119 | * | ||
120 | * Returns: a new udev library context | ||
121 | **/ | ||
122 | UDEV_EXPORT struct udev *udev_new(void) | ||
123 | { | ||
124 | struct udev *udev; | ||
125 | const char *env; | ||
126 | char *config_file = NULL; | ||
127 | FILE *f; | ||
128 | |||
129 | udev = calloc(1, sizeof(struct udev)); | ||
130 | if (udev == NULL) | ||
131 | return NULL; | ||
132 | udev->refcount = 1; | ||
133 | udev->log_fn = log_stderr; | ||
134 | udev->log_priority = LOG_ERR; | ||
135 | udev_list_init(udev, &udev->properties_list, true); | ||
136 | |||
137 | /* custom config file */ | ||
138 | env = getenv("UDEV_CONFIG_FILE"); | ||
139 | if (env != NULL) { | ||
140 | if (set_value(&config_file, env) == NULL) | ||
141 | goto err; | ||
142 | udev_add_property(udev, "UDEV_CONFIG_FILE", config_file); | ||
143 | } | ||
144 | |||
145 | /* default config file */ | ||
146 | if (config_file == NULL) | ||
147 | config_file = strdup(SYSCONFDIR "/udev/udev.conf"); | ||
148 | if (config_file == NULL) | ||
149 | goto err; | ||
150 | |||
151 | f = fopen(config_file, "re"); | ||
152 | if (f != NULL) { | ||
153 | char line[UTIL_LINE_SIZE]; | ||
154 | int line_nr = 0; | ||
155 | |||
156 | while (fgets(line, sizeof(line), f)) { | ||
157 | size_t len; | ||
158 | char *key; | ||
159 | char *val; | ||
160 | |||
161 | line_nr++; | ||
162 | |||
163 | /* find key */ | ||
164 | key = line; | ||
165 | while (isspace(key[0])) | ||
166 | key++; | ||
167 | |||
168 | /* comment or empty line */ | ||
169 | if (key[0] == '#' || key[0] == '\0') | ||
170 | continue; | ||
171 | |||
172 | /* split key/value */ | ||
173 | val = strchr(key, '='); | ||
174 | if (val == NULL) { | ||
175 | err(udev, "missing <key>=<value> in '%s'[%i], skip line\n", config_file, line_nr); | ||
176 | continue; | ||
177 | } | ||
178 | val[0] = '\0'; | ||
179 | val++; | ||
180 | |||
181 | /* find value */ | ||
182 | while (isspace(val[0])) | ||
183 | val++; | ||
184 | |||
185 | /* terminate key */ | ||
186 | len = strlen(key); | ||
187 | if (len == 0) | ||
188 | continue; | ||
189 | while (isspace(key[len-1])) | ||
190 | len--; | ||
191 | key[len] = '\0'; | ||
192 | |||
193 | /* terminate value */ | ||
194 | len = strlen(val); | ||
195 | if (len == 0) | ||
196 | continue; | ||
197 | while (isspace(val[len-1])) | ||
198 | len--; | ||
199 | val[len] = '\0'; | ||
200 | |||
201 | if (len == 0) | ||
202 | continue; | ||
203 | |||
204 | /* unquote */ | ||
205 | if (val[0] == '"' || val[0] == '\'') { | ||
206 | if (val[len-1] != val[0]) { | ||
207 | err(udev, "inconsistent quoting in '%s'[%i], skip line\n", config_file, line_nr); | ||
208 | continue; | ||
209 | } | ||
210 | val[len-1] = '\0'; | ||
211 | val++; | ||
212 | } | ||
213 | |||
214 | if (strcmp(key, "udev_log") == 0) { | ||
215 | udev_set_log_priority(udev, util_log_priority(val)); | ||
216 | continue; | ||
217 | } | ||
218 | if (strcmp(key, "udev_root") == 0) { | ||
219 | set_value(&udev->dev_path, val); | ||
220 | continue; | ||
221 | } | ||
222 | if (strcmp(key, "udev_run") == 0) { | ||
223 | set_value(&udev->run_path, val); | ||
224 | continue; | ||
225 | } | ||
226 | if (strcmp(key, "udev_sys") == 0) { | ||
227 | set_value(&udev->sys_path, val); | ||
228 | continue; | ||
229 | } | ||
230 | if (strcmp(key, "udev_rules") == 0) { | ||
231 | set_value(&udev->rules_path[0], val); | ||
232 | udev->rules_path_count = 1; | ||
233 | continue; | ||
234 | } | ||
235 | } | ||
236 | fclose(f); | ||
237 | } | ||
238 | |||
239 | /* environment overrides config */ | ||
240 | env = getenv("UDEV_LOG"); | ||
241 | if (env != NULL) | ||
242 | udev_set_log_priority(udev, util_log_priority(env)); | ||
243 | |||
244 | /* set defaults */ | ||
245 | if (udev->dev_path == NULL) | ||
246 | if (set_value(&udev->dev_path, "/dev") == NULL) | ||
247 | goto err; | ||
248 | |||
249 | if (udev->sys_path == NULL) | ||
250 | if (set_value(&udev->sys_path, "/sys") == NULL) | ||
251 | goto err; | ||
252 | |||
253 | if (udev->run_path == NULL) | ||
254 | if (set_value(&udev->run_path, "/run/udev") == NULL) | ||
255 | goto err; | ||
256 | |||
257 | if (udev->rules_path[0] == NULL) { | ||
258 | /* /usr/lib/udev -- system rules */ | ||
259 | udev->rules_path[0] = strdup(PKGLIBEXECDIR "/rules.d"); | ||
260 | if (!udev->rules_path[0]) | ||
261 | goto err; | ||
262 | |||
263 | /* /run/udev -- runtime rules */ | ||
264 | if (asprintf(&udev->rules_path[1], "%s/rules.d", udev->run_path) < 0) | ||
265 | goto err; | ||
266 | |||
267 | /* /etc/udev -- local administration rules */ | ||
268 | udev->rules_path[2] = strdup(SYSCONFDIR "/udev/rules.d"); | ||
269 | if (!udev->rules_path[2]) | ||
270 | goto err; | ||
271 | |||
272 | udev->rules_path_count = 3; | ||
273 | } | ||
274 | |||
275 | dbg(udev, "context %p created\n", udev); | ||
276 | dbg(udev, "log_priority=%d\n", udev->log_priority); | ||
277 | dbg(udev, "config_file='%s'\n", config_file); | ||
278 | dbg(udev, "dev_path='%s'\n", udev->dev_path); | ||
279 | dbg(udev, "sys_path='%s'\n", udev->sys_path); | ||
280 | dbg(udev, "run_path='%s'\n", udev->run_path); | ||
281 | dbg(udev, "rules_path='%s':'%s':'%s'\n", udev->rules_path[0], udev->rules_path[1], udev->rules_path[2]); | ||
282 | free(config_file); | ||
283 | return udev; | ||
284 | err: | ||
285 | free(config_file); | ||
286 | err(udev, "context creation failed\n"); | ||
287 | udev_unref(udev); | ||
288 | return NULL; | ||
289 | } | ||
290 | |||
291 | /** | ||
292 | * udev_ref: | ||
293 | * @udev: udev library context | ||
294 | * | ||
295 | * Take a reference of the udev library context. | ||
296 | * | ||
297 | * Returns: the passed udev library context | ||
298 | **/ | ||
299 | UDEV_EXPORT struct udev *udev_ref(struct udev *udev) | ||
300 | { | ||
301 | if (udev == NULL) | ||
302 | return NULL; | ||
303 | udev->refcount++; | ||
304 | return udev; | ||
305 | } | ||
306 | |||
307 | /** | ||
308 | * udev_unref: | ||
309 | * @udev: udev library context | ||
310 | * | ||
311 | * Drop a reference of the udev library context. If the refcount | ||
312 | * reaches zero, the resources of the context will be released. | ||
313 | * | ||
314 | **/ | ||
315 | UDEV_EXPORT struct udev *udev_unref(struct udev *udev) | ||
316 | { | ||
317 | if (udev == NULL) | ||
318 | return NULL; | ||
319 | udev->refcount--; | ||
320 | if (udev->refcount > 0) | ||
321 | return udev; | ||
322 | udev_list_cleanup(&udev->properties_list); | ||
323 | free(udev->dev_path); | ||
324 | free(udev->sys_path); | ||
325 | free(udev->rules_path[0]); | ||
326 | free(udev->rules_path[1]); | ||
327 | free(udev->rules_path[2]); | ||
328 | free(udev->run_path); | ||
329 | dbg(udev, "context %p released\n", udev); | ||
330 | free(udev); | ||
331 | return NULL; | ||
332 | } | ||
333 | |||
334 | /** | ||
335 | * udev_set_log_fn: | ||
336 | * @udev: udev library context | ||
337 | * @log_fn: function to be called for logging messages | ||
338 | * | ||
339 | * The built-in logging writes to stderr. It can be | ||
340 | * overridden by a custom function, to plug log messages | ||
341 | * into the users' logging functionality. | ||
342 | * | ||
343 | **/ | ||
344 | UDEV_EXPORT void udev_set_log_fn(struct udev *udev, | ||
345 | void (*log_fn)(struct udev *udev, | ||
346 | int priority, const char *file, int line, const char *fn, | ||
347 | const char *format, va_list args)) | ||
348 | { | ||
349 | udev->log_fn = log_fn; | ||
350 | info(udev, "custom logging function %p registered\n", log_fn); | ||
351 | } | ||
352 | |||
353 | /** | ||
354 | * udev_get_log_priority: | ||
355 | * @udev: udev library context | ||
356 | * | ||
357 | * The initial logging priority is read from the udev config file | ||
358 | * at startup. | ||
359 | * | ||
360 | * Returns: the current logging priority | ||
361 | **/ | ||
362 | UDEV_EXPORT int udev_get_log_priority(struct udev *udev) | ||
363 | { | ||
364 | return udev->log_priority; | ||
365 | } | ||
366 | |||
367 | /** | ||
368 | * udev_set_log_priority: | ||
369 | * @udev: udev library context | ||
370 | * @priority: the new logging priority | ||
371 | * | ||
372 | * Set the current logging priority. The value controls which messages | ||
373 | * are logged. | ||
374 | **/ | ||
375 | UDEV_EXPORT void udev_set_log_priority(struct udev *udev, int priority) | ||
376 | { | ||
377 | char num[32]; | ||
378 | |||
379 | udev->log_priority = priority; | ||
380 | snprintf(num, sizeof(num), "%u", udev->log_priority); | ||
381 | udev_add_property(udev, "UDEV_LOG", num); | ||
382 | } | ||
383 | |||
384 | int udev_get_rules_path(struct udev *udev, char **path[], unsigned long long *stamp_usec[]) | ||
385 | { | ||
386 | *path = udev->rules_path; | ||
387 | if (stamp_usec) | ||
388 | *stamp_usec = udev->rules_path_ts; | ||
389 | return udev->rules_path_count; | ||
390 | } | ||
391 | |||
392 | /** | ||
393 | * udev_get_sys_path: | ||
394 | * @udev: udev library context | ||
395 | * | ||
396 | * Retrieve the sysfs mount point. The default is "/sys". For | ||
397 | * testing purposes, it can be overridden with udev_sys= | ||
398 | * in the udev configuration file. | ||
399 | * | ||
400 | * Returns: the sys mount point | ||
401 | **/ | ||
402 | UDEV_EXPORT const char *udev_get_sys_path(struct udev *udev) | ||
403 | { | ||
404 | if (udev == NULL) | ||
405 | return NULL; | ||
406 | return udev->sys_path; | ||
407 | } | ||
408 | |||
409 | /** | ||
410 | * udev_get_dev_path: | ||
411 | * @udev: udev library context | ||
412 | * | ||
413 | * Retrieve the device directory path. The default value is "/dev", | ||
414 | * the actual value may be overridden in the udev configuration | ||
415 | * file. | ||
416 | * | ||
417 | * Returns: the device directory path | ||
418 | **/ | ||
419 | UDEV_EXPORT const char *udev_get_dev_path(struct udev *udev) | ||
420 | { | ||
421 | if (udev == NULL) | ||
422 | return NULL; | ||
423 | return udev->dev_path; | ||
424 | } | ||
425 | |||
426 | /** | ||
427 | * udev_get_run_path: | ||
428 | * @udev: udev library context | ||
429 | * | ||
430 | * Retrieve the udev runtime directory path. The default is "/run/udev". | ||
431 | * | ||
432 | * Returns: the runtime directory path | ||
433 | **/ | ||
434 | UDEV_EXPORT const char *udev_get_run_path(struct udev *udev) | ||
435 | { | ||
436 | if (udev == NULL) | ||
437 | return NULL; | ||
438 | return udev->run_path; | ||
439 | } | ||
440 | |||
441 | struct udev_list_entry *udev_add_property(struct udev *udev, const char *key, const char *value) | ||
442 | { | ||
443 | if (value == NULL) { | ||
444 | struct udev_list_entry *list_entry; | ||
445 | |||
446 | list_entry = udev_get_properties_list_entry(udev); | ||
447 | list_entry = udev_list_entry_get_by_name(list_entry, key); | ||
448 | if (list_entry != NULL) | ||
449 | udev_list_entry_delete(list_entry); | ||
450 | return NULL; | ||
451 | } | ||
452 | return udev_list_entry_add(&udev->properties_list, key, value); | ||
453 | } | ||
454 | |||
455 | struct udev_list_entry *udev_get_properties_list_entry(struct udev *udev) | ||
456 | { | ||
457 | return udev_list_get_entry(&udev->properties_list); | ||
458 | } |
File src/libudev.h added (mode: 100644) (index 0000000..dc75133) | |||
1 | /* | ||
2 | * libudev - interface to udev device information | ||
3 | * | ||
4 | * Copyright (C) 2008-2011 Kay Sievers <kay.sievers@vrfy.org> | ||
5 | * | ||
6 | * This library is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU Lesser General Public | ||
8 | * License as published by the Free Software Foundation; either | ||
9 | * version 2.1 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #ifndef _LIBUDEV_H_ | ||
13 | #define _LIBUDEV_H_ | ||
14 | |||
15 | #include <stdarg.h> | ||
16 | #include <sys/types.h> | ||
17 | #include <sys/stat.h> | ||
18 | |||
19 | #ifdef __cplusplus | ||
20 | extern "C" { | ||
21 | #endif | ||
22 | |||
23 | /* | ||
24 | * udev - library context | ||
25 | * | ||
26 | * reads the udev config and system environment | ||
27 | * allows custom logging | ||
28 | */ | ||
29 | struct udev; | ||
30 | struct udev *udev_ref(struct udev *udev); | ||
31 | struct udev *udev_unref(struct udev *udev); | ||
32 | struct udev *udev_new(void); | ||
33 | void udev_set_log_fn(struct udev *udev, | ||
34 | void (*log_fn)(struct udev *udev, | ||
35 | int priority, const char *file, int line, const char *fn, | ||
36 | const char *format, va_list args)); | ||
37 | int udev_get_log_priority(struct udev *udev); | ||
38 | void udev_set_log_priority(struct udev *udev, int priority); | ||
39 | const char *udev_get_sys_path(struct udev *udev); | ||
40 | const char *udev_get_dev_path(struct udev *udev); | ||
41 | const char *udev_get_run_path(struct udev *udev); | ||
42 | void *udev_get_userdata(struct udev *udev); | ||
43 | void udev_set_userdata(struct udev *udev, void *userdata); | ||
44 | |||
45 | /* | ||
46 | * udev_list | ||
47 | * | ||
48 | * access to libudev generated lists | ||
49 | */ | ||
50 | struct udev_list_entry; | ||
51 | struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry); | ||
52 | struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name); | ||
53 | const char *udev_list_entry_get_name(struct udev_list_entry *list_entry); | ||
54 | const char *udev_list_entry_get_value(struct udev_list_entry *list_entry); | ||
55 | /** | ||
56 | * udev_list_entry_foreach: | ||
57 | * @list_entry: entry to store the current position | ||
58 | * @first_entry: first entry to start with | ||
59 | * | ||
60 | * Helper to iterate over all entries of a list. | ||
61 | */ | ||
62 | #define udev_list_entry_foreach(list_entry, first_entry) \ | ||
63 | for (list_entry = first_entry; \ | ||
64 | list_entry != NULL; \ | ||
65 | list_entry = udev_list_entry_get_next(list_entry)) | ||
66 | |||
67 | /* | ||
68 | * udev_device | ||
69 | * | ||
70 | * access to sysfs/kernel devices | ||
71 | */ | ||
72 | struct udev_device; | ||
73 | struct udev_device *udev_device_ref(struct udev_device *udev_device); | ||
74 | struct udev_device *udev_device_unref(struct udev_device *udev_device); | ||
75 | struct udev *udev_device_get_udev(struct udev_device *udev_device); | ||
76 | struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath); | ||
77 | struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum); | ||
78 | struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname); | ||
79 | struct udev_device *udev_device_new_from_device_id(struct udev *udev, char *id); | ||
80 | struct udev_device *udev_device_new_from_environment(struct udev *udev); | ||
81 | /* udev_device_get_parent_*() does not take a reference on the returned device, it is automatically unref'd with the parent */ | ||
82 | struct udev_device *udev_device_get_parent(struct udev_device *udev_device); | ||
83 | struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, | ||
84 | const char *subsystem, const char *devtype); | ||
85 | /* retrieve device properties */ | ||
86 | const char *udev_device_get_devpath(struct udev_device *udev_device); | ||
87 | const char *udev_device_get_subsystem(struct udev_device *udev_device); | ||
88 | const char *udev_device_get_devtype(struct udev_device *udev_device); | ||
89 | const char *udev_device_get_syspath(struct udev_device *udev_device); | ||
90 | const char *udev_device_get_sysname(struct udev_device *udev_device); | ||
91 | const char *udev_device_get_sysnum(struct udev_device *udev_device); | ||
92 | const char *udev_device_get_devnode(struct udev_device *udev_device); | ||
93 | int udev_device_get_is_initialized(struct udev_device *udev_device); | ||
94 | struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device); | ||
95 | struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device); | ||
96 | struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device); | ||
97 | struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device); | ||
98 | const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key); | ||
99 | const char *udev_device_get_driver(struct udev_device *udev_device); | ||
100 | dev_t udev_device_get_devnum(struct udev_device *udev_device); | ||
101 | const char *udev_device_get_action(struct udev_device *udev_device); | ||
102 | unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device); | ||
103 | unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device); | ||
104 | const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr); | ||
105 | int udev_device_has_tag(struct udev_device *udev_device, const char *tag); | ||
106 | |||
107 | /* | ||
108 | * udev_monitor | ||
109 | * | ||
110 | * access to kernel uevents and udev events | ||
111 | */ | ||
112 | struct udev_monitor; | ||
113 | struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor); | ||
114 | struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor); | ||
115 | struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor); | ||
116 | /* kernel and udev generated events over netlink */ | ||
117 | struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name); | ||
118 | /* custom socket (use netlink and filters instead) */ | ||
119 | struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path); | ||
120 | /* bind socket */ | ||
121 | int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor); | ||
122 | int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size); | ||
123 | int udev_monitor_get_fd(struct udev_monitor *udev_monitor); | ||
124 | struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor); | ||
125 | /* in-kernel socket filters to select messages that get delivered to a listener */ | ||
126 | int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, | ||
127 | const char *subsystem, const char *devtype); | ||
128 | int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag); | ||
129 | int udev_monitor_filter_update(struct udev_monitor *udev_monitor); | ||
130 | int udev_monitor_filter_remove(struct udev_monitor *udev_monitor); | ||
131 | |||
132 | /* | ||
133 | * udev_enumerate | ||
134 | * | ||
135 | * search sysfs for specific devices and provide a sorted list | ||
136 | */ | ||
137 | struct udev_enumerate; | ||
138 | struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate); | ||
139 | struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate); | ||
140 | struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate); | ||
141 | struct udev_enumerate *udev_enumerate_new(struct udev *udev); | ||
142 | /* device properties filter */ | ||
143 | int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem); | ||
144 | int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem); | ||
145 | int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value); | ||
146 | int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value); | ||
147 | int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value); | ||
148 | int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname); | ||
149 | int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag); | ||
150 | int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent); | ||
151 | int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate); | ||
152 | int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath); | ||
153 | /* run enumeration with active filters */ | ||
154 | int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate); | ||
155 | int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate); | ||
156 | /* return device list */ | ||
157 | struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate); | ||
158 | |||
159 | /* | ||
160 | * udev_queue | ||
161 | * | ||
162 | * access to the currently running udev events | ||
163 | */ | ||
164 | struct udev_queue; | ||
165 | struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue); | ||
166 | struct udev_queue *udev_queue_unref(struct udev_queue *udev_queue); | ||
167 | struct udev *udev_queue_get_udev(struct udev_queue *udev_queue); | ||
168 | struct udev_queue *udev_queue_new(struct udev *udev); | ||
169 | unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue); | ||
170 | unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue); | ||
171 | int udev_queue_get_udev_is_active(struct udev_queue *udev_queue); | ||
172 | int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue); | ||
173 | int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum); | ||
174 | int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue, | ||
175 | unsigned long long int start, unsigned long long int end); | ||
176 | struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue); | ||
177 | |||
178 | /* | ||
179 | * udev_util | ||
180 | * | ||
181 | * udev specific utilities | ||
182 | */ | ||
183 | int udev_util_encode_string(const char *str, char *str_enc, size_t len); | ||
184 | |||
185 | |||
186 | #ifdef __cplusplus | ||
187 | } /* extern "C" */ | ||
188 | #endif | ||
189 | |||
190 | #endif |
File src/libudev.pc.in added (mode: 100644) (index 0000000..c9a47fc) | |||
1 | prefix=@prefix@ | ||
2 | exec_prefix=@exec_prefix@ | ||
3 | libdir=@libdir@ | ||
4 | includedir=@includedir@ | ||
5 | |||
6 | Name: libudev | ||
7 | Description: Library to access udev device information | ||
8 | Version: @VERSION@ | ||
9 | Libs: -L${libdir} -ludev -lrt | ||
10 | Libs.private: | ||
11 | Cflags: -I${includedir} |
File src/udev-builtin-blkid.c added (mode: 100644) (index 0000000..e57f03e) | |||
1 | /* | ||
2 | * probe disks for filesystems and partitions | ||
3 | * | ||
4 | * Copyright (C) 2011 Kay Sievers <kay.sievers@vrfy.org> | ||
5 | * Copyright (C) 2011 Karel Zak <kzak@redhat.com> | ||
6 | * | ||
7 | * This program is free software: you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation, either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | */ | ||
20 | |||
21 | #include <stdio.h> | ||
22 | #include <stdlib.h> | ||
23 | #include <stdarg.h> | ||
24 | #include <unistd.h> | ||
25 | #include <string.h> | ||
26 | #include <errno.h> | ||
27 | #include <fcntl.h> | ||
28 | #include <getopt.h> | ||
29 | #include <sys/stat.h> | ||
30 | #include <blkid/blkid.h> | ||
31 | |||
32 | #include "udev.h" | ||
33 | |||
34 | static void print_property(struct udev_device *dev, bool test, const char *name, const char *value) | ||
35 | { | ||
36 | char s[265]; | ||
37 | |||
38 | s[0] = '\0'; | ||
39 | |||
40 | if (!strcmp(name, "TYPE")) { | ||
41 | udev_builtin_add_property(dev, test, "ID_FS_TYPE", value); | ||
42 | |||
43 | } else if (!strcmp(name, "USAGE")) { | ||
44 | udev_builtin_add_property(dev, test, "ID_FS_USAGE", value); | ||
45 | |||
46 | } else if (!strcmp(name, "VERSION")) { | ||
47 | udev_builtin_add_property(dev, test, "ID_FS_VERSION", value); | ||
48 | |||
49 | } else if (!strcmp(name, "UUID")) { | ||
50 | blkid_safe_string(value, s, sizeof(s)); | ||
51 | udev_builtin_add_property(dev, test, "ID_FS_UUID", s); | ||
52 | blkid_encode_string(value, s, sizeof(s)); | ||
53 | udev_builtin_add_property(dev, test, "ID_FS_UUID_ENC", s); | ||
54 | |||
55 | } else if (!strcmp(name, "UUID_SUB")) { | ||
56 | blkid_safe_string(value, s, sizeof(s)); | ||
57 | udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB", s); | ||
58 | blkid_encode_string(value, s, sizeof(s)); | ||
59 | udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB_ENC", s); | ||
60 | |||
61 | } else if (!strcmp(name, "LABEL")) { | ||
62 | blkid_safe_string(value, s, sizeof(s)); | ||
63 | udev_builtin_add_property(dev, test, "ID_FS_LABEL", s); | ||
64 | blkid_encode_string(value, s, sizeof(s)); | ||
65 | udev_builtin_add_property(dev, test, "ID_FS_LABEL_ENC", s); | ||
66 | |||
67 | } else if (!strcmp(name, "PTTYPE")) { | ||
68 | udev_builtin_add_property(dev, test, "ID_PART_TABLE_TYPE", value); | ||
69 | |||
70 | } else if (!strcmp(name, "PART_ENTRY_NAME")) { | ||
71 | blkid_encode_string(value, s, sizeof(s)); | ||
72 | udev_builtin_add_property(dev, test, "ID_PART_ENTRY_NAME", s); | ||
73 | |||
74 | } else if (!strcmp(name, "PART_ENTRY_TYPE")) { | ||
75 | blkid_encode_string(value, s, sizeof(s)); | ||
76 | udev_builtin_add_property(dev, test, "ID_PART_ENTRY_TYPE", s); | ||
77 | |||
78 | } else if (!strncmp(name, "PART_ENTRY_", 11)) { | ||
79 | util_strscpyl(s, sizeof(s), "ID_", name, NULL); | ||
80 | udev_builtin_add_property(dev, test, s, value); | ||
81 | } | ||
82 | } | ||
83 | |||
84 | static int probe_superblocks(blkid_probe pr) | ||
85 | { | ||
86 | struct stat st; | ||
87 | int rc; | ||
88 | |||
89 | if (fstat(blkid_probe_get_fd(pr), &st)) | ||
90 | return -1; | ||
91 | |||
92 | blkid_probe_enable_partitions(pr, 1); | ||
93 | |||
94 | if (!S_ISCHR(st.st_mode) && blkid_probe_get_size(pr) <= 1024 * 1440 && | ||
95 | blkid_probe_is_wholedisk(pr)) { | ||
96 | /* | ||
97 | * check if the small disk is partitioned, if yes then | ||
98 | * don't probe for filesystems. | ||
99 | */ | ||
100 | blkid_probe_enable_superblocks(pr, 0); | ||
101 | |||
102 | rc = blkid_do_fullprobe(pr); | ||
103 | if (rc < 0) | ||
104 | return rc; /* -1 = error, 1 = nothing, 0 = succes */ | ||
105 | |||
106 | if (blkid_probe_lookup_value(pr, "PTTYPE", NULL, NULL) == 0) | ||
107 | return 0; /* partition table detected */ | ||
108 | } | ||
109 | |||
110 | blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS); | ||
111 | blkid_probe_enable_superblocks(pr, 1); | ||
112 | |||
113 | return blkid_do_safeprobe(pr); | ||
114 | } | ||
115 | |||
116 | static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool test) | ||
117 | { | ||
118 | struct udev *udev = udev_device_get_udev(dev); | ||
119 | int64_t offset = 0; | ||
120 | bool noraid = false; | ||
121 | int fd = -1; | ||
122 | blkid_probe pr; | ||
123 | const char *data; | ||
124 | const char *name; | ||
125 | int nvals; | ||
126 | int i; | ||
127 | size_t len; | ||
128 | int err = 0; | ||
129 | |||
130 | static const struct option options[] = { | ||
131 | { "offset", optional_argument, NULL, 'o' }, | ||
132 | { "noraid", no_argument, NULL, 'R' }, | ||
133 | {} | ||
134 | }; | ||
135 | |||
136 | for (;;) { | ||
137 | int option; | ||
138 | |||
139 | option = getopt_long(argc, argv, "oR", options, NULL); | ||
140 | if (option == -1) | ||
141 | break; | ||
142 | |||
143 | switch (option) { | ||
144 | case 'o': | ||
145 | offset = strtoull(optarg, NULL, 0); | ||
146 | break; | ||
147 | case 'R': | ||
148 | noraid = true; | ||
149 | break; | ||
150 | } | ||
151 | } | ||
152 | |||
153 | pr = blkid_new_probe(); | ||
154 | if (!pr) { | ||
155 | err = -ENOMEM; | ||
156 | return EXIT_FAILURE; | ||
157 | } | ||
158 | |||
159 | blkid_probe_set_superblocks_flags(pr, | ||
160 | BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID | | ||
161 | BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE | | ||
162 | BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION); | ||
163 | |||
164 | if (noraid) | ||
165 | blkid_probe_filter_superblocks_usage(pr, BLKID_FLTR_NOTIN, BLKID_USAGE_RAID); | ||
166 | |||
167 | fd = open(udev_device_get_devnode(dev), O_RDONLY|O_CLOEXEC); | ||
168 | if (fd < 0) { | ||
169 | fprintf(stderr, "error: %s: %m\n", udev_device_get_devnode(dev)); | ||
170 | goto out; | ||
171 | } | ||
172 | |||
173 | err = blkid_probe_set_device(pr, fd, offset, 0); | ||
174 | if (err < 0) | ||
175 | goto out; | ||
176 | |||
177 | info(udev, "probe %s %sraid offset=%llu\n", | ||
178 | udev_device_get_devnode(dev), | ||
179 | noraid ? "no" : "", (unsigned long long) offset); | ||
180 | |||
181 | err = probe_superblocks(pr); | ||
182 | if (err < 0) | ||
183 | goto out; | ||
184 | |||
185 | nvals = blkid_probe_numof_values(pr); | ||
186 | for (i = 0; i < nvals; i++) { | ||
187 | if (blkid_probe_get_value(pr, i, &name, &data, &len)) | ||
188 | continue; | ||
189 | len = strnlen((char *) data, len); | ||
190 | print_property(dev, test, name, (char *) data); | ||
191 | } | ||
192 | |||
193 | blkid_free_probe(pr); | ||
194 | out: | ||
195 | if (fd > 0) | ||
196 | close(fd); | ||
197 | if (err < 0) | ||
198 | return EXIT_FAILURE; | ||
199 | return EXIT_SUCCESS; | ||
200 | } | ||
201 | |||
202 | const struct udev_builtin udev_builtin_blkid = { | ||
203 | .name = "blkid", | ||
204 | .cmd = builtin_blkid, | ||
205 | .help = "filesystem and partition probing", | ||
206 | .run_once = true, | ||
207 | }; |
File src/udev-builtin-firmware.c added (mode: 100644) (index 0000000..d212c64) | |||
1 | /* | ||
2 | * firmware - Kernel firmware loader | ||
3 | * | ||
4 | * Copyright (C) 2009 Piter Punk <piterpunk@slackware.com> | ||
5 | * Copyright (C) 2009-2011 Kay Sievers <kay.sievers@vrfy.org> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License as | ||
9 | * published by the Free Software Foundation; either version 2 of the | ||
10 | * License, or (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details:* | ||
16 | */ | ||
17 | |||
18 | #include <unistd.h> | ||
19 | #include <stdlib.h> | ||
20 | #include <string.h> | ||
21 | #include <stdio.h> | ||
22 | #include <getopt.h> | ||
23 | #include <errno.h> | ||
24 | #include <stdbool.h> | ||
25 | #include <sys/utsname.h> | ||
26 | #include <sys/stat.h> | ||
27 | |||
28 | #include "udev.h" | ||
29 | |||
30 | static bool set_loading(struct udev *udev, char *loadpath, const char *state) | ||
31 | { | ||
32 | FILE *ldfile; | ||
33 | |||
34 | ldfile = fopen(loadpath, "we"); | ||
35 | if (ldfile == NULL) { | ||
36 | err(udev, "error: can not open '%s'\n", loadpath); | ||
37 | return false; | ||
38 | }; | ||
39 | fprintf(ldfile, "%s\n", state); | ||
40 | fclose(ldfile); | ||
41 | return true; | ||
42 | } | ||
43 | |||
44 | static bool copy_firmware(struct udev *udev, const char *source, const char *target, size_t size) | ||
45 | { | ||
46 | char *buf; | ||
47 | FILE *fsource = NULL, *ftarget = NULL; | ||
48 | bool ret = false; | ||
49 | |||
50 | buf = malloc(size); | ||
51 | if (buf == NULL) { | ||
52 | err(udev,"No memory available to load firmware file"); | ||
53 | return false; | ||
54 | } | ||
55 | |||
56 | info(udev, "writing '%s' (%zi) to '%s'\n", source, size, target); | ||
57 | |||
58 | fsource = fopen(source, "re"); | ||
59 | if (fsource == NULL) | ||
60 | goto exit; | ||
61 | ftarget = fopen(target, "we"); | ||
62 | if (ftarget == NULL) | ||
63 | goto exit; | ||
64 | if (fread(buf, size, 1, fsource) != 1) | ||
65 | goto exit; | ||
66 | if (fwrite(buf, size, 1, ftarget) == 1) | ||
67 | ret = true; | ||
68 | exit: | ||
69 | if (ftarget != NULL) | ||
70 | fclose(ftarget); | ||
71 | if (fsource != NULL) | ||
72 | fclose(fsource); | ||
73 | free(buf); | ||
74 | return ret; | ||
75 | } | ||
76 | |||
77 | static int builtin_firmware(struct udev_device *dev, int argc, char *argv[], bool test) | ||
78 | { | ||
79 | struct udev *udev = udev_device_get_udev(dev); | ||
80 | static const char *searchpath[] = { FIRMWARE_PATH }; | ||
81 | char fwencpath[UTIL_PATH_SIZE]; | ||
82 | char misspath[UTIL_PATH_SIZE]; | ||
83 | char loadpath[UTIL_PATH_SIZE]; | ||
84 | char datapath[UTIL_PATH_SIZE]; | ||
85 | char fwpath[UTIL_PATH_SIZE]; | ||
86 | const char *firmware; | ||
87 | FILE *fwfile; | ||
88 | struct utsname kernel; | ||
89 | struct stat statbuf; | ||
90 | unsigned int i; | ||
91 | int rc = EXIT_SUCCESS; | ||
92 | |||
93 | firmware = udev_device_get_property_value(dev, "FIRMWARE"); | ||
94 | if (firmware == NULL) { | ||
95 | err(udev, "firmware parameter missing\n\n"); | ||
96 | rc = EXIT_FAILURE; | ||
97 | goto exit; | ||
98 | } | ||
99 | |||
100 | /* lookup firmware file */ | ||
101 | uname(&kernel); | ||
102 | for (i = 0; i < ARRAY_SIZE(searchpath); i++) { | ||
103 | util_strscpyl(fwpath, sizeof(fwpath), searchpath[i], kernel.release, "/", firmware, NULL); | ||
104 | dbg(udev, "trying %s\n", fwpath); | ||
105 | fwfile = fopen(fwpath, "re"); | ||
106 | if (fwfile != NULL) | ||
107 | break; | ||
108 | |||
109 | util_strscpyl(fwpath, sizeof(fwpath), searchpath[i], firmware, NULL); | ||
110 | dbg(udev, "trying %s\n", fwpath); | ||
111 | fwfile = fopen(fwpath, "re"); | ||
112 | if (fwfile != NULL) | ||
113 | break; | ||
114 | } | ||
115 | |||
116 | util_path_encode(firmware, fwencpath, sizeof(fwencpath)); | ||
117 | util_strscpyl(misspath, sizeof(misspath), udev_get_run_path(udev), "/firmware-missing/", fwencpath, NULL); | ||
118 | util_strscpyl(loadpath, sizeof(loadpath), udev_device_get_syspath(dev), "/loading", NULL); | ||
119 | |||
120 | if (fwfile == NULL) { | ||
121 | int err; | ||
122 | |||
123 | /* This link indicates the missing firmware file and the associated device */ | ||
124 | info(udev, "did not find firmware file '%s'\n", firmware); | ||
125 | do { | ||
126 | err = util_create_path(udev, misspath); | ||
127 | if (err != 0 && err != -ENOENT) | ||
128 | break; | ||
129 | err = symlink(udev_device_get_devpath(dev), misspath); | ||
130 | if (err != 0) | ||
131 | err = -errno; | ||
132 | } while (err == -ENOENT); | ||
133 | rc = EXIT_FAILURE; | ||
134 | set_loading(udev, loadpath, "-1"); | ||
135 | goto exit; | ||
136 | } | ||
137 | |||
138 | if (stat(fwpath, &statbuf) < 0 || statbuf.st_size == 0) { | ||
139 | rc = EXIT_FAILURE; | ||
140 | goto exit; | ||
141 | } | ||
142 | if (unlink(misspath) == 0) | ||
143 | util_delete_path(udev, misspath); | ||
144 | |||
145 | if (!set_loading(udev, loadpath, "1")) | ||
146 | goto exit; | ||
147 | |||
148 | util_strscpyl(datapath, sizeof(datapath), udev_device_get_syspath(dev), "/data", NULL); | ||
149 | if (!copy_firmware(udev, fwpath, datapath, statbuf.st_size)) { | ||
150 | err(udev, "error sending firmware '%s' to device\n", firmware); | ||
151 | set_loading(udev, loadpath, "-1"); | ||
152 | rc = EXIT_FAILURE; | ||
153 | goto exit; | ||
154 | }; | ||
155 | |||
156 | set_loading(udev, loadpath, "0"); | ||
157 | exit: | ||
158 | if (fwfile) | ||
159 | fclose(fwfile); | ||
160 | return rc; | ||
161 | } | ||
162 | |||
163 | const struct udev_builtin udev_builtin_firmware = { | ||
164 | .name = "firmware", | ||
165 | .cmd = builtin_firmware, | ||
166 | .help = "kernel firmware loader", | ||
167 | .run_once = true, | ||
168 | }; |
File src/udev-builtin-hwdb.c added (mode: 100644) (index 0000000..aa996f3) | |||
1 | /* | ||
2 | * usb-db, pci-db - lookup vendor/product database | ||
3 | * | ||
4 | * Copyright (C) 2009 Lennart Poettering <lennart@poettering.net> | ||
5 | * Copyright (C) 2011 Kay Sievers <kay.sievers@vrfy.org> | ||
6 | * | ||
7 | * This program is free software: you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation, either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | */ | ||
20 | |||
21 | #include <stdio.h> | ||
22 | #include <errno.h> | ||
23 | #include <string.h> | ||
24 | #include <inttypes.h> | ||
25 | #include <ctype.h> | ||
26 | #include <stdlib.h> | ||
27 | |||
28 | #include "udev.h" | ||
29 | |||
30 | static int get_id_attr( | ||
31 | struct udev_device *parent, | ||
32 | const char *name, | ||
33 | uint16_t *value) { | ||
34 | |||
35 | const char *t; | ||
36 | unsigned u; | ||
37 | |||
38 | if (!(t = udev_device_get_sysattr_value(parent, name))) { | ||
39 | fprintf(stderr, "%s lacks %s.\n", udev_device_get_syspath(parent), name); | ||
40 | return -1; | ||
41 | } | ||
42 | |||
43 | if (!strncmp(t, "0x", 2)) | ||
44 | t += 2; | ||
45 | |||
46 | if (sscanf(t, "%04x", &u) != 1 || u > 0xFFFFU) { | ||
47 | fprintf(stderr, "Failed to parse %s on %s.\n", name, udev_device_get_syspath(parent)); | ||
48 | return -1; | ||
49 | } | ||
50 | |||
51 | *value = (uint16_t) u; | ||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | static int get_vid_pid( | ||
56 | struct udev_device *parent, | ||
57 | const char *vendor_attr, | ||
58 | const char *product_attr, | ||
59 | uint16_t *vid, | ||
60 | uint16_t *pid) { | ||
61 | |||
62 | if (get_id_attr(parent, vendor_attr, vid) < 0) | ||
63 | return -1; | ||
64 | else if (*vid <= 0) { | ||
65 | fprintf(stderr, "Invalid vendor id.\n"); | ||
66 | return -1; | ||
67 | } | ||
68 | |||
69 | if (get_id_attr(parent, product_attr, pid) < 0) | ||
70 | return -1; | ||
71 | |||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | static void rstrip(char *n) { | ||
76 | size_t i; | ||
77 | |||
78 | for (i = strlen(n); i > 0 && isspace(n[i-1]); i--) | ||
79 | n[i-1] = 0; | ||
80 | } | ||
81 | |||
82 | #define HEXCHARS "0123456789abcdefABCDEF" | ||
83 | #define WHITESPACE " \t\n\r" | ||
84 | static int lookup_vid_pid(const char *database, | ||
85 | uint16_t vid, uint16_t pid, | ||
86 | char **vendor, char **product) | ||
87 | { | ||
88 | |||
89 | FILE *f; | ||
90 | int ret = -1; | ||
91 | int found_vendor = 0; | ||
92 | char *line = NULL; | ||
93 | |||
94 | *vendor = *product = NULL; | ||
95 | |||
96 | if (!(f = fopen(database, "rme"))) { | ||
97 | fprintf(stderr, "Failed to open database file '%s': %s\n", database, strerror(errno)); | ||
98 | return -1; | ||
99 | } | ||
100 | |||
101 | for (;;) { | ||
102 | size_t n; | ||
103 | |||
104 | if (getline(&line, &n, f) < 0) | ||
105 | break; | ||
106 | |||
107 | rstrip(line); | ||
108 | |||
109 | if (line[0] == '#' || line[0] == 0) | ||
110 | continue; | ||
111 | |||
112 | if (strspn(line, HEXCHARS) == 4) { | ||
113 | unsigned u; | ||
114 | |||
115 | if (found_vendor) | ||
116 | break; | ||
117 | |||
118 | if (sscanf(line, "%04x", &u) == 1 && u == vid) { | ||
119 | char *t; | ||
120 | |||
121 | t = line+4; | ||
122 | t += strspn(t, WHITESPACE); | ||
123 | |||
124 | if (!(*vendor = strdup(t))) { | ||
125 | fprintf(stderr, "Out of memory.\n"); | ||
126 | goto finish; | ||
127 | } | ||
128 | |||
129 | found_vendor = 1; | ||
130 | } | ||
131 | |||
132 | continue; | ||
133 | } | ||
134 | |||
135 | if (found_vendor && line[0] == '\t' && strspn(line+1, HEXCHARS) == 4) { | ||
136 | unsigned u; | ||
137 | |||
138 | if (sscanf(line+1, "%04x", &u) == 1 && u == pid) { | ||
139 | char *t; | ||
140 | |||
141 | t = line+5; | ||
142 | t += strspn(t, WHITESPACE); | ||
143 | |||
144 | if (!(*product = strdup(t))) { | ||
145 | fprintf(stderr, "Out of memory.\n"); | ||
146 | goto finish; | ||
147 | } | ||
148 | |||
149 | break; | ||
150 | } | ||
151 | } | ||
152 | } | ||
153 | |||
154 | ret = 0; | ||
155 | |||
156 | finish: | ||
157 | free(line); | ||
158 | fclose(f); | ||
159 | |||
160 | if (ret < 0) { | ||
161 | free(*product); | ||
162 | free(*vendor); | ||
163 | |||
164 | *product = *vendor = NULL; | ||
165 | } | ||
166 | |||
167 | return ret; | ||
168 | } | ||
169 | |||
170 | static struct udev_device *find_device(struct udev_device *dev, const char *subsys, const char *devtype) | ||
171 | { | ||
172 | const char *str; | ||
173 | |||
174 | str = udev_device_get_subsystem(dev); | ||
175 | if (str == NULL) | ||
176 | goto try_parent; | ||
177 | if (strcmp(str, subsys) != 0) | ||
178 | goto try_parent; | ||
179 | |||
180 | if (devtype != NULL) { | ||
181 | str = udev_device_get_devtype(dev); | ||
182 | if (str == NULL) | ||
183 | goto try_parent; | ||
184 | if (strcmp(str, devtype) != 0) | ||
185 | goto try_parent; | ||
186 | } | ||
187 | return dev; | ||
188 | try_parent: | ||
189 | return udev_device_get_parent_with_subsystem_devtype(dev, subsys, devtype); | ||
190 | } | ||
191 | |||
192 | |||
193 | static int builtin_db(struct udev_device *dev, bool test, | ||
194 | const char *database, | ||
195 | const char *vendor_attr, const char *product_attr, | ||
196 | const char *subsys, const char *devtype) | ||
197 | { | ||
198 | struct udev_device *parent; | ||
199 | uint16_t vid = 0, pid = 0; | ||
200 | char *vendor = NULL, *product = NULL; | ||
201 | |||
202 | parent = find_device(dev, subsys, devtype); | ||
203 | if (!parent) { | ||
204 | fprintf(stderr, "Failed to find device.\n"); | ||
205 | goto finish; | ||
206 | } | ||
207 | |||
208 | if (get_vid_pid(parent, vendor_attr, product_attr, &vid, &pid) < 0) | ||
209 | goto finish; | ||
210 | |||
211 | if (lookup_vid_pid(database, vid, pid, &vendor, &product) < 0) | ||
212 | goto finish; | ||
213 | |||
214 | if (vendor) | ||
215 | udev_builtin_add_property(dev, test, "ID_VENDOR_FROM_DATABASE", vendor); | ||
216 | if (product) | ||
217 | udev_builtin_add_property(dev, test, "ID_MODEL_FROM_DATABASE", product); | ||
218 | |||
219 | finish: | ||
220 | free(vendor); | ||
221 | free(product); | ||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static int builtin_usb_db(struct udev_device *dev, int argc, char *argv[], bool test) | ||
226 | { | ||
227 | return builtin_db(dev, test, USB_DATABASE, "idVendor", "idProduct", "usb", "usb_device"); | ||
228 | } | ||
229 | |||
230 | static int builtin_pci_db(struct udev_device *dev, int argc, char *argv[], bool test) | ||
231 | { | ||
232 | return builtin_db(dev, test, PCI_DATABASE, "vendor", "device", "pci", NULL); | ||
233 | } | ||
234 | |||
235 | const struct udev_builtin udev_builtin_usb_db = { | ||
236 | .name = "usb-db", | ||
237 | .cmd = builtin_usb_db, | ||
238 | .help = "USB vendor/product database", | ||
239 | .run_once = true, | ||
240 | }; | ||
241 | |||
242 | const struct udev_builtin udev_builtin_pci_db = { | ||
243 | .name = "pci-db", | ||
244 | .cmd = builtin_pci_db, | ||
245 | .help = "PCI vendor/product database", | ||
246 | .run_once = true, | ||
247 | }; |
File src/udev-builtin-input_id.c added (mode: 100644) (index 0000000..a062ef7) | |||
1 | /* | ||
2 | * compose persistent device path | ||
3 | * | ||
4 | * Copyright (C) 2009 Martin Pitt <martin.pitt@ubuntu.com> | ||
5 | * Portions Copyright (C) 2004 David Zeuthen, <david@fubar.dk> | ||
6 | * Copyright (C) 2011 Kay Sievers <kay.sievers@vrfy.org> | ||
7 | * | ||
8 | * This program is free software: you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation, either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
20 | */ | ||
21 | |||
22 | #include <stdio.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <stdarg.h> | ||
25 | #include <unistd.h> | ||
26 | #include <string.h> | ||
27 | #include <errno.h> | ||
28 | #include <linux/limits.h> | ||
29 | #include <linux/input.h> | ||
30 | |||
31 | #include "udev.h" | ||
32 | |||
33 | /* we must use this kernel-compatible implementation */ | ||
34 | #define BITS_PER_LONG (sizeof(unsigned long) * 8) | ||
35 | #define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) | ||
36 | #define OFF(x) ((x)%BITS_PER_LONG) | ||
37 | #define BIT(x) (1UL<<OFF(x)) | ||
38 | #define LONG(x) ((x)/BITS_PER_LONG) | ||
39 | #define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1) | ||
40 | |||
41 | /* | ||
42 | * Read a capability attribute and return bitmask. | ||
43 | * @param dev udev_device | ||
44 | * @param attr sysfs attribute name (e. g. "capabilities/key") | ||
45 | * @param bitmask: Output array which has a sizeof of bitmask_size | ||
46 | */ | ||
47 | static void get_cap_mask(struct udev_device *dev, | ||
48 | struct udev_device *pdev, const char* attr, | ||
49 | unsigned long *bitmask, size_t bitmask_size, | ||
50 | bool test) | ||
51 | { | ||
52 | struct udev *udev = udev_device_get_udev(dev); | ||
53 | char text[4096]; | ||
54 | unsigned i; | ||
55 | char* word; | ||
56 | unsigned long val; | ||
57 | |||
58 | snprintf(text, sizeof(text), "%s", udev_device_get_sysattr_value(pdev, attr)); | ||
59 | info(udev, "%s raw kernel attribute: %s\n", attr, text); | ||
60 | |||
61 | memset (bitmask, 0, bitmask_size); | ||
62 | i = 0; | ||
63 | while ((word = strrchr(text, ' ')) != NULL) { | ||
64 | val = strtoul (word+1, NULL, 16); | ||
65 | if (i < bitmask_size/sizeof(unsigned long)) | ||
66 | bitmask[i] = val; | ||
67 | else | ||
68 | info(udev, "ignoring %s block %lX which is larger than maximum size\n", attr, val); | ||
69 | *word = '\0'; | ||
70 | ++i; | ||
71 | } | ||
72 | val = strtoul (text, NULL, 16); | ||
73 | if (i < bitmask_size / sizeof(unsigned long)) | ||
74 | bitmask[i] = val; | ||
75 | else | ||
76 | info(udev, "ignoring %s block %lX which is larger than maximum size\n", attr, val); | ||
77 | |||
78 | if (test) { | ||
79 | /* printf pattern with the right unsigned long number of hex chars */ | ||
80 | snprintf(text, sizeof(text), " bit %%4u: %%0%zilX\n", 2 * sizeof(unsigned long)); | ||
81 | info(udev, "%s decoded bit map:\n", attr); | ||
82 | val = bitmask_size / sizeof (unsigned long); | ||
83 | /* skip over leading zeros */ | ||
84 | while (bitmask[val-1] == 0 && val > 0) | ||
85 | --val; | ||
86 | for (i = 0; i < val; ++i) | ||
87 | info(udev, text, i * BITS_PER_LONG, bitmask[i]); | ||
88 | } | ||
89 | } | ||
90 | |||
91 | /* pointer devices */ | ||
92 | static void test_pointers (struct udev_device *dev, | ||
93 | const unsigned long* bitmask_ev, | ||
94 | const unsigned long* bitmask_abs, | ||
95 | const unsigned long* bitmask_key, | ||
96 | const unsigned long* bitmask_rel, | ||
97 | bool test) | ||
98 | { | ||
99 | int is_mouse = 0; | ||
100 | int is_touchpad = 0; | ||
101 | |||
102 | if (!test_bit (EV_KEY, bitmask_ev)) { | ||
103 | if (test_bit (EV_ABS, bitmask_ev) && | ||
104 | test_bit (ABS_X, bitmask_abs) && | ||
105 | test_bit (ABS_Y, bitmask_abs) && | ||
106 | test_bit (ABS_Z, bitmask_abs)) | ||
107 | udev_builtin_add_property(dev, test, "ID_INPUT_ACCELEROMETER", "1"); | ||
108 | return; | ||
109 | } | ||
110 | |||
111 | if (test_bit (EV_ABS, bitmask_ev) && | ||
112 | test_bit (ABS_X, bitmask_abs) && test_bit (ABS_Y, bitmask_abs)) { | ||
113 | if (test_bit (BTN_STYLUS, bitmask_key) || test_bit (BTN_TOOL_PEN, bitmask_key)) | ||
114 | udev_builtin_add_property(dev, test, "ID_INPUT_TABLET", "1"); | ||
115 | else if (test_bit (BTN_TOOL_FINGER, bitmask_key) && !test_bit (BTN_TOOL_PEN, bitmask_key)) | ||
116 | is_touchpad = 1; | ||
117 | else if (test_bit (BTN_TRIGGER, bitmask_key) || | ||
118 | test_bit (BTN_A, bitmask_key) || | ||
119 | test_bit (BTN_1, bitmask_key)) | ||
120 | udev_builtin_add_property(dev, test, "ID_INPUT_JOYSTICK", "1"); | ||
121 | else if (test_bit (BTN_MOUSE, bitmask_key)) | ||
122 | /* This path is taken by VMware's USB mouse, which has | ||
123 | * absolute axes, but no touch/pressure button. */ | ||
124 | is_mouse = 1; | ||
125 | else if (test_bit (BTN_TOUCH, bitmask_key)) | ||
126 | udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHSCREEN", "1"); | ||
127 | } | ||
128 | |||
129 | if (test_bit (EV_REL, bitmask_ev) && | ||
130 | test_bit (REL_X, bitmask_rel) && test_bit (REL_Y, bitmask_rel) && | ||
131 | test_bit (BTN_MOUSE, bitmask_key)) | ||
132 | is_mouse = 1; | ||
133 | |||
134 | if (is_mouse) | ||
135 | udev_builtin_add_property(dev, test, "ID_INPUT_MOUSE", "1"); | ||
136 | if (is_touchpad) | ||
137 | udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHPAD", "1"); | ||
138 | } | ||
139 | |||
140 | /* key like devices */ | ||
141 | static void test_key (struct udev_device *dev, | ||
142 | const unsigned long* bitmask_ev, | ||
143 | const unsigned long* bitmask_key, | ||
144 | bool test) | ||
145 | { | ||
146 | struct udev *udev = udev_device_get_udev(dev); | ||
147 | unsigned i; | ||
148 | unsigned long found; | ||
149 | unsigned long mask; | ||
150 | |||
151 | /* do we have any KEY_* capability? */ | ||
152 | if (!test_bit (EV_KEY, bitmask_ev)) { | ||
153 | info(udev, "test_key: no EV_KEY capability\n"); | ||
154 | return; | ||
155 | } | ||
156 | |||
157 | /* only consider KEY_* here, not BTN_* */ | ||
158 | found = 0; | ||
159 | for (i = 0; i < BTN_MISC/BITS_PER_LONG; ++i) { | ||
160 | found |= bitmask_key[i]; | ||
161 | info(udev, "test_key: checking bit block %lu for any keys; found=%i\n", i*BITS_PER_LONG, found > 0); | ||
162 | } | ||
163 | /* If there are no keys in the lower block, check the higher block */ | ||
164 | if (!found) { | ||
165 | for (i = KEY_OK; i < BTN_TRIGGER_HAPPY; ++i) { | ||
166 | if (test_bit (i, bitmask_key)) { | ||
167 | info(udev, "test_key: Found key %x in high block\n", i); | ||
168 | found = 1; | ||
169 | break; | ||
170 | } | ||
171 | } | ||
172 | } | ||
173 | |||
174 | if (found > 0) | ||
175 | udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1"); | ||
176 | |||
177 | /* the first 32 bits are ESC, numbers, and Q to D; if we have all of | ||
178 | * those, consider it a full keyboard; do not test KEY_RESERVED, though */ | ||
179 | mask = 0xFFFFFFFE; | ||
180 | if ((bitmask_key[0] & mask) == mask) | ||
181 | udev_builtin_add_property(dev, test, "ID_INPUT_KEYBOARD", "1"); | ||
182 | } | ||
183 | |||
184 | static int builtin_input_id(struct udev_device *dev, int argc, char *argv[], bool test) | ||
185 | { | ||
186 | struct udev_device *pdev; | ||
187 | unsigned long bitmask_ev[NBITS(EV_MAX)]; | ||
188 | unsigned long bitmask_abs[NBITS(ABS_MAX)]; | ||
189 | unsigned long bitmask_key[NBITS(KEY_MAX)]; | ||
190 | unsigned long bitmask_rel[NBITS(REL_MAX)]; | ||
191 | |||
192 | /* walk up the parental chain until we find the real input device; the | ||
193 | * argument is very likely a subdevice of this, like eventN */ | ||
194 | pdev = dev; | ||
195 | while (pdev != NULL && udev_device_get_sysattr_value(pdev, "capabilities/ev") == NULL) | ||
196 | pdev = udev_device_get_parent_with_subsystem_devtype(pdev, "input", NULL); | ||
197 | |||
198 | /* not an "input" class device */ | ||
199 | if (pdev == NULL) | ||
200 | return EXIT_SUCCESS; | ||
201 | |||
202 | /* Use this as a flag that input devices were detected, so that this | ||
203 | * program doesn't need to be called more than once per device */ | ||
204 | udev_builtin_add_property(dev, test, "ID_INPUT", "1"); | ||
205 | get_cap_mask(dev, pdev, "capabilities/ev", bitmask_ev, sizeof(bitmask_ev), test); | ||
206 | get_cap_mask(dev, pdev, "capabilities/abs", bitmask_abs, sizeof(bitmask_abs), test); | ||
207 | get_cap_mask(dev, pdev, "capabilities/rel", bitmask_rel, sizeof(bitmask_rel), test); | ||
208 | get_cap_mask(dev, pdev, "capabilities/key", bitmask_key, sizeof(bitmask_key), test); | ||
209 | test_pointers(dev, bitmask_ev, bitmask_abs, bitmask_key, bitmask_rel, test); | ||
210 | test_key(dev, bitmask_ev, bitmask_key, test); | ||
211 | return EXIT_SUCCESS; | ||
212 | } | ||
213 | |||
214 | const struct udev_builtin udev_builtin_input_id = { | ||
215 | .name = "input_id", | ||
216 | .cmd = builtin_input_id, | ||
217 | .help = "input device properties", | ||
218 | }; |
File src/udev-builtin-kmod.c added (mode: 100644) (index 0000000..57e813f) | |||
1 | /* | ||
2 | * load kernel modules | ||
3 | * | ||
4 | * Copyright (C) 2011 Kay Sievers <kay.sievers@vrfy.org> | ||
5 | * Copyright (C) 2011 ProFUSION embedded systems | ||
6 | * | ||
7 | * This program is free software: you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation, either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | */ | ||
20 | |||
21 | #include <stdio.h> | ||
22 | #include <stdlib.h> | ||
23 | #include <stdarg.h> | ||
24 | #include <unistd.h> | ||
25 | #include <string.h> | ||
26 | #include <errno.h> | ||
27 | #include <fcntl.h> | ||
28 | #include <sys/stat.h> | ||
29 | #include <sys/wait.h> | ||
30 | #include <libkmod.h> | ||
31 | |||
32 | #include "udev.h" | ||
33 | |||
34 | static struct kmod_ctx *ctx; | ||
35 | |||
36 | static int load_module(struct udev *udev, const char *alias) | ||
37 | { | ||
38 | struct kmod_list *list = NULL; | ||
39 | struct kmod_list *l; | ||
40 | int err; | ||
41 | |||
42 | err = kmod_module_new_from_lookup(ctx, alias, &list); | ||
43 | if (err < 0) | ||
44 | return err; | ||
45 | |||
46 | if (list == NULL) | ||
47 | info(udev, "no module matches '%s'\n", alias); | ||
48 | |||
49 | kmod_list_foreach(l, list) { | ||
50 | struct kmod_module *mod = kmod_module_get_module(l); | ||
51 | |||
52 | err = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL); | ||
53 | if (err == KMOD_PROBE_APPLY_BLACKLIST) | ||
54 | info(udev, "module '%s' is blacklisted\n", kmod_module_get_name(mod)); | ||
55 | else if (err == 0) | ||
56 | info(udev, "inserted '%s'\n", kmod_module_get_name(mod)); | ||
57 | else | ||
58 | info(udev, "failed to insert '%s'\n", kmod_module_get_name(mod)); | ||
59 | |||
60 | kmod_module_unref(mod); | ||
61 | } | ||
62 | |||
63 | kmod_module_unref_list(list); | ||
64 | return err; | ||
65 | } | ||
66 | |||
67 | static void udev_kmod_log(void *data, int priority, const char *file, int line, | ||
68 | const char *fn, const char *format, va_list args) | ||
69 | { | ||
70 | udev_main_log(data, priority, file, line, fn, format, args); | ||
71 | } | ||
72 | |||
73 | /* needs to re-instantiate the context after a reload */ | ||
74 | static int builtin_kmod(struct udev_device *dev, int argc, char *argv[], bool test) | ||
75 | { | ||
76 | struct udev *udev = udev_device_get_udev(dev); | ||
77 | int i; | ||
78 | |||
79 | if (!ctx) { | ||
80 | ctx = kmod_new(NULL, NULL); | ||
81 | if (!ctx) | ||
82 | return -ENOMEM; | ||
83 | |||
84 | info(udev, "load module index\n"); | ||
85 | kmod_set_log_fn(ctx, udev_kmod_log, udev); | ||
86 | kmod_load_resources(ctx); | ||
87 | } | ||
88 | |||
89 | if (argc < 3 || strcmp(argv[1], "load")) { | ||
90 | err(udev, "expect: %s load <module>\n", argv[0]); | ||
91 | return EXIT_FAILURE; | ||
92 | } | ||
93 | |||
94 | for (i = 2; argv[i]; i++) { | ||
95 | info(udev, "execute '%s' '%s'\n", argv[1], argv[i]); | ||
96 | load_module(udev, argv[i]); | ||
97 | } | ||
98 | |||
99 | return EXIT_SUCCESS; | ||
100 | } | ||
101 | |||
102 | /* called at udev startup */ | ||
103 | static int builtin_kmod_init(struct udev *udev) | ||
104 | { | ||
105 | if (ctx) | ||
106 | return 0; | ||
107 | |||
108 | ctx = kmod_new(NULL, NULL); | ||
109 | if (!ctx) | ||
110 | return -ENOMEM; | ||
111 | |||
112 | info(udev, "load module index\n"); | ||
113 | kmod_set_log_fn(ctx, udev_kmod_log, udev); | ||
114 | kmod_load_resources(ctx); | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | /* called on udev shutdown and reload request */ | ||
119 | static void builtin_kmod_exit(struct udev *udev) | ||
120 | { | ||
121 | info(udev, "unload module index\n"); | ||
122 | ctx = kmod_unref(ctx); | ||
123 | } | ||
124 | |||
125 | /* called every couple of seconds during event activity; 'true' if config has changed */ | ||
126 | static bool builtin_kmod_validate(struct udev *udev) | ||
127 | { | ||
128 | info(udev, "validate module index\n"); | ||
129 | if (kmod_validate_resources(ctx) != KMOD_RESOURCES_OK) | ||
130 | return true; | ||
131 | return false; | ||
132 | } | ||
133 | |||
134 | const struct udev_builtin udev_builtin_kmod = { | ||
135 | .name = "kmod", | ||
136 | .cmd = builtin_kmod, | ||
137 | .init = builtin_kmod_init, | ||
138 | .exit = builtin_kmod_exit, | ||
139 | .validate = builtin_kmod_validate, | ||
140 | .help = "kernel module loader", | ||
141 | .run_once = false, | ||
142 | }; |
File src/udev-builtin-path_id.c added (mode: 100644) (index 0000000..dafbed7) | |||
1 | /* | ||
2 | * compose persistent device path | ||
3 | * | ||
4 | * Copyright (C) 2009-2011 Kay Sievers <kay.sievers@vrfy.org> | ||
5 | * | ||
6 | * Logic based on Hannes Reinecke's shell script. | ||
7 | * | ||
8 | * This program is free software: you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation, either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
20 | */ | ||
21 | |||
22 | #include <stdio.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <stdarg.h> | ||
25 | #include <unistd.h> | ||
26 | #include <string.h> | ||
27 | #include <ctype.h> | ||
28 | #include <fcntl.h> | ||
29 | #include <errno.h> | ||
30 | #include <dirent.h> | ||
31 | #include <getopt.h> | ||
32 | |||
33 | #include "udev.h" | ||
34 | |||
35 | static int path_prepend(char **path, const char *fmt, ...) | ||
36 | { | ||
37 | va_list va; | ||
38 | char *pre; | ||
39 | int err = 0; | ||
40 | |||
41 | va_start(va, fmt); | ||
42 | err = vasprintf(&pre, fmt, va); | ||
43 | va_end(va); | ||
44 | if (err < 0) | ||
45 | goto out; | ||
46 | |||
47 | if (*path != NULL) { | ||
48 | char *new; | ||
49 | |||
50 | err = asprintf(&new, "%s-%s", pre, *path); | ||
51 | free(pre); | ||
52 | if (err < 0) | ||
53 | goto out; | ||
54 | free(*path); | ||
55 | *path = new; | ||
56 | } else { | ||
57 | *path = pre; | ||
58 | } | ||
59 | |||
60 | out: | ||
61 | return err; | ||
62 | } | ||
63 | |||
64 | /* | ||
65 | ** Linux only supports 32 bit luns. | ||
66 | ** See drivers/scsi/scsi_scan.c::scsilun_to_int() for more details. | ||
67 | */ | ||
68 | static int format_lun_number(struct udev_device *dev, char **path) | ||
69 | { | ||
70 | unsigned long lun = strtoul(udev_device_get_sysnum(dev), NULL, 10); | ||
71 | |||
72 | /* address method 0, peripheral device addressing with bus id of zero */ | ||
73 | if (lun < 256) | ||
74 | return path_prepend(path, "lun-%d", lun); | ||
75 | /* handle all other lun addressing methods by using a variant of the original lun format */ | ||
76 | return path_prepend(path, "lun-0x%04x%04x00000000", (lun & 0xffff), (lun >> 16) & 0xffff); | ||
77 | } | ||
78 | |||
79 | static struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys) | ||
80 | { | ||
81 | struct udev_device *parent = dev; | ||
82 | |||
83 | while (parent != NULL) { | ||
84 | const char *subsystem; | ||
85 | |||
86 | subsystem = udev_device_get_subsystem(parent); | ||
87 | if (subsystem == NULL || strcmp(subsystem, subsys) != 0) | ||
88 | break; | ||
89 | dev = parent; | ||
90 | parent = udev_device_get_parent(parent); | ||
91 | } | ||
92 | return dev; | ||
93 | } | ||
94 | |||
95 | static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent, char **path) | ||
96 | { | ||
97 | struct udev *udev = udev_device_get_udev(parent); | ||
98 | struct udev_device *targetdev; | ||
99 | struct udev_device *fcdev = NULL; | ||
100 | const char *port; | ||
101 | char *lun = NULL;; | ||
102 | |||
103 | targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target"); | ||
104 | if (targetdev == NULL) | ||
105 | return NULL; | ||
106 | |||
107 | fcdev = udev_device_new_from_subsystem_sysname(udev, "fc_transport", udev_device_get_sysname(targetdev)); | ||
108 | if (fcdev == NULL) | ||
109 | return NULL; | ||
110 | port = udev_device_get_sysattr_value(fcdev, "port_name"); | ||
111 | if (port == NULL) { | ||
112 | parent = NULL; | ||
113 | goto out; | ||
114 | } | ||
115 | |||
116 | format_lun_number(parent, &lun); | ||
117 | path_prepend(path, "fc-%s-%s", port, lun); | ||
118 | if (lun) | ||
119 | free(lun); | ||
120 | out: | ||
121 | udev_device_unref(fcdev); | ||
122 | return parent; | ||
123 | } | ||
124 | |||
125 | static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **path) | ||
126 | { | ||
127 | struct udev *udev = udev_device_get_udev(parent); | ||
128 | struct udev_device *targetdev; | ||
129 | struct udev_device *target_parent; | ||
130 | struct udev_device *sasdev; | ||
131 | const char *sas_address; | ||
132 | char *lun = NULL; | ||
133 | |||
134 | targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target"); | ||
135 | if (targetdev == NULL) | ||
136 | return NULL; | ||
137 | |||
138 | target_parent = udev_device_get_parent(targetdev); | ||
139 | if (target_parent == NULL) | ||
140 | return NULL; | ||
141 | |||
142 | sasdev = udev_device_new_from_subsystem_sysname(udev, "sas_device", | ||
143 | udev_device_get_sysname(target_parent)); | ||
144 | if (sasdev == NULL) | ||
145 | return NULL; | ||
146 | |||
147 | sas_address = udev_device_get_sysattr_value(sasdev, "sas_address"); | ||
148 | if (sas_address == NULL) { | ||
149 | parent = NULL; | ||
150 | goto out; | ||
151 | } | ||
152 | |||
153 | format_lun_number(parent, &lun); | ||
154 | path_prepend(path, "sas-%s-%s", sas_address, lun); | ||
155 | if (lun) | ||
156 | free(lun); | ||
157 | out: | ||
158 | udev_device_unref(sasdev); | ||
159 | return parent; | ||
160 | } | ||
161 | |||
162 | static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **path) | ||
163 | { | ||
164 | struct udev *udev = udev_device_get_udev(parent); | ||
165 | struct udev_device *transportdev; | ||
166 | struct udev_device *sessiondev = NULL; | ||
167 | const char *target; | ||
168 | char *connname; | ||
169 | struct udev_device *conndev = NULL; | ||
170 | const char *addr; | ||
171 | const char *port; | ||
172 | char *lun = NULL; | ||
173 | |||
174 | /* find iscsi session */ | ||
175 | transportdev = parent; | ||
176 | for (;;) { | ||
177 | transportdev = udev_device_get_parent(transportdev); | ||
178 | if (transportdev == NULL) | ||
179 | return NULL; | ||
180 | if (strncmp(udev_device_get_sysname(transportdev), "session", 7) == 0) | ||
181 | break; | ||
182 | } | ||
183 | |||
184 | /* find iscsi session device */ | ||
185 | sessiondev = udev_device_new_from_subsystem_sysname(udev, "iscsi_session", udev_device_get_sysname(transportdev)); | ||
186 | if (sessiondev == NULL) | ||
187 | return NULL; | ||
188 | target = udev_device_get_sysattr_value(sessiondev, "targetname"); | ||
189 | if (target == NULL) { | ||
190 | parent = NULL; | ||
191 | goto out; | ||
192 | } | ||
193 | |||
194 | if (asprintf(&connname, "connection%s:0", udev_device_get_sysnum(transportdev)) < 0) { | ||
195 | parent = NULL; | ||
196 | goto out; | ||
197 | } | ||
198 | conndev = udev_device_new_from_subsystem_sysname(udev, "iscsi_connection", connname); | ||
199 | free(connname); | ||
200 | if (conndev == NULL) { | ||
201 | parent = NULL; | ||
202 | goto out; | ||
203 | } | ||
204 | addr = udev_device_get_sysattr_value(conndev, "persistent_address"); | ||
205 | port = udev_device_get_sysattr_value(conndev, "persistent_port"); | ||
206 | if (addr == NULL || port == NULL) { | ||
207 | parent = NULL; | ||
208 | goto out; | ||
209 | } | ||
210 | |||
211 | format_lun_number(parent, &lun); | ||
212 | path_prepend(path, "ip-%s:%s-iscsi-%s-%s", addr, port, target, lun); | ||
213 | if (lun) | ||
214 | free(lun); | ||
215 | out: | ||
216 | udev_device_unref(sessiondev); | ||
217 | udev_device_unref(conndev); | ||
218 | return parent; | ||
219 | } | ||
220 | |||
221 | static struct udev_device *handle_scsi_default(struct udev_device *parent, char **path) | ||
222 | { | ||
223 | struct udev_device *hostdev; | ||
224 | int host, bus, target, lun; | ||
225 | const char *name; | ||
226 | char *base; | ||
227 | char *pos; | ||
228 | DIR *dir; | ||
229 | struct dirent *dent; | ||
230 | int basenum; | ||
231 | |||
232 | hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host"); | ||
233 | if (hostdev == NULL) | ||
234 | return NULL; | ||
235 | |||
236 | name = udev_device_get_sysname(parent); | ||
237 | if (sscanf(name, "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) | ||
238 | return NULL; | ||
239 | |||
240 | /* | ||
241 | * Rebase host offset to get the local relative number | ||
242 | * | ||
243 | * Note: This is by definition racy, unreliable and too simple. | ||
244 | * Please do not copy this model anywhere. It's just a left-over | ||
245 | * from the time we had no idea how things should look like in | ||
246 | * the end. | ||
247 | * | ||
248 | * Making assumptions about a global in-kernel counter and use | ||
249 | * that to calculate a local offset is a very broken concept. It | ||
250 | * can only work as long as things are in strict order. | ||
251 | * | ||
252 | * The kernel needs to export the instance/port number of a | ||
253 | * controller directly, without the need for rebase magic like | ||
254 | * this. Manual driver unbind/bind, parallel hotplug/unplug will | ||
255 | * get into the way of this "I hope it works" logic. | ||
256 | */ | ||
257 | basenum = -1; | ||
258 | base = strdup(udev_device_get_syspath(hostdev)); | ||
259 | if (base == NULL) | ||
260 | return NULL; | ||
261 | pos = strrchr(base, '/'); | ||
262 | if (pos == NULL) { | ||
263 | parent = NULL; | ||
264 | goto out; | ||
265 | } | ||
266 | pos[0] = '\0'; | ||
267 | dir = opendir(base); | ||
268 | if (dir == NULL) { | ||
269 | parent = NULL; | ||
270 | goto out; | ||
271 | } | ||
272 | for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { | ||
273 | char *rest; | ||
274 | int i; | ||
275 | |||
276 | if (dent->d_name[0] == '.') | ||
277 | continue; | ||
278 | if (dent->d_type != DT_DIR && dent->d_type != DT_LNK) | ||
279 | continue; | ||
280 | if (strncmp(dent->d_name, "host", 4) != 0) | ||
281 | continue; | ||
282 | i = strtoul(&dent->d_name[4], &rest, 10); | ||
283 | if (rest[0] != '\0') | ||
284 | continue; | ||
285 | /* | ||
286 | * find the smallest number; the host really needs to export its | ||
287 | * own instance number per parent device; relying on the global host | ||
288 | * enumeration and plainly rebasing the numbers sounds unreliable | ||
289 | */ | ||
290 | if (basenum == -1 || i < basenum) | ||
291 | basenum = i; | ||
292 | } | ||
293 | closedir(dir); | ||
294 | if (basenum == -1) { | ||
295 | parent = NULL; | ||
296 | goto out; | ||
297 | } | ||
298 | host -= basenum; | ||
299 | |||
300 | path_prepend(path, "scsi-%u:%u:%u:%u", host, bus, target, lun); | ||
301 | out: | ||
302 | free(base); | ||
303 | return hostdev; | ||
304 | } | ||
305 | |||
306 | static struct udev_device *handle_scsi(struct udev_device *parent, char **path) | ||
307 | { | ||
308 | const char *devtype; | ||
309 | const char *name; | ||
310 | const char *id; | ||
311 | |||
312 | devtype = udev_device_get_devtype(parent); | ||
313 | if (devtype == NULL || strcmp(devtype, "scsi_device") != 0) | ||
314 | return parent; | ||
315 | |||
316 | /* firewire */ | ||
317 | id = udev_device_get_sysattr_value(parent, "ieee1394_id"); | ||
318 | if (id != NULL) { | ||
319 | parent = skip_subsystem(parent, "scsi"); | ||
320 | path_prepend(path, "ieee1394-0x%s", id); | ||
321 | goto out; | ||
322 | } | ||
323 | |||
324 | /* lousy scsi sysfs does not have a "subsystem" for the transport */ | ||
325 | name = udev_device_get_syspath(parent); | ||
326 | |||
327 | if (strstr(name, "/rport-") != NULL) { | ||
328 | parent = handle_scsi_fibre_channel(parent, path); | ||
329 | goto out; | ||
330 | } | ||
331 | |||
332 | if (strstr(name, "/end_device-") != NULL) { | ||
333 | parent = handle_scsi_sas(parent, path); | ||
334 | goto out; | ||
335 | } | ||
336 | |||
337 | if (strstr(name, "/session") != NULL) { | ||
338 | parent = handle_scsi_iscsi(parent, path); | ||
339 | goto out; | ||
340 | } | ||
341 | |||
342 | /* | ||
343 | * We do not support the ATA transport class, it creates duplicated link | ||
344 | * names as the fake SCSI host adapters are all separated, they are all | ||
345 | * re-based as host == 0. ATA should just stop faking two duplicated | ||
346 | * hierarchies for a single topology and leave the SCSI stuff alone; | ||
347 | * until that happens, there are no by-path/ links for ATA devices behind | ||
348 | * an ATA transport class. | ||
349 | */ | ||
350 | if (strstr(name, "/ata") != NULL) { | ||
351 | parent = NULL; | ||
352 | goto out; | ||
353 | } | ||
354 | |||
355 | parent = handle_scsi_default(parent, path); | ||
356 | out: | ||
357 | return parent; | ||
358 | } | ||
359 | |||
360 | static struct udev_device *handle_cciss(struct udev_device *parent, char **path) | ||
361 | { | ||
362 | const char *str; | ||
363 | unsigned int controller, disk; | ||
364 | |||
365 | str = udev_device_get_sysname(parent); | ||
366 | if (sscanf(str, "c%ud%u%*s", &controller, &disk) != 2) | ||
367 | return NULL; | ||
368 | |||
369 | path_prepend(path, "cciss-disk%u", disk); | ||
370 | parent = skip_subsystem(parent, "cciss"); | ||
371 | return parent; | ||
372 | } | ||
373 | |||
374 | static void handle_scsi_tape(struct udev_device *dev, char **path) | ||
375 | { | ||
376 | const char *name; | ||
377 | |||
378 | /* must be the last device in the syspath */ | ||
379 | if (*path != NULL) | ||
380 | return; | ||
381 | |||
382 | name = udev_device_get_sysname(dev); | ||
383 | if (strncmp(name, "nst", 3) == 0 && strchr("lma", name[3]) != NULL) | ||
384 | path_prepend(path, "nst%c", name[3]); | ||
385 | else if (strncmp(name, "st", 2) == 0 && strchr("lma", name[2]) != NULL) | ||
386 | path_prepend(path, "st%c", name[2]); | ||
387 | } | ||
388 | |||
389 | static struct udev_device *handle_usb(struct udev_device *parent, char **path) | ||
390 | { | ||
391 | const char *devtype; | ||
392 | const char *str; | ||
393 | const char *port; | ||
394 | |||
395 | devtype = udev_device_get_devtype(parent); | ||
396 | if (devtype == NULL) | ||
397 | return parent; | ||
398 | if (strcmp(devtype, "usb_interface") != 0 && strcmp(devtype, "usb_device") != 0) | ||
399 | return parent; | ||
400 | |||
401 | str = udev_device_get_sysname(parent); | ||
402 | port = strchr(str, '-'); | ||
403 | if (port == NULL) | ||
404 | return parent; | ||
405 | port++; | ||
406 | |||
407 | parent = skip_subsystem(parent, "usb"); | ||
408 | path_prepend(path, "usb-0:%s", port); | ||
409 | return parent; | ||
410 | } | ||
411 | |||
412 | static struct udev_device *handle_ccw(struct udev_device *parent, struct udev_device *dev, char **path) | ||
413 | { | ||
414 | struct udev_device *scsi_dev; | ||
415 | |||
416 | scsi_dev = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device"); | ||
417 | if (scsi_dev != NULL) { | ||
418 | const char *wwpn; | ||
419 | const char *lun; | ||
420 | const char *hba_id; | ||
421 | |||
422 | hba_id = udev_device_get_sysattr_value(scsi_dev, "hba_id"); | ||
423 | wwpn = udev_device_get_sysattr_value(scsi_dev, "wwpn"); | ||
424 | lun = udev_device_get_sysattr_value(scsi_dev, "fcp_lun"); | ||
425 | if (hba_id != NULL && lun != NULL && wwpn != NULL) { | ||
426 | path_prepend(path, "ccw-%s-zfcp-%s:%s", hba_id, wwpn, lun); | ||
427 | goto out; | ||
428 | } | ||
429 | } | ||
430 | |||
431 | path_prepend(path, "ccw-%s", udev_device_get_sysname(parent)); | ||
432 | out: | ||
433 | parent = skip_subsystem(parent, "ccw"); | ||
434 | return parent; | ||
435 | } | ||
436 | |||
437 | static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool test) | ||
438 | { | ||
439 | struct udev_device *parent; | ||
440 | char *path = NULL; | ||
441 | bool some_transport = false; | ||
442 | |||
443 | /* S390 ccw bus */ | ||
444 | parent = udev_device_get_parent_with_subsystem_devtype(dev, "ccw", NULL); | ||
445 | if (parent != NULL) { | ||
446 | handle_ccw(parent, dev, &path); | ||
447 | goto out; | ||
448 | } | ||
449 | |||
450 | /* walk up the chain of devices and compose path */ | ||
451 | parent = dev; | ||
452 | while (parent != NULL) { | ||
453 | const char *subsys; | ||
454 | |||
455 | subsys = udev_device_get_subsystem(parent); | ||
456 | if (subsys == NULL) { | ||
457 | ; | ||
458 | } else if (strcmp(subsys, "scsi_tape") == 0) { | ||
459 | handle_scsi_tape(parent, &path); | ||
460 | } else if (strcmp(subsys, "scsi") == 0) { | ||
461 | parent = handle_scsi(parent, &path); | ||
462 | some_transport = true; | ||
463 | } else if (strcmp(subsys, "cciss") == 0) { | ||
464 | parent = handle_cciss(parent, &path); | ||
465 | some_transport = true; | ||
466 | } else if (strcmp(subsys, "usb") == 0) { | ||
467 | parent = handle_usb(parent, &path); | ||
468 | some_transport = true; | ||
469 | } else if (strcmp(subsys, "serio") == 0) { | ||
470 | path_prepend(&path, "serio-%s", udev_device_get_sysnum(parent)); | ||
471 | parent = skip_subsystem(parent, "serio"); | ||
472 | } else if (strcmp(subsys, "pci") == 0) { | ||
473 | path_prepend(&path, "pci-%s", udev_device_get_sysname(parent)); | ||
474 | parent = skip_subsystem(parent, "pci"); | ||
475 | } else if (strcmp(subsys, "platform") == 0) { | ||
476 | path_prepend(&path, "platform-%s", udev_device_get_sysname(parent)); | ||
477 | parent = skip_subsystem(parent, "platform"); | ||
478 | some_transport = true; | ||
479 | } else if (strcmp(subsys, "acpi") == 0) { | ||
480 | path_prepend(&path, "acpi-%s", udev_device_get_sysname(parent)); | ||
481 | parent = skip_subsystem(parent, "acpi"); | ||
482 | } else if (strcmp(subsys, "xen") == 0) { | ||
483 | path_prepend(&path, "xen-%s", udev_device_get_sysname(parent)); | ||
484 | parent = skip_subsystem(parent, "xen"); | ||
485 | } else if (strcmp(subsys, "virtio") == 0) { | ||
486 | path_prepend(&path, "virtio-pci-%s", udev_device_get_sysname(parent)); | ||
487 | parent = skip_subsystem(parent, "virtio"); | ||
488 | } | ||
489 | |||
490 | parent = udev_device_get_parent(parent); | ||
491 | } | ||
492 | |||
493 | /* | ||
494 | * Do not return a single-parent-device-only for block | ||
495 | * devices, they might have entire buses behind it which | ||
496 | * do not get unique IDs only by using the parent device. | ||
497 | */ | ||
498 | if (!some_transport && !strcmp(udev_device_get_subsystem(dev), "block")) { | ||
499 | free(path); | ||
500 | path = NULL; | ||
501 | } | ||
502 | |||
503 | out: | ||
504 | if (path != NULL) { | ||
505 | char tag[UTIL_NAME_SIZE]; | ||
506 | size_t i; | ||
507 | const char *p; | ||
508 | |||
509 | /* compose valid udev tag name */ | ||
510 | for (p = path, i = 0; *p; p++) { | ||
511 | if ((*p >= '0' && *p <= '9') || | ||
512 | (*p >= 'A' && *p <= 'Z') || | ||
513 | (*p >= 'a' && *p <= 'z') || | ||
514 | *p == '-') { | ||
515 | tag[i++] = *p; | ||
516 | continue; | ||
517 | } | ||
518 | |||
519 | /* skip all leading '_' */ | ||
520 | if (i == 0) | ||
521 | continue; | ||
522 | |||
523 | /* avoid second '_' */ | ||
524 | if (tag[i-1] == '_') | ||
525 | continue; | ||
526 | |||
527 | tag[i++] = '_'; | ||
528 | } | ||
529 | /* strip trailing '_' */ | ||
530 | while (i > 0 && tag[i-1] == '_') | ||
531 | i--; | ||
532 | tag[i] = '\0'; | ||
533 | |||
534 | udev_builtin_add_property(dev, test, "ID_PATH", path); | ||
535 | udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag); | ||
536 | free(path); | ||
537 | return EXIT_SUCCESS; | ||
538 | } | ||
539 | return EXIT_FAILURE; | ||
540 | } | ||
541 | |||
542 | const struct udev_builtin udev_builtin_path_id = { | ||
543 | .name = "path_id", | ||
544 | .cmd = builtin_path_id, | ||
545 | .help = "compose persistent device path", | ||
546 | .run_once = true, | ||
547 | }; |
File src/udev-builtin-usb_id.c added (mode: 100644) (index 0000000..85828e3) | |||
1 | /* | ||
2 | * USB device properties and persistent device path | ||
3 | * | ||
4 | * Copyright (c) 2005 SUSE Linux Products GmbH, Germany | ||
5 | * Author: Hannes Reinecke <hare@suse.de> | ||
6 | * | ||
7 | * Copyright (C) 2005-2011 Kay Sievers <kay.sievers@vrfy.org> | ||
8 | * | ||
9 | * This program is free software: you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation, either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
21 | */ | ||
22 | |||
23 | #include <stdio.h> | ||
24 | #include <stdlib.h> | ||
25 | #include <stdarg.h> | ||
26 | #include <unistd.h> | ||
27 | #include <string.h> | ||
28 | #include <ctype.h> | ||
29 | #include <fcntl.h> | ||
30 | #include <errno.h> | ||
31 | |||
32 | #include "udev.h" | ||
33 | |||
34 | static void set_usb_iftype(char *to, int if_class_num, size_t len) | ||
35 | { | ||
36 | char *type = "generic"; | ||
37 | |||
38 | switch (if_class_num) { | ||
39 | case 1: | ||
40 | type = "audio"; | ||
41 | break; | ||
42 | case 2: /* CDC-Control */ | ||
43 | break; | ||
44 | case 3: | ||
45 | type = "hid"; | ||
46 | break; | ||
47 | case 5: /* Physical */ | ||
48 | break; | ||
49 | case 6: | ||
50 | type = "media"; | ||
51 | break; | ||
52 | case 7: | ||
53 | type = "printer"; | ||
54 | break; | ||
55 | case 8: | ||
56 | type = "storage"; | ||
57 | break; | ||
58 | case 9: | ||
59 | type = "hub"; | ||
60 | break; | ||
61 | case 0x0a: /* CDC-Data */ | ||
62 | break; | ||
63 | case 0x0b: /* Chip/Smart Card */ | ||
64 | break; | ||
65 | case 0x0d: /* Content Security */ | ||
66 | break; | ||
67 | case 0x0e: | ||
68 | type = "video"; | ||
69 | break; | ||
70 | case 0xdc: /* Diagnostic Device */ | ||
71 | break; | ||
72 | case 0xe0: /* Wireless Controller */ | ||
73 | break; | ||
74 | case 0xfe: /* Application-specific */ | ||
75 | break; | ||
76 | case 0xff: /* Vendor-specific */ | ||
77 | break; | ||
78 | default: | ||
79 | break; | ||
80 | } | ||
81 | strncpy(to, type, len); | ||
82 | to[len-1] = '\0'; | ||
83 | } | ||
84 | |||
85 | static int set_usb_mass_storage_ifsubtype(char *to, const char *from, size_t len) | ||
86 | { | ||
87 | int type_num = 0; | ||
88 | char *eptr; | ||
89 | char *type = "generic"; | ||
90 | |||
91 | type_num = strtoul(from, &eptr, 0); | ||
92 | if (eptr != from) { | ||
93 | switch (type_num) { | ||
94 | case 2: | ||
95 | type = "atapi"; | ||
96 | break; | ||
97 | case 3: | ||
98 | type = "tape"; | ||
99 | break; | ||
100 | case 4: /* UFI */ | ||
101 | case 5: /* SFF-8070i */ | ||
102 | type = "floppy"; | ||
103 | break; | ||
104 | case 1: /* RBC devices */ | ||
105 | type = "rbc"; | ||
106 | break; | ||
107 | case 6: /* Transparent SPC-2 devices */ | ||
108 | type = "scsi"; | ||
109 | break; | ||
110 | default: | ||
111 | break; | ||
112 | } | ||
113 | } | ||
114 | util_strscpy(to, len, type); | ||
115 | return type_num; | ||
116 | } | ||
117 | |||
118 | static void set_scsi_type(char *to, const char *from, size_t len) | ||
119 | { | ||
120 | int type_num; | ||
121 | char *eptr; | ||
122 | char *type = "generic"; | ||
123 | |||
124 | type_num = strtoul(from, &eptr, 0); | ||
125 | if (eptr != from) { | ||
126 | switch (type_num) { | ||
127 | case 0: | ||
128 | case 0xe: | ||
129 | type = "disk"; | ||
130 | break; | ||
131 | case 1: | ||
132 | type = "tape"; | ||
133 | break; | ||
134 | case 4: | ||
135 | case 7: | ||
136 | case 0xf: | ||
137 | type = "optical"; | ||
138 | break; | ||
139 | case 5: | ||
140 | type = "cd"; | ||
141 | break; | ||
142 | default: | ||
143 | break; | ||
144 | } | ||
145 | } | ||
146 | util_strscpy(to, len, type); | ||
147 | } | ||
148 | |||
149 | #define USB_DT_DEVICE 0x01 | ||
150 | #define USB_DT_INTERFACE 0x04 | ||
151 | |||
152 | static int dev_if_packed_info(struct udev_device *dev, char *ifs_str, size_t len) | ||
153 | { | ||
154 | char *filename = NULL; | ||
155 | int fd; | ||
156 | ssize_t size; | ||
157 | unsigned char buf[18 + 65535]; | ||
158 | unsigned int pos, strpos; | ||
159 | struct usb_interface_descriptor { | ||
160 | u_int8_t bLength; | ||
161 | u_int8_t bDescriptorType; | ||
162 | u_int8_t bInterfaceNumber; | ||
163 | u_int8_t bAlternateSetting; | ||
164 | u_int8_t bNumEndpoints; | ||
165 | u_int8_t bInterfaceClass; | ||
166 | u_int8_t bInterfaceSubClass; | ||
167 | u_int8_t bInterfaceProtocol; | ||
168 | u_int8_t iInterface; | ||
169 | } __attribute__((packed)); | ||
170 | int err = 0; | ||
171 | |||
172 | if (asprintf(&filename, "%s/descriptors", udev_device_get_syspath(dev)) < 0) { | ||
173 | err = -1; | ||
174 | goto out; | ||
175 | } | ||
176 | fd = open(filename, O_RDONLY|O_CLOEXEC); | ||
177 | if (fd < 0) { | ||
178 | fprintf(stderr, "error opening USB device 'descriptors' file\n"); | ||
179 | err = -1; | ||
180 | goto out; | ||
181 | } | ||
182 | size = read(fd, buf, sizeof(buf)); | ||
183 | close(fd); | ||
184 | if (size < 18 || size == sizeof(buf)) { | ||
185 | err = -1; | ||
186 | goto out; | ||
187 | } | ||
188 | |||
189 | pos = 0; | ||
190 | strpos = 0; | ||
191 | ifs_str[0] = '\0'; | ||
192 | while (pos < sizeof(buf) && strpos+7 < len-2) { | ||
193 | struct usb_interface_descriptor *desc; | ||
194 | char if_str[8]; | ||
195 | |||
196 | desc = (struct usb_interface_descriptor *) &buf[pos]; | ||
197 | if (desc->bLength < 3) | ||
198 | break; | ||
199 | pos += desc->bLength; | ||
200 | |||
201 | if (desc->bDescriptorType != USB_DT_INTERFACE) | ||
202 | continue; | ||
203 | |||
204 | if (snprintf(if_str, 8, ":%02x%02x%02x", | ||
205 | desc->bInterfaceClass, | ||
206 | desc->bInterfaceSubClass, | ||
207 | desc->bInterfaceProtocol) != 7) | ||
208 | continue; | ||
209 | |||
210 | if (strstr(ifs_str, if_str) != NULL) | ||
211 | continue; | ||
212 | |||
213 | memcpy(&ifs_str[strpos], if_str, 8), | ||
214 | strpos += 7; | ||
215 | } | ||
216 | if (strpos > 0) { | ||
217 | ifs_str[strpos++] = ':'; | ||
218 | ifs_str[strpos++] = '\0'; | ||
219 | } | ||
220 | out: | ||
221 | free(filename); | ||
222 | return err; | ||
223 | } | ||
224 | |||
225 | /* | ||
226 | * A unique USB identification is generated like this: | ||
227 | * | ||
228 | * 1.) Get the USB device type from InterfaceClass and InterfaceSubClass | ||
229 | * 2.) If the device type is 'Mass-Storage/SPC-2' or 'Mass-Storage/RBC' | ||
230 | * use the SCSI vendor and model as USB-Vendor and USB-model. | ||
231 | * 3.) Otherwise use the USB manufacturer and product as | ||
232 | * USB-Vendor and USB-model. Any non-printable characters | ||
233 | * in those strings will be skipped; a slash '/' will be converted | ||
234 | * into a full stop '.'. | ||
235 | * 4.) If that fails, too, we will use idVendor and idProduct | ||
236 | * as USB-Vendor and USB-model. | ||
237 | * 5.) The USB identification is the USB-vendor and USB-model | ||
238 | * string concatenated with an underscore '_'. | ||
239 | * 6.) If the device supplies a serial number, this number | ||
240 | * is concatenated with the identification with an underscore '_'. | ||
241 | */ | ||
242 | static int builtin_usb_id(struct udev_device *dev, int argc, char *argv[], bool test) | ||
243 | { | ||
244 | char vendor_str[64]; | ||
245 | char vendor_str_enc[256]; | ||
246 | const char *vendor_id; | ||
247 | char model_str[64]; | ||
248 | char model_str_enc[256]; | ||
249 | const char *product_id; | ||
250 | char serial_str[UTIL_NAME_SIZE]; | ||
251 | char packed_if_str[UTIL_NAME_SIZE]; | ||
252 | char revision_str[64]; | ||
253 | char type_str[64]; | ||
254 | char instance_str[64]; | ||
255 | const char *ifnum = NULL; | ||
256 | const char *driver = NULL; | ||
257 | char serial[256]; | ||
258 | |||
259 | struct udev *udev = udev_device_get_udev(dev); | ||
260 | struct udev_device *dev_interface = NULL; | ||
261 | struct udev_device *dev_usb = NULL; | ||
262 | const char *if_class, *if_subclass; | ||
263 | int if_class_num; | ||
264 | int protocol = 0; | ||
265 | size_t l; | ||
266 | char *s; | ||
267 | |||
268 | vendor_str[0] = '\0'; | ||
269 | model_str[0] = '\0'; | ||
270 | serial_str[0] = '\0'; | ||
271 | packed_if_str[0] = '\0'; | ||
272 | revision_str[0] = '\0'; | ||
273 | type_str[0] = '\0'; | ||
274 | instance_str[0] = '\0'; | ||
275 | |||
276 | dbg(udev, "syspath %s\n", udev_device_get_syspath(dev)); | ||
277 | |||
278 | /* shortcut, if we are called directly for a "usb_device" type */ | ||
279 | if (udev_device_get_devtype(dev) != NULL && strcmp(udev_device_get_devtype(dev), "usb_device") == 0) { | ||
280 | dev_if_packed_info(dev, packed_if_str, sizeof(packed_if_str)); | ||
281 | dev_usb = dev; | ||
282 | goto fallback; | ||
283 | } | ||
284 | |||
285 | /* usb interface directory */ | ||
286 | dev_interface = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface"); | ||
287 | if (dev_interface == NULL) { | ||
288 | info(udev, "unable to access usb_interface device of '%s'\n", | ||
289 | udev_device_get_syspath(dev)); | ||
290 | return EXIT_FAILURE; | ||
291 | } | ||
292 | |||
293 | ifnum = udev_device_get_sysattr_value(dev_interface, "bInterfaceNumber"); | ||
294 | driver = udev_device_get_sysattr_value(dev_interface, "driver"); | ||
295 | |||
296 | if_class = udev_device_get_sysattr_value(dev_interface, "bInterfaceClass"); | ||
297 | if (!if_class) { | ||
298 | info(udev, "%s: cannot get bInterfaceClass attribute\n", | ||
299 | udev_device_get_sysname(dev)); | ||
300 | return EXIT_FAILURE; | ||
301 | } | ||
302 | |||
303 | if_class_num = strtoul(if_class, NULL, 16); | ||
304 | if (if_class_num == 8) { | ||
305 | /* mass storage */ | ||
306 | if_subclass = udev_device_get_sysattr_value(dev_interface, "bInterfaceSubClass"); | ||
307 | if (if_subclass != NULL) | ||
308 | protocol = set_usb_mass_storage_ifsubtype(type_str, if_subclass, sizeof(type_str)-1); | ||
309 | } else { | ||
310 | set_usb_iftype(type_str, if_class_num, sizeof(type_str)-1); | ||
311 | } | ||
312 | |||
313 | info(udev, "%s: if_class %d protocol %d\n", | ||
314 | udev_device_get_syspath(dev_interface), if_class_num, protocol); | ||
315 | |||
316 | /* usb device directory */ | ||
317 | dev_usb = udev_device_get_parent_with_subsystem_devtype(dev_interface, "usb", "usb_device"); | ||
318 | if (!dev_usb) { | ||
319 | info(udev, "unable to find parent 'usb' device of '%s'\n", | ||
320 | udev_device_get_syspath(dev)); | ||
321 | return EXIT_FAILURE; | ||
322 | } | ||
323 | |||
324 | /* all interfaces of the device in a single string */ | ||
325 | dev_if_packed_info(dev_usb, packed_if_str, sizeof(packed_if_str)); | ||
326 | |||
327 | /* mass storage : SCSI or ATAPI */ | ||
328 | if ((protocol == 6 || protocol == 2)) { | ||
329 | struct udev_device *dev_scsi; | ||
330 | const char *scsi_model, *scsi_vendor, *scsi_type, *scsi_rev; | ||
331 | int host, bus, target, lun; | ||
332 | |||
333 | /* get scsi device */ | ||
334 | dev_scsi = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device"); | ||
335 | if (dev_scsi == NULL) { | ||
336 | info(udev, "unable to find parent 'scsi' device of '%s'\n", | ||
337 | udev_device_get_syspath(dev)); | ||
338 | goto fallback; | ||
339 | } | ||
340 | if (sscanf(udev_device_get_sysname(dev_scsi), "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) { | ||
341 | info(udev, "invalid scsi device '%s'\n", udev_device_get_sysname(dev_scsi)); | ||
342 | goto fallback; | ||
343 | } | ||
344 | |||
345 | /* Generic SPC-2 device */ | ||
346 | scsi_vendor = udev_device_get_sysattr_value(dev_scsi, "vendor"); | ||
347 | if (!scsi_vendor) { | ||
348 | info(udev, "%s: cannot get SCSI vendor attribute\n", | ||
349 | udev_device_get_sysname(dev_scsi)); | ||
350 | goto fallback; | ||
351 | } | ||
352 | udev_util_encode_string(scsi_vendor, vendor_str_enc, sizeof(vendor_str_enc)); | ||
353 | util_replace_whitespace(scsi_vendor, vendor_str, sizeof(vendor_str)-1); | ||
354 | util_replace_chars(vendor_str, NULL); | ||
355 | |||
356 | scsi_model = udev_device_get_sysattr_value(dev_scsi, "model"); | ||
357 | if (!scsi_model) { | ||
358 | info(udev, "%s: cannot get SCSI model attribute\n", | ||
359 | udev_device_get_sysname(dev_scsi)); | ||
360 | goto fallback; | ||
361 | } | ||
362 | udev_util_encode_string(scsi_model, model_str_enc, sizeof(model_str_enc)); | ||
363 | util_replace_whitespace(scsi_model, model_str, sizeof(model_str)-1); | ||
364 | util_replace_chars(model_str, NULL); | ||
365 | |||
366 | scsi_type = udev_device_get_sysattr_value(dev_scsi, "type"); | ||
367 | if (!scsi_type) { | ||
368 | info(udev, "%s: cannot get SCSI type attribute\n", | ||
369 | udev_device_get_sysname(dev_scsi)); | ||
370 | goto fallback; | ||
371 | } | ||
372 | set_scsi_type(type_str, scsi_type, sizeof(type_str)-1); | ||
373 | |||
374 | scsi_rev = udev_device_get_sysattr_value(dev_scsi, "rev"); | ||
375 | if (!scsi_rev) { | ||
376 | info(udev, "%s: cannot get SCSI revision attribute\n", | ||
377 | udev_device_get_sysname(dev_scsi)); | ||
378 | goto fallback; | ||
379 | } | ||
380 | util_replace_whitespace(scsi_rev, revision_str, sizeof(revision_str)-1); | ||
381 | util_replace_chars(revision_str, NULL); | ||
382 | |||
383 | /* | ||
384 | * some broken devices have the same identifiers | ||
385 | * for all luns, export the target:lun number | ||
386 | */ | ||
387 | sprintf(instance_str, "%d:%d", target, lun); | ||
388 | } | ||
389 | |||
390 | fallback: | ||
391 | vendor_id = udev_device_get_sysattr_value(dev_usb, "idVendor"); | ||
392 | product_id = udev_device_get_sysattr_value(dev_usb, "idProduct"); | ||
393 | |||
394 | /* fallback to USB vendor & device */ | ||
395 | if (vendor_str[0] == '\0') { | ||
396 | const char *usb_vendor = NULL; | ||
397 | |||
398 | usb_vendor = udev_device_get_sysattr_value(dev_usb, "manufacturer"); | ||
399 | if (!usb_vendor) | ||
400 | usb_vendor = vendor_id; | ||
401 | if (!usb_vendor) { | ||
402 | info(udev, "No USB vendor information available\n"); | ||
403 | return EXIT_FAILURE; | ||
404 | } | ||
405 | udev_util_encode_string(usb_vendor, vendor_str_enc, sizeof(vendor_str_enc)); | ||
406 | util_replace_whitespace(usb_vendor, vendor_str, sizeof(vendor_str)-1); | ||
407 | util_replace_chars(vendor_str, NULL); | ||
408 | } | ||
409 | |||
410 | if (model_str[0] == '\0') { | ||
411 | const char *usb_model = NULL; | ||
412 | |||
413 | usb_model = udev_device_get_sysattr_value(dev_usb, "product"); | ||
414 | if (!usb_model) | ||
415 | usb_model = product_id; | ||
416 | if (!usb_model) { | ||
417 | dbg(udev, "No USB model information available\n"); | ||
418 | return EXIT_FAILURE; | ||
419 | } | ||
420 | udev_util_encode_string(usb_model, model_str_enc, sizeof(model_str_enc)); | ||
421 | util_replace_whitespace(usb_model, model_str, sizeof(model_str)-1); | ||
422 | util_replace_chars(model_str, NULL); | ||
423 | } | ||
424 | |||
425 | if (revision_str[0] == '\0') { | ||
426 | const char *usb_rev; | ||
427 | |||
428 | usb_rev = udev_device_get_sysattr_value(dev_usb, "bcdDevice"); | ||
429 | if (usb_rev) { | ||
430 | util_replace_whitespace(usb_rev, revision_str, sizeof(revision_str)-1); | ||
431 | util_replace_chars(revision_str, NULL); | ||
432 | } | ||
433 | } | ||
434 | |||
435 | if (serial_str[0] == '\0') { | ||
436 | const char *usb_serial; | ||
437 | |||
438 | usb_serial = udev_device_get_sysattr_value(dev_usb, "serial"); | ||
439 | if (usb_serial) { | ||
440 | util_replace_whitespace(usb_serial, serial_str, sizeof(serial_str)-1); | ||
441 | util_replace_chars(serial_str, NULL); | ||
442 | } | ||
443 | } | ||
444 | |||
445 | s = serial; | ||
446 | l = util_strpcpyl(&s, sizeof(serial), vendor_str, "_", model_str, NULL); | ||
447 | if (serial_str[0] != '\0') | ||
448 | l = util_strpcpyl(&s, l, "_", serial_str, NULL); | ||
449 | |||
450 | if (instance_str[0] != '\0') | ||
451 | util_strpcpyl(&s, l, "-", instance_str, NULL); | ||
452 | |||
453 | udev_builtin_add_property(dev, test, "ID_VENDOR", vendor_str); | ||
454 | udev_builtin_add_property(dev, test, "ID_VENDOR_ENC", vendor_str_enc); | ||
455 | udev_builtin_add_property(dev, test, "ID_VENDOR_ID", vendor_id); | ||
456 | udev_builtin_add_property(dev, test, "ID_MODEL", model_str); | ||
457 | udev_builtin_add_property(dev, test, "ID_MODEL_ENC", model_str_enc); | ||
458 | udev_builtin_add_property(dev, test, "ID_MODEL_ID", product_id); | ||
459 | udev_builtin_add_property(dev, test, "ID_REVISION", revision_str); | ||
460 | udev_builtin_add_property(dev, test, "ID_SERIAL", serial); | ||
461 | if (serial_str[0] != '\0') | ||
462 | udev_builtin_add_property(dev, test, "ID_SERIAL_SHORT", serial_str); | ||
463 | if (type_str[0] != '\0') | ||
464 | udev_builtin_add_property(dev, test, "ID_TYPE", type_str); | ||
465 | if (instance_str[0] != '\0') | ||
466 | udev_builtin_add_property(dev, test, "ID_INSTANCE", instance_str); | ||
467 | udev_builtin_add_property(dev, test, "ID_BUS", "usb"); | ||
468 | if (packed_if_str[0] != '\0') | ||
469 | udev_builtin_add_property(dev, test, "ID_USB_INTERFACES", packed_if_str); | ||
470 | if (ifnum != NULL) | ||
471 | udev_builtin_add_property(dev, test, "ID_USB_INTERFACE_NUM", ifnum); | ||
472 | if (driver != NULL) | ||
473 | udev_builtin_add_property(dev, test, "ID_USB_DRIVER", driver); | ||
474 | return EXIT_SUCCESS; | ||
475 | } | ||
476 | |||
477 | const struct udev_builtin udev_builtin_usb_id = { | ||
478 | .name = "usb_id", | ||
479 | .cmd = builtin_usb_id, | ||
480 | .help = "usb device properties", | ||
481 | .run_once = true, | ||
482 | }; |
File src/udev-builtin.c added (mode: 100644) (index 0000000..a2fafda) | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 Kay Sievers <kay.sievers@vrfy.org> | ||
3 | * | ||
4 | * This program is free software: you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation, either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #include <unistd.h> | ||
19 | #include <stdio.h> | ||
20 | #include <stdlib.h> | ||
21 | #include <stddef.h> | ||
22 | #include <string.h> | ||
23 | #include <errno.h> | ||
24 | #include <getopt.h> | ||
25 | |||
26 | #include "udev.h" | ||
27 | |||
28 | static const struct udev_builtin *builtins[] = { | ||
29 | [UDEV_BUILTIN_BLKID] = &udev_builtin_blkid, | ||
30 | [UDEV_BUILTIN_FIRMWARE] = &udev_builtin_firmware, | ||
31 | [UDEV_BUILTIN_INPUT_ID] = &udev_builtin_input_id, | ||
32 | [UDEV_BUILTIN_KMOD] = &udev_builtin_kmod, | ||
33 | [UDEV_BUILTIN_PATH_ID] = &udev_builtin_path_id, | ||
34 | [UDEV_BUILTIN_PCI_DB] = &udev_builtin_pci_db, | ||
35 | [UDEV_BUILTIN_USB_DB] = &udev_builtin_usb_db, | ||
36 | [UDEV_BUILTIN_USB_ID] = &udev_builtin_usb_id, | ||
37 | }; | ||
38 | |||
39 | int udev_builtin_init(struct udev *udev) | ||
40 | { | ||
41 | unsigned int i; | ||
42 | int err = 0; | ||
43 | |||
44 | for (i = 0; i < ARRAY_SIZE(builtins); i++) { | ||
45 | if (builtins[i]->init) { | ||
46 | err = builtins[i]->init(udev); | ||
47 | if (err < 0) | ||
48 | break; | ||
49 | } | ||
50 | } | ||
51 | return err; | ||
52 | } | ||
53 | |||
54 | void udev_builtin_exit(struct udev *udev) | ||
55 | { | ||
56 | unsigned int i; | ||
57 | |||
58 | for (i = 0; i < ARRAY_SIZE(builtins); i++) | ||
59 | if (builtins[i]->exit) | ||
60 | builtins[i]->exit(udev); | ||
61 | } | ||
62 | |||
63 | bool udev_builtin_validate(struct udev *udev) | ||
64 | { | ||
65 | unsigned int i; | ||
66 | bool change = false; | ||
67 | |||
68 | for (i = 0; i < ARRAY_SIZE(builtins); i++) | ||
69 | if (builtins[i]->validate) | ||
70 | if (builtins[i]->validate(udev)) | ||
71 | change = true; | ||
72 | return change; | ||
73 | } | ||
74 | |||
75 | void udev_builtin_list(struct udev *udev) | ||
76 | { | ||
77 | unsigned int i; | ||
78 | |||
79 | for (i = 0; i < ARRAY_SIZE(builtins); i++) | ||
80 | fprintf(stderr, " %-12s %s\n", builtins[i]->name, builtins[i]->help); | ||
81 | } | ||
82 | |||
83 | const char *udev_builtin_name(enum udev_builtin_cmd cmd) | ||
84 | { | ||
85 | return builtins[cmd]->name; | ||
86 | } | ||
87 | |||
88 | bool udev_builtin_run_once(enum udev_builtin_cmd cmd) | ||
89 | { | ||
90 | return builtins[cmd]->run_once; | ||
91 | } | ||
92 | |||
93 | enum udev_builtin_cmd udev_builtin_lookup(const char *command) | ||
94 | { | ||
95 | char name[UTIL_PATH_SIZE]; | ||
96 | enum udev_builtin_cmd i; | ||
97 | char *pos; | ||
98 | |||
99 | util_strscpy(name, sizeof(name), command); | ||
100 | pos = strchr(name, ' '); | ||
101 | if (pos) | ||
102 | pos[0] = '\0'; | ||
103 | for (i = 0; i < ARRAY_SIZE(builtins); i++) | ||
104 | if (strcmp(builtins[i]->name, name) == 0) | ||
105 | return i; | ||
106 | return UDEV_BUILTIN_MAX; | ||
107 | } | ||
108 | |||
109 | int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test) | ||
110 | { | ||
111 | char arg[UTIL_PATH_SIZE]; | ||
112 | int argc; | ||
113 | char *argv[128]; | ||
114 | |||
115 | optind = 0; | ||
116 | util_strscpy(arg, sizeof(arg), command); | ||
117 | udev_build_argv(udev_device_get_udev(dev), arg, &argc, argv); | ||
118 | return builtins[cmd]->cmd(dev, argc, argv, test); | ||
119 | } | ||
120 | |||
121 | int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val) | ||
122 | { | ||
123 | struct udev_list_entry *entry; | ||
124 | |||
125 | entry = udev_device_add_property(dev, key, val); | ||
126 | /* store in db, skip private keys */ | ||
127 | if (key[0] != '.') | ||
128 | udev_list_entry_set_num(entry, true); | ||
129 | |||
130 | info(udev_device_get_udev(dev), "%s=%s\n", key, val); | ||
131 | if (test) | ||
132 | printf("%s=%s\n", key, val); | ||
133 | return 0; | ||
134 | } |
File src/udev-ctrl.c added (mode: 100644) (index 0000000..5556f1a) | |||
1 | /* | ||
2 | * libudev - interface to udev device information | ||
3 | * | ||
4 | * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org> | ||
5 | * | ||
6 | * This library is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU Lesser General Public | ||
8 | * License as published by the Free Software Foundation; either | ||
9 | * version 2.1 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <errno.h> | ||
13 | #include <stdio.h> | ||
14 | #include <stdlib.h> | ||
15 | #include <stddef.h> | ||
16 | #include <string.h> | ||
17 | #include <unistd.h> | ||
18 | #include <sys/types.h> | ||
19 | #include <sys/poll.h> | ||
20 | #include <sys/socket.h> | ||
21 | #include <sys/un.h> | ||
22 | |||
23 | #include "udev.h" | ||
24 | |||
25 | /* wire protocol magic must match */ | ||
26 | #define UDEV_CTRL_MAGIC 0xdead1dea | ||
27 | |||
28 | enum udev_ctrl_msg_type { | ||
29 | UDEV_CTRL_UNKNOWN, | ||
30 | UDEV_CTRL_SET_LOG_LEVEL, | ||
31 | UDEV_CTRL_STOP_EXEC_QUEUE, | ||
32 | UDEV_CTRL_START_EXEC_QUEUE, | ||
33 | UDEV_CTRL_RELOAD, | ||
34 | UDEV_CTRL_SET_ENV, | ||
35 | UDEV_CTRL_SET_CHILDREN_MAX, | ||
36 | UDEV_CTRL_PING, | ||
37 | UDEV_CTRL_EXIT, | ||
38 | }; | ||
39 | |||
40 | struct udev_ctrl_msg_wire { | ||
41 | char version[16]; | ||
42 | unsigned int magic; | ||
43 | enum udev_ctrl_msg_type type; | ||
44 | union { | ||
45 | int intval; | ||
46 | char buf[256]; | ||
47 | }; | ||
48 | }; | ||
49 | |||
50 | struct udev_ctrl_msg { | ||
51 | int refcount; | ||
52 | struct udev_ctrl_connection *conn; | ||
53 | struct udev_ctrl_msg_wire ctrl_msg_wire; | ||
54 | }; | ||
55 | |||
56 | struct udev_ctrl { | ||
57 | int refcount; | ||
58 | struct udev *udev; | ||
59 | int sock; | ||
60 | struct sockaddr_un saddr; | ||
61 | socklen_t addrlen; | ||
62 | bool bound; | ||
63 | bool cleanup_socket; | ||
64 | bool connected; | ||
65 | }; | ||
66 | |||
67 | struct udev_ctrl_connection { | ||
68 | int refcount; | ||
69 | struct udev_ctrl *uctrl; | ||
70 | int sock; | ||
71 | }; | ||
72 | |||
73 | struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd) | ||
74 | { | ||
75 | struct udev_ctrl *uctrl; | ||
76 | |||
77 | uctrl = calloc(1, sizeof(struct udev_ctrl)); | ||
78 | if (uctrl == NULL) | ||
79 | return NULL; | ||
80 | uctrl->refcount = 1; | ||
81 | uctrl->udev = udev; | ||
82 | |||
83 | if (fd < 0) { | ||
84 | uctrl->sock = socket(AF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); | ||
85 | if (uctrl->sock < 0) { | ||
86 | err(udev, "error getting socket: %m\n"); | ||
87 | udev_ctrl_unref(uctrl); | ||
88 | return NULL; | ||
89 | } | ||
90 | } else { | ||
91 | uctrl->bound = true; | ||
92 | uctrl->sock = fd; | ||
93 | } | ||
94 | |||
95 | uctrl->saddr.sun_family = AF_LOCAL; | ||
96 | util_strscpyl(uctrl->saddr.sun_path, sizeof(uctrl->saddr.sun_path), | ||
97 | udev_get_run_path(udev), "/control", NULL); | ||
98 | uctrl->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(uctrl->saddr.sun_path); | ||
99 | return uctrl; | ||
100 | } | ||
101 | |||
102 | struct udev_ctrl *udev_ctrl_new(struct udev *udev) | ||
103 | { | ||
104 | return udev_ctrl_new_from_fd(udev, -1); | ||
105 | } | ||
106 | |||
107 | int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) | ||
108 | { | ||
109 | int err; | ||
110 | |||
111 | if (!uctrl->bound) { | ||
112 | err = bind(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen); | ||
113 | if (err < 0 && errno == EADDRINUSE) { | ||
114 | unlink(uctrl->saddr.sun_path); | ||
115 | err = bind(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen); | ||
116 | } | ||
117 | |||
118 | if (err < 0) { | ||
119 | err = -errno; | ||
120 | err(uctrl->udev, "bind failed: %m\n"); | ||
121 | return err; | ||
122 | } | ||
123 | |||
124 | err = listen(uctrl->sock, 0); | ||
125 | if (err < 0) { | ||
126 | err = -errno; | ||
127 | err(uctrl->udev, "listen failed: %m\n"); | ||
128 | return err; | ||
129 | } | ||
130 | |||
131 | uctrl->bound = true; | ||
132 | uctrl->cleanup_socket = true; | ||
133 | } | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl) | ||
138 | { | ||
139 | return uctrl->udev; | ||
140 | } | ||
141 | |||
142 | struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl) | ||
143 | { | ||
144 | if (uctrl == NULL) | ||
145 | return NULL; | ||
146 | uctrl->refcount++; | ||
147 | return uctrl; | ||
148 | } | ||
149 | |||
150 | struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl) | ||
151 | { | ||
152 | if (uctrl == NULL) | ||
153 | return NULL; | ||
154 | uctrl->refcount--; | ||
155 | if (uctrl->refcount > 0) | ||
156 | return uctrl; | ||
157 | if (uctrl->sock >= 0) | ||
158 | close(uctrl->sock); | ||
159 | free(uctrl); | ||
160 | return NULL; | ||
161 | } | ||
162 | |||
163 | int udev_ctrl_cleanup(struct udev_ctrl *uctrl) | ||
164 | { | ||
165 | if (uctrl == NULL) | ||
166 | return 0; | ||
167 | if (uctrl->cleanup_socket) | ||
168 | unlink(uctrl->saddr.sun_path); | ||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | int udev_ctrl_get_fd(struct udev_ctrl *uctrl) | ||
173 | { | ||
174 | if (uctrl == NULL) | ||
175 | return -EINVAL; | ||
176 | return uctrl->sock; | ||
177 | } | ||
178 | |||
179 | struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) | ||
180 | { | ||
181 | struct udev_ctrl_connection *conn; | ||
182 | struct ucred ucred; | ||
183 | socklen_t slen; | ||
184 | const int on = 1; | ||
185 | |||
186 | conn = calloc(1, sizeof(struct udev_ctrl_connection)); | ||
187 | if (conn == NULL) | ||
188 | return NULL; | ||
189 | conn->refcount = 1; | ||
190 | conn->uctrl = uctrl; | ||
191 | |||
192 | conn->sock = accept4(uctrl->sock, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK); | ||
193 | if (conn->sock < 0) { | ||
194 | if (errno != EINTR) | ||
195 | err(uctrl->udev, "unable to receive ctrl connection: %m\n"); | ||
196 | goto err; | ||
197 | } | ||
198 | |||
199 | /* check peer credential of connection */ | ||
200 | slen = sizeof(ucred); | ||
201 | if (getsockopt(conn->sock, SOL_SOCKET, SO_PEERCRED, &ucred, &slen) < 0) { | ||
202 | err(uctrl->udev, "unable to receive credentials of ctrl connection: %m\n"); | ||
203 | goto err; | ||
204 | } | ||
205 | if (ucred.uid > 0) { | ||
206 | err(uctrl->udev, "sender uid=%i, message ignored\n", ucred.uid); | ||
207 | goto err; | ||
208 | } | ||
209 | |||
210 | /* enable receiving of the sender credentials in the messages */ | ||
211 | setsockopt(conn->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); | ||
212 | udev_ctrl_ref(uctrl); | ||
213 | return conn; | ||
214 | err: | ||
215 | if (conn->sock >= 0) | ||
216 | close(conn->sock); | ||
217 | free(conn); | ||
218 | return NULL; | ||
219 | } | ||
220 | |||
221 | struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn) | ||
222 | { | ||
223 | if (conn == NULL) | ||
224 | return NULL; | ||
225 | conn->refcount++; | ||
226 | return conn; | ||
227 | } | ||
228 | |||
229 | struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn) | ||
230 | { | ||
231 | if (conn == NULL) | ||
232 | return NULL; | ||
233 | conn->refcount--; | ||
234 | if (conn->refcount > 0) | ||
235 | return conn; | ||
236 | if (conn->sock >= 0) | ||
237 | close(conn->sock); | ||
238 | udev_ctrl_unref(conn->uctrl); | ||
239 | free(conn); | ||
240 | return NULL; | ||
241 | } | ||
242 | |||
243 | static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf, int timeout) | ||
244 | { | ||
245 | struct udev_ctrl_msg_wire ctrl_msg_wire; | ||
246 | int err = 0; | ||
247 | |||
248 | memset(&ctrl_msg_wire, 0x00, sizeof(struct udev_ctrl_msg_wire)); | ||
249 | strcpy(ctrl_msg_wire.version, "udev-" VERSION); | ||
250 | ctrl_msg_wire.magic = UDEV_CTRL_MAGIC; | ||
251 | ctrl_msg_wire.type = type; | ||
252 | |||
253 | if (buf != NULL) | ||
254 | util_strscpy(ctrl_msg_wire.buf, sizeof(ctrl_msg_wire.buf), buf); | ||
255 | else | ||
256 | ctrl_msg_wire.intval = intval; | ||
257 | |||
258 | if (!uctrl->connected) { | ||
259 | if (connect(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen) < 0) { | ||
260 | err = -errno; | ||
261 | goto out; | ||
262 | } | ||
263 | uctrl->connected = true; | ||
264 | } | ||
265 | if (send(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0) < 0) { | ||
266 | err = -errno; | ||
267 | goto out; | ||
268 | } | ||
269 | |||
270 | /* wait for peer message handling or disconnect */ | ||
271 | for (;;) { | ||
272 | struct pollfd pfd[1]; | ||
273 | int r; | ||
274 | |||
275 | pfd[0].fd = uctrl->sock; | ||
276 | pfd[0].events = POLLIN; | ||
277 | r = poll(pfd, 1, timeout * 1000); | ||
278 | if (r < 0) { | ||
279 | if (errno == EINTR) | ||
280 | continue; | ||
281 | err = -errno; | ||
282 | break; | ||
283 | } | ||
284 | |||
285 | if (r > 0 && pfd[0].revents & POLLERR) { | ||
286 | err = -EIO; | ||
287 | break; | ||
288 | } | ||
289 | |||
290 | if (r == 0) | ||
291 | err = -ETIMEDOUT; | ||
292 | break; | ||
293 | } | ||
294 | out: | ||
295 | return err; | ||
296 | } | ||
297 | |||
298 | int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout) | ||
299 | { | ||
300 | return ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL, timeout); | ||
301 | } | ||
302 | |||
303 | int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout) | ||
304 | { | ||
305 | return ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL, timeout); | ||
306 | } | ||
307 | |||
308 | int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout) | ||
309 | { | ||
310 | return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL, timeout); | ||
311 | } | ||
312 | |||
313 | int udev_ctrl_send_reload(struct udev_ctrl *uctrl, int timeout) | ||
314 | { | ||
315 | return ctrl_send(uctrl, UDEV_CTRL_RELOAD, 0, NULL, timeout); | ||
316 | } | ||
317 | |||
318 | int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout) | ||
319 | { | ||
320 | return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key, timeout); | ||
321 | } | ||
322 | |||
323 | int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout) | ||
324 | { | ||
325 | return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL, timeout); | ||
326 | } | ||
327 | |||
328 | int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout) | ||
329 | { | ||
330 | return ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL, timeout); | ||
331 | } | ||
332 | |||
333 | int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout) | ||
334 | { | ||
335 | return ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL, timeout); | ||
336 | } | ||
337 | |||
338 | struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) | ||
339 | { | ||
340 | struct udev *udev = conn->uctrl->udev; | ||
341 | struct udev_ctrl_msg *uctrl_msg; | ||
342 | ssize_t size; | ||
343 | struct msghdr smsg; | ||
344 | struct cmsghdr *cmsg; | ||
345 | struct iovec iov; | ||
346 | struct ucred *cred; | ||
347 | char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; | ||
348 | |||
349 | uctrl_msg = calloc(1, sizeof(struct udev_ctrl_msg)); | ||
350 | if (uctrl_msg == NULL) | ||
351 | return NULL; | ||
352 | uctrl_msg->refcount = 1; | ||
353 | uctrl_msg->conn = conn; | ||
354 | udev_ctrl_connection_ref(conn); | ||
355 | |||
356 | /* wait for the incoming message */ | ||
357 | for(;;) { | ||
358 | struct pollfd pfd[1]; | ||
359 | int r; | ||
360 | |||
361 | pfd[0].fd = conn->sock; | ||
362 | pfd[0].events = POLLIN; | ||
363 | |||
364 | r = poll(pfd, 1, 10000); | ||
365 | if (r < 0) { | ||
366 | if (errno == EINTR) | ||
367 | continue; | ||
368 | goto err; | ||
369 | } else if (r == 0) { | ||
370 | err(udev, "timeout waiting for ctrl message\n"); | ||
371 | goto err; | ||
372 | } else { | ||
373 | if (!(pfd[0].revents & POLLIN)) { | ||
374 | err(udev, "ctrl connection error: %m\n"); | ||
375 | goto err; | ||
376 | } | ||
377 | } | ||
378 | |||
379 | break; | ||
380 | } | ||
381 | |||
382 | iov.iov_base = &uctrl_msg->ctrl_msg_wire; | ||
383 | iov.iov_len = sizeof(struct udev_ctrl_msg_wire); | ||
384 | memset(&smsg, 0x00, sizeof(struct msghdr)); | ||
385 | smsg.msg_iov = &iov; | ||
386 | smsg.msg_iovlen = 1; | ||
387 | smsg.msg_control = cred_msg; | ||
388 | smsg.msg_controllen = sizeof(cred_msg); | ||
389 | size = recvmsg(conn->sock, &smsg, 0); | ||
390 | if (size < 0) { | ||
391 | err(udev, "unable to receive ctrl message: %m\n"); | ||
392 | goto err; | ||
393 | } | ||
394 | cmsg = CMSG_FIRSTHDR(&smsg); | ||
395 | cred = (struct ucred *) CMSG_DATA(cmsg); | ||
396 | |||
397 | if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { | ||
398 | err(udev, "no sender credentials received, message ignored\n"); | ||
399 | goto err; | ||
400 | } | ||
401 | |||
402 | if (cred->uid != 0) { | ||
403 | err(udev, "sender uid=%i, message ignored\n", cred->uid); | ||
404 | goto err; | ||
405 | } | ||
406 | |||
407 | if (uctrl_msg->ctrl_msg_wire.magic != UDEV_CTRL_MAGIC) { | ||
408 | err(udev, "message magic 0x%08x doesn't match, ignore it\n", uctrl_msg->ctrl_msg_wire.magic); | ||
409 | goto err; | ||
410 | } | ||
411 | |||
412 | dbg(udev, "created ctrl_msg %p (%i)\n", uctrl_msg, uctrl_msg->ctrl_msg_wire.type); | ||
413 | return uctrl_msg; | ||
414 | err: | ||
415 | udev_ctrl_msg_unref(uctrl_msg); | ||
416 | return NULL; | ||
417 | } | ||
418 | |||
419 | struct udev_ctrl_msg *udev_ctrl_msg_ref(struct udev_ctrl_msg *ctrl_msg) | ||
420 | { | ||
421 | if (ctrl_msg == NULL) | ||
422 | return NULL; | ||
423 | ctrl_msg->refcount++; | ||
424 | return ctrl_msg; | ||
425 | } | ||
426 | |||
427 | struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg) | ||
428 | { | ||
429 | if (ctrl_msg == NULL) | ||
430 | return NULL; | ||
431 | ctrl_msg->refcount--; | ||
432 | if (ctrl_msg->refcount > 0) | ||
433 | return ctrl_msg; | ||
434 | dbg(ctrl_msg->conn->uctrl->udev, "release ctrl_msg %p\n", ctrl_msg); | ||
435 | udev_ctrl_connection_unref(ctrl_msg->conn); | ||
436 | free(ctrl_msg); | ||
437 | return NULL; | ||
438 | } | ||
439 | |||
440 | int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg) | ||
441 | { | ||
442 | if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_LOG_LEVEL) | ||
443 | return ctrl_msg->ctrl_msg_wire.intval; | ||
444 | return -1; | ||
445 | } | ||
446 | |||
447 | int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg) | ||
448 | { | ||
449 | if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_STOP_EXEC_QUEUE) | ||
450 | return 1; | ||
451 | return -1; | ||
452 | } | ||
453 | |||
454 | int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg) | ||
455 | { | ||
456 | if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_START_EXEC_QUEUE) | ||
457 | return 1; | ||
458 | return -1; | ||
459 | } | ||
460 | |||
461 | int udev_ctrl_get_reload(struct udev_ctrl_msg *ctrl_msg) | ||
462 | { | ||
463 | if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_RELOAD) | ||
464 | return 1; | ||
465 | return -1; | ||
466 | } | ||
467 | |||
468 | const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg) | ||
469 | { | ||
470 | if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_ENV) | ||
471 | return ctrl_msg->ctrl_msg_wire.buf; | ||
472 | return NULL; | ||
473 | } | ||
474 | |||
475 | int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg) | ||
476 | { | ||
477 | if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_CHILDREN_MAX) | ||
478 | return ctrl_msg->ctrl_msg_wire.intval; | ||
479 | return -1; | ||
480 | } | ||
481 | |||
482 | int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg) | ||
483 | { | ||
484 | if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_PING) | ||
485 | return 1; | ||
486 | return -1; | ||
487 | } | ||
488 | |||
489 | int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg) | ||
490 | { | ||
491 | if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_EXIT) | ||
492 | return 1; | ||
493 | return -1; | ||
494 | } |
File src/udev-event.c added (mode: 100644) (index 0000000..63df9e8) | |||
1 | /* | ||
2 | * Copyright (C) 2003-2010 Kay Sievers <kay.sievers@vrfy.org> | ||
3 | * | ||
4 | * This program is free software: you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation, either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #include <stdlib.h> | ||
19 | #include <stdio.h> | ||
20 | #include <stddef.h> | ||
21 | #include <unistd.h> | ||
22 | #include <fcntl.h> | ||
23 | #include <errno.h> | ||
24 | #include <ctype.h> | ||
25 | #include <string.h> | ||
26 | #include <time.h> | ||
27 | #include <net/if.h> | ||
28 | #include <sys/ioctl.h> | ||
29 | #include <sys/prctl.h> | ||
30 | #include <sys/poll.h> | ||
31 | #include <sys/epoll.h> | ||
32 | #include <sys/wait.h> | ||
33 | #include <sys/socket.h> | ||
34 | #include <sys/signalfd.h> | ||
35 | #include <linux/sockios.h> | ||
36 | |||
37 | #include "udev.h" | ||
38 | |||
39 | struct udev_event *udev_event_new(struct udev_device *dev) | ||
40 | { | ||
41 | struct udev *udev = udev_device_get_udev(dev); | ||
42 | struct udev_event *event; | ||
43 | |||
44 | event = calloc(1, sizeof(struct udev_event)); | ||
45 | if (event == NULL) | ||
46 | return NULL; | ||
47 | event->dev = dev; | ||
48 | event->udev = udev; | ||
49 | udev_list_init(udev, &event->run_list, false); | ||
50 | event->fd_signal = -1; | ||
51 | event->birth_usec = now_usec(); | ||
52 | event->timeout_usec = 30 * 1000 * 1000; | ||
53 | dbg(event->udev, "allocated event %p\n", event); | ||
54 | return event; | ||
55 | } | ||
56 | |||
57 | void udev_event_unref(struct udev_event *event) | ||
58 | { | ||
59 | if (event == NULL) | ||
60 | return; | ||
61 | udev_list_cleanup(&event->run_list); | ||
62 | free(event->program_result); | ||
63 | free(event->name); | ||
64 | dbg(event->udev, "free event %p\n", event); | ||
65 | free(event); | ||
66 | } | ||
67 | |||
68 | size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size) | ||
69 | { | ||
70 | struct udev_device *dev = event->dev; | ||
71 | enum subst_type { | ||
72 | SUBST_UNKNOWN, | ||
73 | SUBST_DEVNODE, | ||
74 | SUBST_ATTR, | ||
75 | SUBST_ENV, | ||
76 | SUBST_KERNEL, | ||
77 | SUBST_KERNEL_NUMBER, | ||
78 | SUBST_DRIVER, | ||
79 | SUBST_DEVPATH, | ||
80 | SUBST_ID, | ||
81 | SUBST_MAJOR, | ||
82 | SUBST_MINOR, | ||
83 | SUBST_RESULT, | ||
84 | SUBST_PARENT, | ||
85 | SUBST_NAME, | ||
86 | SUBST_LINKS, | ||
87 | SUBST_ROOT, | ||
88 | SUBST_SYS, | ||
89 | }; | ||
90 | static const struct subst_map { | ||
91 | char *name; | ||
92 | char fmt; | ||
93 | enum subst_type type; | ||
94 | } map[] = { | ||
95 | { .name = "devnode", .fmt = 'N', .type = SUBST_DEVNODE }, | ||
96 | { .name = "tempnode", .fmt = 'N', .type = SUBST_DEVNODE }, | ||
97 | { .name = "attr", .fmt = 's', .type = SUBST_ATTR }, | ||
98 | { .name = "sysfs", .fmt = 's', .type = SUBST_ATTR }, | ||
99 | { .name = "env", .fmt = 'E', .type = SUBST_ENV }, | ||
100 | { .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL }, | ||
101 | { .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER }, | ||
102 | { .name = "driver", .fmt = 'd', .type = SUBST_DRIVER }, | ||
103 | { .name = "devpath", .fmt = 'p', .type = SUBST_DEVPATH }, | ||
104 | { .name = "id", .fmt = 'b', .type = SUBST_ID }, | ||
105 | { .name = "major", .fmt = 'M', .type = SUBST_MAJOR }, | ||
106 | { .name = "minor", .fmt = 'm', .type = SUBST_MINOR }, | ||
107 | { .name = "result", .fmt = 'c', .type = SUBST_RESULT }, | ||
108 | { .name = "parent", .fmt = 'P', .type = SUBST_PARENT }, | ||
109 | { .name = "name", .fmt = 'D', .type = SUBST_NAME }, | ||
110 | { .name = "links", .fmt = 'L', .type = SUBST_LINKS }, | ||
111 | { .name = "root", .fmt = 'r', .type = SUBST_ROOT }, | ||
112 | { .name = "sys", .fmt = 'S', .type = SUBST_SYS }, | ||
113 | }; | ||
114 | const char *from; | ||
115 | char *s; | ||
116 | size_t l; | ||
117 | |||
118 | from = src; | ||
119 | s = dest; | ||
120 | l = size; | ||
121 | |||
122 | for (;;) { | ||
123 | enum subst_type type = SUBST_UNKNOWN; | ||
124 | char attrbuf[UTIL_PATH_SIZE]; | ||
125 | char *attr = NULL; | ||
126 | |||
127 | while (from[0] != '\0') { | ||
128 | if (from[0] == '$') { | ||
129 | /* substitute named variable */ | ||
130 | unsigned int i; | ||
131 | |||
132 | if (from[1] == '$') { | ||
133 | from++; | ||
134 | goto copy; | ||
135 | } | ||
136 | |||
137 | for (i = 0; i < ARRAY_SIZE(map); i++) { | ||
138 | if (strncmp(&from[1], map[i].name, strlen(map[i].name)) == 0) { | ||
139 | type = map[i].type; | ||
140 | from += strlen(map[i].name)+1; | ||
141 | dbg(event->udev, "will substitute format name '%s'\n", map[i].name); | ||
142 | goto subst; | ||
143 | } | ||
144 | } | ||
145 | } else if (from[0] == '%') { | ||
146 | /* substitute format char */ | ||
147 | unsigned int i; | ||
148 | |||
149 | if (from[1] == '%') { | ||
150 | from++; | ||
151 | goto copy; | ||
152 | } | ||
153 | |||
154 | for (i = 0; i < ARRAY_SIZE(map); i++) { | ||
155 | if (from[1] == map[i].fmt) { | ||
156 | type = map[i].type; | ||
157 | from += 2; | ||
158 | dbg(event->udev, "will substitute format char '%c'\n", map[i].fmt); | ||
159 | goto subst; | ||
160 | } | ||
161 | } | ||
162 | } | ||
163 | copy: | ||
164 | /* copy char */ | ||
165 | if (l == 0) | ||
166 | goto out; | ||
167 | s[0] = from[0]; | ||
168 | from++; | ||
169 | s++; | ||
170 | l--; | ||
171 | } | ||
172 | |||
173 | goto out; | ||
174 | subst: | ||
175 | /* extract possible $format{attr} */ | ||
176 | if (from[0] == '{') { | ||
177 | unsigned int i; | ||
178 | |||
179 | from++; | ||
180 | for (i = 0; from[i] != '}'; i++) { | ||
181 | if (from[i] == '\0') { | ||
182 | err(event->udev, "missing closing brace for format '%s'\n", src); | ||
183 | goto out; | ||
184 | } | ||
185 | } | ||
186 | if (i >= sizeof(attrbuf)) | ||
187 | goto out; | ||
188 | memcpy(attrbuf, from, i); | ||
189 | attrbuf[i] = '\0'; | ||
190 | from += i+1; | ||
191 | attr = attrbuf; | ||
192 | } else { | ||
193 | attr = NULL; | ||
194 | } | ||
195 | |||
196 | switch (type) { | ||
197 | case SUBST_DEVPATH: | ||
198 | l = util_strpcpy(&s, l, udev_device_get_devpath(dev)); | ||
199 | dbg(event->udev, "substitute devpath '%s'\n", udev_device_get_devpath(dev)); | ||
200 | break; | ||
201 | case SUBST_KERNEL: | ||
202 | l = util_strpcpy(&s, l, udev_device_get_sysname(dev)); | ||
203 | dbg(event->udev, "substitute kernel name '%s'\n", udev_device_get_sysname(dev)); | ||
204 | break; | ||
205 | case SUBST_KERNEL_NUMBER: | ||
206 | if (udev_device_get_sysnum(dev) == NULL) | ||
207 | break; | ||
208 | l = util_strpcpy(&s, l, udev_device_get_sysnum(dev)); | ||
209 | dbg(event->udev, "substitute kernel number '%s'\n", udev_device_get_sysnum(dev)); | ||
210 | break; | ||
211 | case SUBST_ID: | ||
212 | if (event->dev_parent == NULL) | ||
213 | break; | ||
214 | l = util_strpcpy(&s, l, udev_device_get_sysname(event->dev_parent)); | ||
215 | dbg(event->udev, "substitute id '%s'\n", udev_device_get_sysname(event->dev_parent)); | ||
216 | break; | ||
217 | case SUBST_DRIVER: { | ||
218 | const char *driver; | ||
219 | |||
220 | if (event->dev_parent == NULL) | ||
221 | break; | ||
222 | |||
223 | driver = udev_device_get_driver(event->dev_parent); | ||
224 | if (driver == NULL) | ||
225 | break; | ||
226 | l = util_strpcpy(&s, l, driver); | ||
227 | dbg(event->udev, "substitute driver '%s'\n", driver); | ||
228 | break; | ||
229 | } | ||
230 | case SUBST_MAJOR: { | ||
231 | char num[UTIL_PATH_SIZE]; | ||
232 | |||
233 | sprintf(num, "%d", major(udev_device_get_devnum(dev))); | ||
234 | l = util_strpcpy(&s, l, num); | ||
235 | dbg(event->udev, "substitute major number '%s'\n", num); | ||
236 | break; | ||
237 | } | ||
238 | case SUBST_MINOR: { | ||
239 | char num[UTIL_PATH_SIZE]; | ||
240 | |||
241 | sprintf(num, "%d", minor(udev_device_get_devnum(dev))); | ||
242 | l = util_strpcpy(&s, l, num); | ||
243 | dbg(event->udev, "substitute minor number '%s'\n", num); | ||
244 | break; | ||
245 | } | ||
246 | case SUBST_RESULT: { | ||
247 | char *rest; | ||
248 | int i; | ||
249 | |||
250 | if (event->program_result == NULL) | ||
251 | break; | ||
252 | /* get part part of the result string */ | ||
253 | i = 0; | ||
254 | if (attr != NULL) | ||
255 | i = strtoul(attr, &rest, 10); | ||
256 | if (i > 0) { | ||
257 | char result[UTIL_PATH_SIZE]; | ||
258 | char tmp[UTIL_PATH_SIZE]; | ||
259 | char *cpos; | ||
260 | |||
261 | dbg(event->udev, "request part #%d of result string\n", i); | ||
262 | util_strscpy(result, sizeof(result), event->program_result); | ||
263 | cpos = result; | ||
264 | while (--i) { | ||
265 | while (cpos[0] != '\0' && !isspace(cpos[0])) | ||
266 | cpos++; | ||
267 | while (isspace(cpos[0])) | ||
268 | cpos++; | ||
269 | } | ||
270 | if (i > 0) { | ||
271 | err(event->udev, "requested part of result string not found\n"); | ||
272 | break; | ||
273 | } | ||
274 | util_strscpy(tmp, sizeof(tmp), cpos); | ||
275 | /* %{2+}c copies the whole string from the second part on */ | ||
276 | if (rest[0] != '+') { | ||
277 | cpos = strchr(tmp, ' '); | ||
278 | if (cpos) | ||
279 | cpos[0] = '\0'; | ||
280 | } | ||
281 | l = util_strpcpy(&s, l, tmp); | ||
282 | dbg(event->udev, "substitute part of result string '%s'\n", tmp); | ||
283 | } else { | ||
284 | l = util_strpcpy(&s, l, event->program_result); | ||
285 | dbg(event->udev, "substitute result string '%s'\n", event->program_result); | ||
286 | } | ||
287 | break; | ||
288 | } | ||
289 | case SUBST_ATTR: { | ||
290 | const char *value = NULL; | ||
291 | char vbuf[UTIL_NAME_SIZE]; | ||
292 | size_t len; | ||
293 | int count; | ||
294 | |||
295 | if (attr == NULL) { | ||
296 | err(event->udev, "missing file parameter for attr\n"); | ||
297 | break; | ||
298 | } | ||
299 | |||
300 | /* try to read the value specified by "[dmi/id]product_name" */ | ||
301 | if (util_resolve_subsys_kernel(event->udev, attr, vbuf, sizeof(vbuf), 1) == 0) | ||
302 | value = vbuf; | ||
303 | |||
304 | /* try to read the attribute the device */ | ||
305 | if (value == NULL) | ||
306 | value = udev_device_get_sysattr_value(event->dev, attr); | ||
307 | |||
308 | /* try to read the attribute of the parent device, other matches have selected */ | ||
309 | if (value == NULL && event->dev_parent != NULL && event->dev_parent != event->dev) | ||
310 | value = udev_device_get_sysattr_value(event->dev_parent, attr); | ||
311 | |||
312 | if (value == NULL) | ||
313 | break; | ||
314 | |||
315 | /* strip trailing whitespace, and replace unwanted characters */ | ||
316 | if (value != vbuf) | ||
317 | util_strscpy(vbuf, sizeof(vbuf), value); | ||
318 | len = strlen(vbuf); | ||
319 | while (len > 0 && isspace(vbuf[--len])) | ||
320 | vbuf[len] = '\0'; | ||
321 | count = util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT); | ||
322 | if (count > 0) | ||
323 | info(event->udev, "%i character(s) replaced\n" , count); | ||
324 | l = util_strpcpy(&s, l, vbuf); | ||
325 | dbg(event->udev, "substitute sysfs value '%s'\n", vbuf); | ||
326 | break; | ||
327 | } | ||
328 | case SUBST_PARENT: { | ||
329 | struct udev_device *dev_parent; | ||
330 | const char *devnode; | ||
331 | |||
332 | dev_parent = udev_device_get_parent(event->dev); | ||
333 | if (dev_parent == NULL) | ||
334 | break; | ||
335 | devnode = udev_device_get_devnode(dev_parent); | ||
336 | if (devnode != NULL) { | ||
337 | size_t devlen = strlen(udev_get_dev_path(event->udev))+1; | ||
338 | |||
339 | l = util_strpcpy(&s, l, &devnode[devlen]); | ||
340 | dbg(event->udev, "found parent '%s', got node name '%s'\n", | ||
341 | udev_device_get_syspath(dev_parent), &devnode[devlen]); | ||
342 | } | ||
343 | break; | ||
344 | } | ||
345 | case SUBST_DEVNODE: | ||
346 | if (udev_device_get_devnode(dev) != NULL) | ||
347 | l = util_strpcpy(&s, l, udev_device_get_devnode(dev)); | ||
348 | break; | ||
349 | case SUBST_NAME: { | ||
350 | if (event->name != NULL) { | ||
351 | l = util_strpcpy(&s, l, event->name); | ||
352 | dbg(event->udev, "substitute custom node name '%s'\n", event->name); | ||
353 | } else if (udev_device_get_devnode(dev) != NULL) { | ||
354 | size_t devlen = strlen(udev_get_dev_path(event->udev))+1; | ||
355 | |||
356 | l = util_strpcpy(&s, l, &udev_device_get_devnode(dev)[devlen]); | ||
357 | dbg(event->udev, "substitute node name'%s'\n", &udev_device_get_devnode(dev)[devlen]); | ||
358 | } else { | ||
359 | l = util_strpcpy(&s, l, udev_device_get_sysname(dev)); | ||
360 | dbg(event->udev, "substitute device name'%s'\n", udev_device_get_sysname(dev)); | ||
361 | } | ||
362 | break; | ||
363 | } | ||
364 | case SUBST_LINKS: { | ||
365 | size_t devlen = strlen(udev_get_dev_path(event->udev))+1; | ||
366 | struct udev_list_entry *list_entry; | ||
367 | |||
368 | list_entry = udev_device_get_devlinks_list_entry(dev); | ||
369 | if (list_entry == NULL) | ||
370 | break; | ||
371 | l = util_strpcpy(&s, l, &udev_list_entry_get_name(list_entry)[devlen]); | ||
372 | udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry)) | ||
373 | l = util_strpcpyl(&s, l, " ", &udev_list_entry_get_name(list_entry)[devlen], NULL); | ||
374 | break; | ||
375 | } | ||
376 | case SUBST_ROOT: | ||
377 | l = util_strpcpy(&s, l, udev_get_dev_path(event->udev)); | ||
378 | dbg(event->udev, "substitute udev_root '%s'\n", udev_get_dev_path(event->udev)); | ||
379 | break; | ||
380 | case SUBST_SYS: | ||
381 | l = util_strpcpy(&s, l, udev_get_sys_path(event->udev)); | ||
382 | dbg(event->udev, "substitute sys_path '%s'\n", udev_get_sys_path(event->udev)); | ||
383 | break; | ||
384 | case SUBST_ENV: | ||
385 | if (attr == NULL) { | ||
386 | dbg(event->udev, "missing attribute\n"); | ||
387 | break; | ||
388 | } else { | ||
389 | const char *value; | ||
390 | |||
391 | value = udev_device_get_property_value(event->dev, attr); | ||
392 | if (value == NULL) | ||
393 | break; | ||
394 | dbg(event->udev, "substitute env '%s=%s'\n", attr, value); | ||
395 | l = util_strpcpy(&s, l, value); | ||
396 | break; | ||
397 | } | ||
398 | default: | ||
399 | err(event->udev, "unknown substitution type=%i\n", type); | ||
400 | break; | ||
401 | } | ||
402 | } | ||
403 | |||
404 | out: | ||
405 | s[0] = '\0'; | ||
406 | dbg(event->udev, "'%s' -> '%s' (%zu)\n", src, dest, l); | ||
407 | return l; | ||
408 | } | ||
409 | |||
410 | static int spawn_exec(struct udev_event *event, | ||
411 | const char *cmd, char *const argv[], char **envp, const sigset_t *sigmask, | ||
412 | int fd_stdout, int fd_stderr) | ||
413 | { | ||
414 | struct udev *udev = event->udev; | ||
415 | int err; | ||
416 | int fd; | ||
417 | |||
418 | /* discard child output or connect to pipe */ | ||
419 | fd = open("/dev/null", O_RDWR); | ||
420 | if (fd >= 0) { | ||
421 | dup2(fd, STDIN_FILENO); | ||
422 | if (fd_stdout < 0) | ||
423 | dup2(fd, STDOUT_FILENO); | ||
424 | if (fd_stderr < 0) | ||
425 | dup2(fd, STDERR_FILENO); | ||
426 | close(fd); | ||
427 | } else { | ||
428 | err(udev, "open /dev/null failed: %m\n"); | ||
429 | } | ||
430 | |||
431 | /* connect pipes to std{out,err} */ | ||
432 | if (fd_stdout >= 0) { | ||
433 | dup2(fd_stdout, STDOUT_FILENO); | ||
434 | close(fd_stdout); | ||
435 | } | ||
436 | if (fd_stderr >= 0) { | ||
437 | dup2(fd_stderr, STDERR_FILENO); | ||
438 | close(fd_stderr); | ||
439 | } | ||
440 | |||
441 | /* terminate child in case parent goes away */ | ||
442 | prctl(PR_SET_PDEATHSIG, SIGTERM); | ||
443 | |||
444 | /* restore original udev sigmask before exec */ | ||
445 | if (sigmask) | ||
446 | sigprocmask(SIG_SETMASK, sigmask, NULL); | ||
447 | |||
448 | execve(argv[0], argv, envp); | ||
449 | |||
450 | /* exec failed */ | ||
451 | err = -errno; | ||
452 | err(udev, "failed to execute '%s' '%s': %m\n", argv[0], cmd); | ||
453 | return err; | ||
454 | } | ||
455 | |||
456 | static void spawn_read(struct udev_event *event, | ||
457 | const char *cmd, | ||
458 | int fd_stdout, int fd_stderr, | ||
459 | char *result, size_t ressize) | ||
460 | { | ||
461 | struct udev *udev = event->udev; | ||
462 | size_t respos = 0; | ||
463 | int fd_ep = -1; | ||
464 | struct epoll_event ep_outpipe, ep_errpipe; | ||
465 | |||
466 | /* read from child if requested */ | ||
467 | if (fd_stdout < 0 && fd_stderr < 0) | ||
468 | return; | ||
469 | |||
470 | fd_ep = epoll_create1(EPOLL_CLOEXEC); | ||
471 | if (fd_ep < 0) { | ||
472 | err(udev, "error creating epoll fd: %m\n"); | ||
473 | goto out; | ||
474 | } | ||
475 | |||
476 | if (fd_stdout >= 0) { | ||
477 | memset(&ep_outpipe, 0, sizeof(struct epoll_event)); | ||
478 | ep_outpipe.events = EPOLLIN; | ||
479 | ep_outpipe.data.ptr = &fd_stdout; | ||
480 | if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stdout, &ep_outpipe) < 0) { | ||
481 | err(udev, "fail to add fd to epoll: %m\n"); | ||
482 | goto out; | ||
483 | } | ||
484 | } | ||
485 | |||
486 | if (fd_stderr >= 0) { | ||
487 | memset(&ep_errpipe, 0, sizeof(struct epoll_event)); | ||
488 | ep_errpipe.events = EPOLLIN; | ||
489 | ep_errpipe.data.ptr = &fd_stderr; | ||
490 | if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stderr, &ep_errpipe) < 0) { | ||
491 | err(udev, "fail to add fd to epoll: %m\n"); | ||
492 | goto out; | ||
493 | } | ||
494 | } | ||
495 | |||
496 | /* read child output */ | ||
497 | while (fd_stdout >= 0 || fd_stderr >= 0) { | ||
498 | int timeout; | ||
499 | int fdcount; | ||
500 | struct epoll_event ev[4]; | ||
501 | int i; | ||
502 | |||
503 | if (event->timeout_usec > 0) { | ||
504 | unsigned long long age_usec; | ||
505 | |||
506 | age_usec = now_usec() - event->birth_usec; | ||
507 | if (age_usec >= event->timeout_usec) { | ||
508 | err(udev, "timeout '%s'\n", cmd); | ||
509 | goto out; | ||
510 | } | ||
511 | timeout = ((event->timeout_usec - age_usec) / 1000) + 1000; | ||
512 | } else { | ||
513 | timeout = -1; | ||
514 | } | ||
515 | |||
516 | fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), timeout); | ||
517 | if (fdcount < 0) { | ||
518 | if (errno == EINTR) | ||
519 | continue; | ||
520 | err(udev, "failed to poll: %m\n"); | ||
521 | goto out; | ||
522 | } | ||
523 | if (fdcount == 0) { | ||
524 | err(udev, "timeout '%s'\n", cmd); | ||
525 | goto out; | ||
526 | } | ||
527 | |||
528 | for (i = 0; i < fdcount; i++) { | ||
529 | int *fd = (int *)ev[i].data.ptr; | ||
530 | |||
531 | if (ev[i].events & EPOLLIN) { | ||
532 | ssize_t count; | ||
533 | char buf[4096]; | ||
534 | |||
535 | count = read(*fd, buf, sizeof(buf)-1); | ||
536 | if (count <= 0) | ||
537 | continue; | ||
538 | buf[count] = '\0'; | ||
539 | |||
540 | /* store stdout result */ | ||
541 | if (result != NULL && *fd == fd_stdout) { | ||
542 | if (respos + count < ressize) { | ||
543 | memcpy(&result[respos], buf, count); | ||
544 | respos += count; | ||
545 | } else { | ||
546 | err(udev, "'%s' ressize %zd too short\n", cmd, ressize); | ||
547 | } | ||
548 | } | ||
549 | |||
550 | /* log debug output only if we watch stderr */ | ||
551 | if (fd_stderr >= 0) { | ||
552 | char *pos; | ||
553 | char *line; | ||
554 | |||
555 | pos = buf; | ||
556 | while ((line = strsep(&pos, "\n"))) { | ||
557 | if (pos != NULL || line[0] != '\0') | ||
558 | info(udev, "'%s'(%s) '%s'\n", cmd, *fd == fd_stdout ? "out" : "err" , line); | ||
559 | } | ||
560 | } | ||
561 | } else if (ev[i].events & EPOLLHUP) { | ||
562 | if (epoll_ctl(fd_ep, EPOLL_CTL_DEL, *fd, NULL) < 0) { | ||
563 | err(udev, "failed to remove fd from epoll: %m\n"); | ||
564 | goto out; | ||
565 | } | ||
566 | *fd = -1; | ||
567 | } | ||
568 | } | ||
569 | } | ||
570 | |||
571 | /* return the child's stdout string */ | ||
572 | if (result != NULL) { | ||
573 | result[respos] = '\0'; | ||
574 | dbg(udev, "result='%s'\n", result); | ||
575 | } | ||
576 | out: | ||
577 | if (fd_ep >= 0) | ||
578 | close(fd_ep); | ||
579 | } | ||
580 | |||
581 | static int spawn_wait(struct udev_event *event, const char *cmd, pid_t pid) | ||
582 | { | ||
583 | struct udev *udev = event->udev; | ||
584 | struct pollfd pfd[1]; | ||
585 | int err = 0; | ||
586 | |||
587 | pfd[0].events = POLLIN; | ||
588 | pfd[0].fd = event->fd_signal; | ||
589 | |||
590 | while (pid > 0) { | ||
591 | int timeout; | ||
592 | int fdcount; | ||
593 | |||
594 | if (event->timeout_usec > 0) { | ||
595 | unsigned long long age_usec; | ||
596 | |||
597 | age_usec = now_usec() - event->birth_usec; | ||
598 | if (age_usec >= event->timeout_usec) | ||
599 | timeout = 1000; | ||
600 | else | ||
601 | timeout = ((event->timeout_usec - age_usec) / 1000) + 1000; | ||
602 | } else { | ||
603 | timeout = -1; | ||
604 | } | ||
605 | |||
606 | fdcount = poll(pfd, 1, timeout); | ||
607 | if (fdcount < 0) { | ||
608 | if (errno == EINTR) | ||
609 | continue; | ||
610 | err = -errno; | ||
611 | err(udev, "failed to poll: %m\n"); | ||
612 | goto out; | ||
613 | } | ||
614 | if (fdcount == 0) { | ||
615 | err(udev, "timeout: killing '%s' [%u]\n", cmd, pid); | ||
616 | kill(pid, SIGKILL); | ||
617 | } | ||
618 | |||
619 | if (pfd[0].revents & POLLIN) { | ||
620 | struct signalfd_siginfo fdsi; | ||
621 | int status; | ||
622 | ssize_t size; | ||
623 | |||
624 | size = read(event->fd_signal, &fdsi, sizeof(struct signalfd_siginfo)); | ||
625 | if (size != sizeof(struct signalfd_siginfo)) | ||
626 | continue; | ||
627 | |||
628 | switch (fdsi.ssi_signo) { | ||
629 | case SIGTERM: | ||
630 | event->sigterm = true; | ||
631 | break; | ||
632 | case SIGCHLD: | ||
633 | if (waitpid(pid, &status, WNOHANG) < 0) | ||
634 | break; | ||
635 | if (WIFEXITED(status)) { | ||
636 | info(udev, "'%s' [%u] exit with return code %i\n", cmd, pid, WEXITSTATUS(status)); | ||
637 | if (WEXITSTATUS(status) != 0) | ||
638 | err = -1; | ||
639 | } else if (WIFSIGNALED(status)) { | ||
640 | err(udev, "'%s' [%u] terminated by signal %i (%s)\n", cmd, pid, WTERMSIG(status), strsignal(WTERMSIG(status))); | ||
641 | err = -1; | ||
642 | } else if (WIFSTOPPED(status)) { | ||
643 | err(udev, "'%s' [%u] stopped\n", cmd, pid); | ||
644 | err = -1; | ||
645 | } else if (WIFCONTINUED(status)) { | ||
646 | err(udev, "'%s' [%u] continued\n", cmd, pid); | ||
647 | err = -1; | ||
648 | } else { | ||
649 | err(udev, "'%s' [%u] exit with status 0x%04x\n", cmd, pid, status); | ||
650 | err = -1; | ||
651 | } | ||
652 | pid = 0; | ||
653 | break; | ||
654 | } | ||
655 | } | ||
656 | } | ||
657 | out: | ||
658 | return err; | ||
659 | } | ||
660 | |||
661 | int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]) | ||
662 | { | ||
663 | int i = 0; | ||
664 | char *pos; | ||
665 | |||
666 | if (strchr(cmd, ' ') == NULL) { | ||
667 | argv[i++] = cmd; | ||
668 | goto out; | ||
669 | } | ||
670 | |||
671 | pos = cmd; | ||
672 | while (pos != NULL && pos[0] != '\0') { | ||
673 | if (pos[0] == '\'') { | ||
674 | /* do not separate quotes */ | ||
675 | pos++; | ||
676 | argv[i] = strsep(&pos, "\'"); | ||
677 | if (pos != NULL) | ||
678 | while (pos[0] == ' ') | ||
679 | pos++; | ||
680 | } else { | ||
681 | argv[i] = strsep(&pos, " "); | ||
682 | if (pos != NULL) | ||
683 | while (pos[0] == ' ') | ||
684 | pos++; | ||
685 | } | ||
686 | dbg(udev, "argv[%i] '%s'\n", i, argv[i]); | ||
687 | i++; | ||
688 | } | ||
689 | out: | ||
690 | argv[i] = NULL; | ||
691 | if (argc) | ||
692 | *argc = i; | ||
693 | return 0; | ||
694 | } | ||
695 | |||
696 | int udev_event_spawn(struct udev_event *event, | ||
697 | const char *cmd, char **envp, const sigset_t *sigmask, | ||
698 | char *result, size_t ressize) | ||
699 | { | ||
700 | struct udev *udev = event->udev; | ||
701 | int outpipe[2] = {-1, -1}; | ||
702 | int errpipe[2] = {-1, -1}; | ||
703 | pid_t pid; | ||
704 | char arg[UTIL_PATH_SIZE]; | ||
705 | char *argv[128]; | ||
706 | char program[UTIL_PATH_SIZE]; | ||
707 | int err = 0; | ||
708 | |||
709 | util_strscpy(arg, sizeof(arg), cmd); | ||
710 | udev_build_argv(event->udev, arg, NULL, argv); | ||
711 | |||
712 | /* pipes from child to parent */ | ||
713 | if (result != NULL || udev_get_log_priority(udev) >= LOG_INFO) { | ||
714 | if (pipe2(outpipe, O_NONBLOCK) != 0) { | ||
715 | err = -errno; | ||
716 | err(udev, "pipe failed: %m\n"); | ||
717 | goto out; | ||
718 | } | ||
719 | } | ||
720 | if (udev_get_log_priority(udev) >= LOG_INFO) { | ||
721 | if (pipe2(errpipe, O_NONBLOCK) != 0) { | ||
722 | err = -errno; | ||
723 | err(udev, "pipe failed: %m\n"); | ||
724 | goto out; | ||
725 | } | ||
726 | } | ||
727 | |||
728 | /* allow programs in /usr/lib/udev/ to be called without the path */ | ||
729 | if (argv[0][0] != '/') { | ||
730 | util_strscpyl(program, sizeof(program), PKGLIBEXECDIR "/", argv[0], NULL); | ||
731 | argv[0] = program; | ||
732 | } | ||
733 | |||
734 | pid = fork(); | ||
735 | switch(pid) { | ||
736 | case 0: | ||
737 | /* child closes parent's ends of pipes */ | ||
738 | if (outpipe[READ_END] >= 0) { | ||
739 | close(outpipe[READ_END]); | ||
740 | outpipe[READ_END] = -1; | ||
741 | } | ||
742 | if (errpipe[READ_END] >= 0) { | ||
743 | close(errpipe[READ_END]); | ||
744 | errpipe[READ_END] = -1; | ||
745 | } | ||
746 | |||
747 | info(udev, "starting '%s'\n", cmd); | ||
748 | |||
749 | err = spawn_exec(event, cmd, argv, envp, sigmask, | ||
750 | outpipe[WRITE_END], errpipe[WRITE_END]); | ||
751 | |||
752 | _exit(2 ); | ||
753 | case -1: | ||
754 | err(udev, "fork of '%s' failed: %m\n", cmd); | ||
755 | err = -1; | ||
756 | goto out; | ||
757 | default: | ||
758 | /* parent closed child's ends of pipes */ | ||
759 | if (outpipe[WRITE_END] >= 0) { | ||
760 | close(outpipe[WRITE_END]); | ||
761 | outpipe[WRITE_END] = -1; | ||
762 | } | ||
763 | if (errpipe[WRITE_END] >= 0) { | ||
764 | close(errpipe[WRITE_END]); | ||
765 | errpipe[WRITE_END] = -1; | ||
766 | } | ||
767 | |||
768 | spawn_read(event, cmd, | ||
769 | outpipe[READ_END], errpipe[READ_END], | ||
770 | result, ressize); | ||
771 | |||
772 | err = spawn_wait(event, cmd, pid); | ||
773 | } | ||
774 | |||
775 | out: | ||
776 | if (outpipe[READ_END] >= 0) | ||
777 | close(outpipe[READ_END]); | ||
778 | if (outpipe[WRITE_END] >= 0) | ||
779 | close(outpipe[WRITE_END]); | ||
780 | if (errpipe[READ_END] >= 0) | ||
781 | close(errpipe[READ_END]); | ||
782 | if (errpipe[WRITE_END] >= 0) | ||
783 | close(errpipe[WRITE_END]); | ||
784 | return err; | ||
785 | } | ||
786 | |||
787 | static inline void rename_netif_kernel_log(struct ifreq ifr) | ||
788 | { | ||
789 | print_kmsg("renamed network interface %s to %s", ifr.ifr_name, ifr.ifr_newname); | ||
790 | } | ||
791 | |||
792 | static int rename_netif(struct udev_event *event) | ||
793 | { | ||
794 | struct udev_device *dev = event->dev; | ||
795 | int sk; | ||
796 | struct ifreq ifr; | ||
797 | int loop; | ||
798 | int err; | ||
799 | |||
800 | info(event->udev, "changing net interface name from '%s' to '%s'\n", | ||
801 | udev_device_get_sysname(dev), event->name); | ||
802 | |||
803 | sk = socket(PF_INET, SOCK_DGRAM, 0); | ||
804 | if (sk < 0) { | ||
805 | err = -errno; | ||
806 | err(event->udev, "error opening socket: %m\n"); | ||
807 | return err; | ||
808 | } | ||
809 | |||
810 | memset(&ifr, 0x00, sizeof(struct ifreq)); | ||
811 | util_strscpy(ifr.ifr_name, IFNAMSIZ, udev_device_get_sysname(dev)); | ||
812 | util_strscpy(ifr.ifr_newname, IFNAMSIZ, event->name); | ||
813 | err = ioctl(sk, SIOCSIFNAME, &ifr); | ||
814 | if (err == 0) { | ||
815 | rename_netif_kernel_log(ifr); | ||
816 | goto out; | ||
817 | } | ||
818 | |||
819 | /* keep trying if the destination interface name already exists */ | ||
820 | err = -errno; | ||
821 | if (err != -EEXIST) | ||
822 | goto out; | ||
823 | |||
824 | /* free our own name, another process may wait for us */ | ||
825 | snprintf(ifr.ifr_newname, IFNAMSIZ, "rename%u", udev_device_get_ifindex(dev)); | ||
826 | err = ioctl(sk, SIOCSIFNAME, &ifr); | ||
827 | if (err < 0) { | ||
828 | err = -errno; | ||
829 | goto out; | ||
830 | } | ||
831 | |||
832 | /* log temporary name */ | ||
833 | rename_netif_kernel_log(ifr); | ||
834 | |||
835 | /* wait a maximum of 90 seconds for our target to become available */ | ||
836 | util_strscpy(ifr.ifr_name, IFNAMSIZ, ifr.ifr_newname); | ||
837 | util_strscpy(ifr.ifr_newname, IFNAMSIZ, event->name); | ||
838 | loop = 90 * 20; | ||
839 | while (loop--) { | ||
840 | const struct timespec duration = { 0, 1000 * 1000 * 1000 / 20 }; | ||
841 | |||
842 | dbg(event->udev, "wait for netif '%s' to become free, loop=%i\n", | ||
843 | event->name, (90 * 20) - loop); | ||
844 | nanosleep(&duration, NULL); | ||
845 | |||
846 | err = ioctl(sk, SIOCSIFNAME, &ifr); | ||
847 | if (err == 0) { | ||
848 | rename_netif_kernel_log(ifr); | ||
849 | break; | ||
850 | } | ||
851 | err = -errno; | ||
852 | if (err != -EEXIST) | ||
853 | break; | ||
854 | } | ||
855 | |||
856 | out: | ||
857 | if (err < 0) | ||
858 | err(event->udev, "error changing net interface name %s to %s: %m\n", ifr.ifr_name, ifr.ifr_newname); | ||
859 | close(sk); | ||
860 | return err; | ||
861 | } | ||
862 | |||
863 | int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules, const sigset_t *sigmask) | ||
864 | { | ||
865 | struct udev_device *dev = event->dev; | ||
866 | int err = 0; | ||
867 | |||
868 | if (udev_device_get_subsystem(dev) == NULL) | ||
869 | return -1; | ||
870 | |||
871 | if (strcmp(udev_device_get_action(dev), "remove") == 0) { | ||
872 | udev_device_read_db(dev, NULL); | ||
873 | udev_device_delete_db(dev); | ||
874 | udev_device_tag_index(dev, NULL, false); | ||
875 | |||
876 | if (major(udev_device_get_devnum(dev)) != 0) | ||
877 | udev_watch_end(event->udev, dev); | ||
878 | |||
879 | udev_rules_apply_to_event(rules, event, sigmask); | ||
880 | |||
881 | if (major(udev_device_get_devnum(dev)) != 0) | ||
882 | udev_node_remove(dev); | ||
883 | } else { | ||
884 | event->dev_db = udev_device_new_from_syspath(event->udev, udev_device_get_syspath(dev)); | ||
885 | if (event->dev_db != NULL) { | ||
886 | udev_device_read_db(event->dev_db, NULL); | ||
887 | udev_device_set_info_loaded(event->dev_db); | ||
888 | |||
889 | /* disable watch during event processing */ | ||
890 | if (major(udev_device_get_devnum(dev)) != 0) | ||
891 | udev_watch_end(event->udev, event->dev_db); | ||
892 | } | ||
893 | |||
894 | udev_rules_apply_to_event(rules, event, sigmask); | ||
895 | |||
896 | /* rename a new network interface, if needed */ | ||
897 | if (udev_device_get_ifindex(dev) > 0 && strcmp(udev_device_get_action(dev), "add") == 0 && | ||
898 | event->name != NULL && strcmp(event->name, udev_device_get_sysname(dev)) != 0) { | ||
899 | char syspath[UTIL_PATH_SIZE]; | ||
900 | char *pos; | ||
901 | |||
902 | err = rename_netif(event); | ||
903 | if (err == 0) { | ||
904 | info(event->udev, "renamed netif to '%s'\n", event->name); | ||
905 | |||
906 | /* remember old name */ | ||
907 | udev_device_add_property(dev, "INTERFACE_OLD", udev_device_get_sysname(dev)); | ||
908 | |||
909 | /* now change the devpath, because the kernel device name has changed */ | ||
910 | util_strscpy(syspath, sizeof(syspath), udev_device_get_syspath(dev)); | ||
911 | pos = strrchr(syspath, '/'); | ||
912 | if (pos != NULL) { | ||
913 | pos++; | ||
914 | util_strscpy(pos, sizeof(syspath) - (pos - syspath), event->name); | ||
915 | udev_device_set_syspath(event->dev, syspath); | ||
916 | udev_device_add_property(dev, "INTERFACE", udev_device_get_sysname(dev)); | ||
917 | info(event->udev, "changed devpath to '%s'\n", udev_device_get_devpath(dev)); | ||
918 | } | ||
919 | } | ||
920 | } | ||
921 | |||
922 | if (major(udev_device_get_devnum(dev)) > 0) { | ||
923 | /* remove/update possible left-over symlinks from old database entry */ | ||
924 | if (event->dev_db != NULL) | ||
925 | udev_node_update_old_links(dev, event->dev_db); | ||
926 | |||
927 | if (!event->mode_set) { | ||
928 | if (udev_device_get_devnode_mode(dev) > 0) { | ||
929 | /* kernel supplied value */ | ||
930 | event->mode = udev_device_get_devnode_mode(dev); | ||
931 | } else if (event->gid > 0) { | ||
932 | /* default 0660 if a group is assigned */ | ||
933 | event->mode = 0660; | ||
934 | } else { | ||
935 | /* default 0600 */ | ||
936 | event->mode = 0600; | ||
937 | } | ||
938 | } | ||
939 | |||
940 | udev_node_add(dev, event->mode, event->uid, event->gid); | ||
941 | } | ||
942 | |||
943 | /* preserve old, or get new initialization timestamp */ | ||
944 | if (event->dev_db != NULL && udev_device_get_usec_initialized(event->dev_db) > 0) | ||
945 | udev_device_set_usec_initialized(event->dev, udev_device_get_usec_initialized(event->dev_db)); | ||
946 | else if (udev_device_get_usec_initialized(event->dev) == 0) | ||
947 | udev_device_set_usec_initialized(event->dev, now_usec()); | ||
948 | |||
949 | /* (re)write database file */ | ||
950 | udev_device_update_db(dev); | ||
951 | udev_device_tag_index(dev, event->dev_db, true); | ||
952 | udev_device_set_is_initialized(dev); | ||
953 | |||
954 | udev_device_unref(event->dev_db); | ||
955 | event->dev_db = NULL; | ||
956 | } | ||
957 | out: | ||
958 | return err; | ||
959 | } | ||
960 | |||
961 | int udev_event_execute_run(struct udev_event *event, const sigset_t *sigmask) | ||
962 | { | ||
963 | struct udev_list_entry *list_entry; | ||
964 | int err = 0; | ||
965 | |||
966 | dbg(event->udev, "executing run list\n"); | ||
967 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&event->run_list)) { | ||
968 | const char *cmd = udev_list_entry_get_name(list_entry); | ||
969 | |||
970 | if (strncmp(cmd, "socket:", strlen("socket:")) == 0) { | ||
971 | struct udev_monitor *monitor; | ||
972 | |||
973 | monitor = udev_monitor_new_from_socket(event->udev, &cmd[strlen("socket:")]); | ||
974 | if (monitor == NULL) | ||
975 | continue; | ||
976 | udev_monitor_send_device(monitor, NULL, event->dev); | ||
977 | udev_monitor_unref(monitor); | ||
978 | } else { | ||
979 | char program[UTIL_PATH_SIZE]; | ||
980 | char **envp; | ||
981 | |||
982 | if (event->exec_delay > 0) { | ||
983 | info(event->udev, "delay execution of '%s'\n", program); | ||
984 | sleep(event->exec_delay); | ||
985 | } | ||
986 | |||
987 | udev_event_apply_format(event, cmd, program, sizeof(program)); | ||
988 | envp = udev_device_get_properties_envp(event->dev); | ||
989 | if (udev_event_spawn(event, program, envp, sigmask, NULL, 0) < 0) { | ||
990 | if (udev_list_entry_get_num(list_entry)) | ||
991 | err = -1; | ||
992 | } | ||
993 | } | ||
994 | } | ||
995 | return err; | ||
996 | } |
File src/udev-node.c added (mode: 100644) (index 0000000..64d8697) | |||
1 | /* | ||
2 | * Copyright (C) 2003-2010 Kay Sievers <kay.sievers@vrfy.org> | ||
3 | * | ||
4 | * This program is free software: you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation, either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #include <stdlib.h> | ||
19 | #include <string.h> | ||
20 | #include <stdio.h> | ||
21 | #include <stddef.h> | ||
22 | #include <stdbool.h> | ||
23 | #include <fcntl.h> | ||
24 | #include <unistd.h> | ||
25 | #include <errno.h> | ||
26 | #include <grp.h> | ||
27 | #include <dirent.h> | ||
28 | #include <sys/time.h> | ||
29 | #include <sys/stat.h> | ||
30 | #include <sys/types.h> | ||
31 | |||
32 | #include "udev.h" | ||
33 | |||
34 | #define TMP_FILE_EXT ".udev-tmp" | ||
35 | |||
36 | static int node_symlink(struct udev *udev, const char *node, const char *slink) | ||
37 | { | ||
38 | struct stat stats; | ||
39 | char target[UTIL_PATH_SIZE]; | ||
40 | char *s; | ||
41 | size_t l; | ||
42 | char slink_tmp[UTIL_PATH_SIZE + sizeof(TMP_FILE_EXT)]; | ||
43 | int i = 0; | ||
44 | int tail = 0; | ||
45 | int err = 0; | ||
46 | |||
47 | /* use relative link */ | ||
48 | target[0] = '\0'; | ||
49 | while (node[i] && (node[i] == slink[i])) { | ||
50 | if (node[i] == '/') | ||
51 | tail = i+1; | ||
52 | i++; | ||
53 | } | ||
54 | s = target; | ||
55 | l = sizeof(target); | ||
56 | while (slink[i] != '\0') { | ||
57 | if (slink[i] == '/') | ||
58 | l = util_strpcpy(&s, l, "../"); | ||
59 | i++; | ||
60 | } | ||
61 | l = util_strscpy(s, l, &node[tail]); | ||
62 | if (l == 0) { | ||
63 | err = -EINVAL; | ||
64 | goto exit; | ||
65 | } | ||
66 | |||
67 | /* preserve link with correct target, do not replace node of other device */ | ||
68 | if (lstat(slink, &stats) == 0) { | ||
69 | if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { | ||
70 | struct stat stats2; | ||
71 | |||
72 | info(udev, "found existing node instead of symlink '%s'\n", slink); | ||
73 | if (lstat(node, &stats2) == 0) { | ||
74 | if ((stats.st_mode & S_IFMT) == (stats2.st_mode & S_IFMT) && | ||
75 | stats.st_rdev == stats2.st_rdev && stats.st_ino != stats2.st_ino) { | ||
76 | info(udev, "replace device node '%s' with symlink to our node '%s'\n", | ||
77 | slink, node); | ||
78 | } else { | ||
79 | err(udev, "device node '%s' already exists, " | ||
80 | "link to '%s' will not overwrite it\n", | ||
81 | slink, node); | ||
82 | goto exit; | ||
83 | } | ||
84 | } | ||
85 | } else if (S_ISLNK(stats.st_mode)) { | ||
86 | char buf[UTIL_PATH_SIZE]; | ||
87 | int len; | ||
88 | |||
89 | dbg(udev, "found existing symlink '%s'\n", slink); | ||
90 | len = readlink(slink, buf, sizeof(buf)); | ||
91 | if (len > 0 && len < (int)sizeof(buf)) { | ||
92 | buf[len] = '\0'; | ||
93 | if (strcmp(target, buf) == 0) { | ||
94 | info(udev, "preserve already existing symlink '%s' to '%s'\n", | ||
95 | slink, target); | ||
96 | udev_selinux_lsetfilecon(udev, slink, S_IFLNK); | ||
97 | utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW); | ||
98 | goto exit; | ||
99 | } | ||
100 | } | ||
101 | } | ||
102 | } else { | ||
103 | info(udev, "creating symlink '%s' to '%s'\n", slink, target); | ||
104 | do { | ||
105 | err = util_create_path_selinux(udev, slink); | ||
106 | if (err != 0 && err != -ENOENT) | ||
107 | break; | ||
108 | udev_selinux_setfscreatecon(udev, slink, S_IFLNK); | ||
109 | err = symlink(target, slink); | ||
110 | if (err != 0) | ||
111 | err = -errno; | ||
112 | udev_selinux_resetfscreatecon(udev); | ||
113 | } while (err == -ENOENT); | ||
114 | if (err == 0) | ||
115 | goto exit; | ||
116 | } | ||
117 | |||
118 | info(udev, "atomically replace '%s'\n", slink); | ||
119 | util_strscpyl(slink_tmp, sizeof(slink_tmp), slink, TMP_FILE_EXT, NULL); | ||
120 | unlink(slink_tmp); | ||
121 | do { | ||
122 | err = util_create_path_selinux(udev, slink_tmp); | ||
123 | if (err != 0 && err != -ENOENT) | ||
124 | break; | ||
125 | udev_selinux_setfscreatecon(udev, slink_tmp, S_IFLNK); | ||
126 | err = symlink(target, slink_tmp); | ||
127 | if (err != 0) | ||
128 | err = -errno; | ||
129 | udev_selinux_resetfscreatecon(udev); | ||
130 | } while (err == -ENOENT); | ||
131 | if (err != 0) { | ||
132 | err(udev, "symlink '%s' '%s' failed: %m\n", target, slink_tmp); | ||
133 | goto exit; | ||
134 | } | ||
135 | err = rename(slink_tmp, slink); | ||
136 | if (err != 0) { | ||
137 | err(udev, "rename '%s' '%s' failed: %m\n", slink_tmp, slink); | ||
138 | unlink(slink_tmp); | ||
139 | } | ||
140 | exit: | ||
141 | return err; | ||
142 | } | ||
143 | |||
144 | /* find device node of device with highest priority */ | ||
145 | static const char *link_find_prioritized(struct udev_device *dev, bool add, const char *stackdir, char *buf, size_t bufsize) | ||
146 | { | ||
147 | struct udev *udev = udev_device_get_udev(dev); | ||
148 | DIR *dir; | ||
149 | int priority = 0; | ||
150 | const char *target = NULL; | ||
151 | |||
152 | if (add) { | ||
153 | priority = udev_device_get_devlink_priority(dev); | ||
154 | util_strscpy(buf, bufsize, udev_device_get_devnode(dev)); | ||
155 | target = buf; | ||
156 | } | ||
157 | |||
158 | dir = opendir(stackdir); | ||
159 | if (dir == NULL) | ||
160 | return target; | ||
161 | for (;;) { | ||
162 | struct udev_device *dev_db; | ||
163 | struct dirent *dent; | ||
164 | |||
165 | dent = readdir(dir); | ||
166 | if (dent == NULL || dent->d_name[0] == '\0') | ||
167 | break; | ||
168 | if (dent->d_name[0] == '.') | ||
169 | continue; | ||
170 | |||
171 | info(udev, "found '%s' claiming '%s'\n", dent->d_name, stackdir); | ||
172 | |||
173 | /* did we find ourself? */ | ||
174 | if (strcmp(dent->d_name, udev_device_get_id_filename(dev)) == 0) | ||
175 | continue; | ||
176 | |||
177 | dev_db = udev_device_new_from_device_id(udev, dent->d_name); | ||
178 | if (dev_db != NULL) { | ||
179 | const char *devnode; | ||
180 | |||
181 | devnode = udev_device_get_devnode(dev_db); | ||
182 | if (devnode != NULL) { | ||
183 | dbg(udev, "compare priority of '%s'(%i) > '%s'(%i)\n", target, priority, | ||
184 | udev_device_get_devnode(dev_db), udev_device_get_devlink_priority(dev_db)); | ||
185 | if (target == NULL || udev_device_get_devlink_priority(dev_db) > priority) { | ||
186 | info(udev, "'%s' claims priority %i for '%s'\n", | ||
187 | udev_device_get_syspath(dev_db), udev_device_get_devlink_priority(dev_db), stackdir); | ||
188 | priority = udev_device_get_devlink_priority(dev_db); | ||
189 | util_strscpy(buf, bufsize, devnode); | ||
190 | target = buf; | ||
191 | } | ||
192 | } | ||
193 | udev_device_unref(dev_db); | ||
194 | } | ||
195 | } | ||
196 | closedir(dir); | ||
197 | return target; | ||
198 | } | ||
199 | |||
200 | /* manage "stack of names" with possibly specified device priorities */ | ||
201 | static void link_update(struct udev_device *dev, const char *slink, bool add) | ||
202 | { | ||
203 | struct udev *udev = udev_device_get_udev(dev); | ||
204 | char name_enc[UTIL_PATH_SIZE]; | ||
205 | char filename[UTIL_PATH_SIZE * 2]; | ||
206 | char dirname[UTIL_PATH_SIZE]; | ||
207 | const char *target; | ||
208 | char buf[UTIL_PATH_SIZE]; | ||
209 | |||
210 | dbg(udev, "update symlink '%s' of '%s'\n", slink, udev_device_get_syspath(dev)); | ||
211 | |||
212 | util_path_encode(&slink[strlen(udev_get_dev_path(udev))+1], name_enc, sizeof(name_enc)); | ||
213 | util_strscpyl(dirname, sizeof(dirname), udev_get_run_path(udev), "/links/", name_enc, NULL); | ||
214 | util_strscpyl(filename, sizeof(filename), dirname, "/", udev_device_get_id_filename(dev), NULL); | ||
215 | |||
216 | if (!add) { | ||
217 | dbg(udev, "removing index: '%s'\n", filename); | ||
218 | if (unlink(filename) == 0) | ||
219 | rmdir(dirname); | ||
220 | } | ||
221 | |||
222 | target = link_find_prioritized(dev, add, dirname, buf, sizeof(buf)); | ||
223 | if (target == NULL) { | ||
224 | info(udev, "no reference left, remove '%s'\n", slink); | ||
225 | if (unlink(slink) == 0) | ||
226 | util_delete_path(udev, slink); | ||
227 | } else { | ||
228 | info(udev, "creating link '%s' to '%s'\n", slink, target); | ||
229 | node_symlink(udev, target, slink); | ||
230 | } | ||
231 | |||
232 | if (add) { | ||
233 | int err; | ||
234 | |||
235 | dbg(udev, "creating index: '%s'\n", filename); | ||
236 | do { | ||
237 | int fd; | ||
238 | |||
239 | err = util_create_path(udev, filename); | ||
240 | if (err != 0 && err != -ENOENT) | ||
241 | break; | ||
242 | fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444); | ||
243 | if (fd >= 0) | ||
244 | close(fd); | ||
245 | else | ||
246 | err = -errno; | ||
247 | } while (err == -ENOENT); | ||
248 | } | ||
249 | } | ||
250 | |||
251 | void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old) | ||
252 | { | ||
253 | struct udev *udev = udev_device_get_udev(dev); | ||
254 | struct udev_list_entry *list_entry; | ||
255 | |||
256 | /* update possible left-over symlinks */ | ||
257 | udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev_old)) { | ||
258 | const char *name = udev_list_entry_get_name(list_entry); | ||
259 | struct udev_list_entry *list_entry_current; | ||
260 | int found; | ||
261 | |||
262 | /* check if old link name still belongs to this device */ | ||
263 | found = 0; | ||
264 | udev_list_entry_foreach(list_entry_current, udev_device_get_devlinks_list_entry(dev)) { | ||
265 | const char *name_current = udev_list_entry_get_name(list_entry_current); | ||
266 | |||
267 | if (strcmp(name, name_current) == 0) { | ||
268 | found = 1; | ||
269 | break; | ||
270 | } | ||
271 | } | ||
272 | if (found) | ||
273 | continue; | ||
274 | |||
275 | info(udev, "update old name, '%s' no longer belonging to '%s'\n", | ||
276 | name, udev_device_get_devpath(dev)); | ||
277 | link_update(dev, name, 0); | ||
278 | } | ||
279 | } | ||
280 | |||
281 | static int node_fixup(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid) | ||
282 | { | ||
283 | struct udev *udev = udev_device_get_udev(dev); | ||
284 | const char *devnode = udev_device_get_devnode(dev); | ||
285 | dev_t devnum = udev_device_get_devnum(dev); | ||
286 | struct stat stats; | ||
287 | int err = 0; | ||
288 | |||
289 | if (strcmp(udev_device_get_subsystem(dev), "block") == 0) | ||
290 | mode |= S_IFBLK; | ||
291 | else | ||
292 | mode |= S_IFCHR; | ||
293 | |||
294 | if (lstat(devnode, &stats) != 0) { | ||
295 | err = -errno; | ||
296 | info(udev, "can not stat() node '%s' (%m)\n", devnode); | ||
297 | goto out; | ||
298 | } | ||
299 | |||
300 | if (((stats.st_mode & S_IFMT) != (mode & S_IFMT)) || (stats.st_rdev != devnum)) { | ||
301 | err = -EEXIST; | ||
302 | info(udev, "found node '%s' with non-matching devnum %s, skip handling\n", | ||
303 | udev_device_get_devnode(dev), udev_device_get_id_filename(dev)); | ||
304 | goto out; | ||
305 | } | ||
306 | |||
307 | if ((stats.st_mode & 0777) != (mode & 0777) || stats.st_uid != uid || stats.st_gid != gid) { | ||
308 | info(udev, "set permissions %s, %#o, uid=%u, gid=%u\n", devnode, mode, uid, gid); | ||
309 | chmod(devnode, mode); | ||
310 | chown(devnode, uid, gid); | ||
311 | } else { | ||
312 | info(udev, "preserve permissions %s, %#o, uid=%u, gid=%u\n", devnode, mode, uid, gid); | ||
313 | } | ||
314 | |||
315 | /* | ||
316 | * Set initial selinux file context only on add events. | ||
317 | * We set the proper context on bootup (triger) or for newly | ||
318 | * added devices, but we don't change it later, in case | ||
319 | * something else has set a custom context in the meantime. | ||
320 | */ | ||
321 | if (strcmp(udev_device_get_action(dev), "add") == 0) | ||
322 | udev_selinux_lsetfilecon(udev, devnode, mode); | ||
323 | |||
324 | /* always update timestamp when we re-use the node, like on media change events */ | ||
325 | utimensat(AT_FDCWD, devnode, NULL, 0); | ||
326 | out: | ||
327 | return err; | ||
328 | } | ||
329 | |||
330 | void udev_node_add(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid) | ||
331 | { | ||
332 | struct udev *udev = udev_device_get_udev(dev); | ||
333 | char filename[UTIL_PATH_SIZE]; | ||
334 | struct udev_list_entry *list_entry; | ||
335 | int err = 0; | ||
336 | |||
337 | info(udev, "handling device node '%s', devnum=%s, mode=%#o, uid=%d, gid=%d\n", | ||
338 | udev_device_get_devnode(dev), udev_device_get_id_filename(dev), mode, uid, gid); | ||
339 | |||
340 | if (node_fixup(dev, mode, uid, gid) < 0) | ||
341 | return; | ||
342 | |||
343 | /* always add /dev/{block,char}/$major:$minor */ | ||
344 | snprintf(filename, sizeof(filename), "%s/%s/%u:%u", | ||
345 | udev_get_dev_path(udev), | ||
346 | strcmp(udev_device_get_subsystem(dev), "block") == 0 ? "block" : "char", | ||
347 | major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev))); | ||
348 | node_symlink(udev, udev_device_get_devnode(dev), filename); | ||
349 | |||
350 | /* create/update symlinks, add symlinks to name index */ | ||
351 | udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) { | ||
352 | if (udev_list_entry_get_num(list_entry)) | ||
353 | /* simple unmanaged link name */ | ||
354 | node_symlink(udev, udev_device_get_devnode(dev), udev_list_entry_get_name(list_entry)); | ||
355 | else | ||
356 | link_update(dev, udev_list_entry_get_name(list_entry), 1); | ||
357 | } | ||
358 | } | ||
359 | |||
360 | void udev_node_remove(struct udev_device *dev) | ||
361 | { | ||
362 | struct udev *udev = udev_device_get_udev(dev); | ||
363 | struct udev_list_entry *list_entry; | ||
364 | const char *devnode; | ||
365 | struct stat stats; | ||
366 | struct udev_device *dev_check; | ||
367 | char filename[UTIL_PATH_SIZE]; | ||
368 | |||
369 | /* remove/update symlinks, remove symlinks from name index */ | ||
370 | udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) | ||
371 | link_update(dev, udev_list_entry_get_name(list_entry), 0); | ||
372 | |||
373 | /* remove /dev/{block,char}/$major:$minor */ | ||
374 | snprintf(filename, sizeof(filename), "%s/%s/%u:%u", | ||
375 | udev_get_dev_path(udev), | ||
376 | strcmp(udev_device_get_subsystem(dev), "block") == 0 ? "block" : "char", | ||
377 | major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev))); | ||
378 | unlink(filename); | ||
379 | } |
File src/udev-rules.c added (mode: 100644) (index 0000000..94f0e29) | |||
1 | /* | ||
2 | * Copyright (C) 2003-2010 Kay Sievers <kay.sievers@vrfy.org> | ||
3 | * Copyright (C) 2008 Alan Jenkins <alan-jenkins@tuffmail.co.uk> | ||
4 | * | ||
5 | * This program is free software: you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation, either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | |||
19 | #include <stddef.h> | ||
20 | #include <limits.h> | ||
21 | #include <stdlib.h> | ||
22 | #include <stdbool.h> | ||
23 | #include <string.h> | ||
24 | #include <stdio.h> | ||
25 | #include <fcntl.h> | ||
26 | #include <ctype.h> | ||
27 | #include <unistd.h> | ||
28 | #include <errno.h> | ||
29 | #include <dirent.h> | ||
30 | #include <fnmatch.h> | ||
31 | #include <time.h> | ||
32 | |||
33 | #include "udev.h" | ||
34 | |||
35 | #define PREALLOC_TOKEN 2048 | ||
36 | #define PREALLOC_STRBUF 32 * 1024 | ||
37 | #define PREALLOC_TRIE 256 | ||
38 | |||
39 | struct uid_gid { | ||
40 | unsigned int name_off; | ||
41 | union { | ||
42 | uid_t uid; | ||
43 | gid_t gid; | ||
44 | }; | ||
45 | }; | ||
46 | |||
47 | struct trie_node { | ||
48 | /* this node's first child */ | ||
49 | unsigned int child_idx; | ||
50 | /* the next child of our parent node's child list */ | ||
51 | unsigned int next_child_idx; | ||
52 | /* this node's last child (shortcut for append) */ | ||
53 | unsigned int last_child_idx; | ||
54 | unsigned int value_off; | ||
55 | unsigned short value_len; | ||
56 | unsigned char key; | ||
57 | }; | ||
58 | |||
59 | struct udev_rules { | ||
60 | struct udev *udev; | ||
61 | int resolve_names; | ||
62 | |||
63 | /* every key in the rules file becomes a token */ | ||
64 | struct token *tokens; | ||
65 | unsigned int token_cur; | ||
66 | unsigned int token_max; | ||
67 | |||
68 | /* all key strings are copied to a single string buffer */ | ||
69 | char *buf; | ||
70 | size_t buf_cur; | ||
71 | size_t buf_max; | ||
72 | unsigned int buf_count; | ||
73 | |||
74 | /* during rule parsing, strings are indexed to find duplicates */ | ||
75 | struct trie_node *trie_nodes; | ||
76 | unsigned int trie_nodes_cur; | ||
77 | unsigned int trie_nodes_max; | ||
78 | |||
79 | /* during rule parsing, uid/gid lookup results are cached */ | ||
80 | struct uid_gid *uids; | ||
81 | unsigned int uids_cur; | ||
82 | unsigned int uids_max; | ||
83 | struct uid_gid *gids; | ||
84 | unsigned int gids_cur; | ||
85 | unsigned int gids_max; | ||
86 | }; | ||
87 | |||
88 | /* KEY=="", KEY!="", KEY+="", KEY="", KEY:="" */ | ||
89 | enum operation_type { | ||
90 | OP_UNSET, | ||
91 | |||
92 | OP_MATCH, | ||
93 | OP_NOMATCH, | ||
94 | OP_MATCH_MAX, | ||
95 | |||
96 | OP_ADD, | ||
97 | OP_ASSIGN, | ||
98 | OP_ASSIGN_FINAL, | ||
99 | }; | ||
100 | |||
101 | enum string_glob_type { | ||
102 | GL_UNSET, | ||
103 | GL_PLAIN, /* no special chars */ | ||
104 | GL_GLOB, /* shell globs ?,*,[] */ | ||
105 | GL_SPLIT, /* multi-value A|B */ | ||
106 | GL_SPLIT_GLOB, /* multi-value with glob A*|B* */ | ||
107 | GL_SOMETHING, /* commonly used "?*" */ | ||
108 | }; | ||
109 | |||
110 | enum string_subst_type { | ||
111 | SB_UNSET, | ||
112 | SB_NONE, | ||
113 | SB_FORMAT, | ||
114 | SB_SUBSYS, | ||
115 | }; | ||
116 | |||
117 | /* tokens of a rule are sorted/handled in this order */ | ||
118 | enum token_type { | ||
119 | TK_UNSET, | ||
120 | TK_RULE, | ||
121 | |||
122 | TK_M_ACTION, /* val */ | ||
123 | TK_M_DEVPATH, /* val */ | ||
124 | TK_M_KERNEL, /* val */ | ||
125 | TK_M_DEVLINK, /* val */ | ||
126 | TK_M_NAME, /* val */ | ||
127 | TK_M_ENV, /* val, attr */ | ||
128 | TK_M_TAG, /* val */ | ||
129 | TK_M_SUBSYSTEM, /* val */ | ||
130 | TK_M_DRIVER, /* val */ | ||
131 | TK_M_WAITFOR, /* val */ | ||
132 | TK_M_ATTR, /* val, attr */ | ||
133 | |||
134 | TK_M_PARENTS_MIN, | ||
135 | TK_M_KERNELS, /* val */ | ||
136 | TK_M_SUBSYSTEMS, /* val */ | ||
137 | TK_M_DRIVERS, /* val */ | ||
138 | TK_M_ATTRS, /* val, attr */ | ||
139 | TK_M_TAGS, /* val */ | ||
140 | TK_M_PARENTS_MAX, | ||
141 | |||
142 | TK_M_TEST, /* val, mode_t */ | ||
143 | TK_M_EVENT_TIMEOUT, /* int */ | ||
144 | TK_M_PROGRAM, /* val */ | ||
145 | TK_M_IMPORT_FILE, /* val */ | ||
146 | TK_M_IMPORT_PROG, /* val */ | ||
147 | TK_M_IMPORT_BUILTIN, /* val */ | ||
148 | TK_M_IMPORT_DB, /* val */ | ||
149 | TK_M_IMPORT_CMDLINE, /* val */ | ||
150 | TK_M_IMPORT_PARENT, /* val */ | ||
151 | TK_M_RESULT, /* val */ | ||
152 | TK_M_MAX, | ||
153 | |||
154 | TK_A_STRING_ESCAPE_NONE, | ||
155 | TK_A_STRING_ESCAPE_REPLACE, | ||
156 | TK_A_DB_PERSIST, | ||
157 | TK_A_INOTIFY_WATCH, /* int */ | ||
158 | TK_A_DEVLINK_PRIO, /* int */ | ||
159 | TK_A_OWNER, /* val */ | ||
160 | TK_A_GROUP, /* val */ | ||
161 | TK_A_MODE, /* val */ | ||
162 | TK_A_OWNER_ID, /* uid_t */ | ||
163 | TK_A_GROUP_ID, /* gid_t */ | ||
164 | TK_A_MODE_ID, /* mode_t */ | ||
165 | TK_A_STATIC_NODE, /* val */ | ||
166 | TK_A_ENV, /* val, attr */ | ||
167 | TK_A_TAG, /* val */ | ||
168 | TK_A_NAME, /* val */ | ||
169 | TK_A_DEVLINK, /* val */ | ||
170 | TK_A_ATTR, /* val, attr */ | ||
171 | TK_A_RUN, /* val, bool */ | ||
172 | TK_A_GOTO, /* size_t */ | ||
173 | |||
174 | TK_END, | ||
175 | }; | ||
176 | |||
177 | /* we try to pack stuff in a way that we take only 12 bytes per token */ | ||
178 | struct token { | ||
179 | union { | ||
180 | unsigned char type; /* same in rule and key */ | ||
181 | struct { | ||
182 | enum token_type type:8; | ||
183 | bool can_set_name:1; | ||
184 | bool has_static_node:1; | ||
185 | unsigned int unused:6; | ||
186 | unsigned short token_count; | ||
187 | unsigned int label_off; | ||
188 | unsigned short filename_off; | ||
189 | unsigned short filename_line; | ||
190 | } rule; | ||
191 | struct { | ||
192 | enum token_type type:8; | ||
193 | enum operation_type op:8; | ||
194 | enum string_glob_type glob:8; | ||
195 | enum string_subst_type subst:4; | ||
196 | enum string_subst_type attrsubst:4; | ||
197 | unsigned int value_off; | ||
198 | union { | ||
199 | unsigned int attr_off; | ||
200 | int devlink_unique; | ||
201 | unsigned int rule_goto; | ||
202 | mode_t mode; | ||
203 | uid_t uid; | ||
204 | gid_t gid; | ||
205 | int devlink_prio; | ||
206 | int event_timeout; | ||
207 | int watch; | ||
208 | enum udev_builtin_cmd builtin_cmd; | ||
209 | }; | ||
210 | } key; | ||
211 | }; | ||
212 | }; | ||
213 | |||
214 | #define MAX_TK 64 | ||
215 | struct rule_tmp { | ||
216 | struct udev_rules *rules; | ||
217 | struct token rule; | ||
218 | struct token token[MAX_TK]; | ||
219 | unsigned int token_cur; | ||
220 | }; | ||
221 | |||
222 | #ifdef ENABLE_DEBUG | ||
223 | static const char *operation_str(enum operation_type type) | ||
224 | { | ||
225 | static const char *operation_strs[] = { | ||
226 | [OP_UNSET] = "UNSET", | ||
227 | [OP_MATCH] = "match", | ||
228 | [OP_NOMATCH] = "nomatch", | ||
229 | [OP_MATCH_MAX] = "MATCH_MAX", | ||
230 | |||
231 | [OP_ADD] = "add", | ||
232 | [OP_ASSIGN] = "assign", | ||
233 | [OP_ASSIGN_FINAL] = "assign-final", | ||
234 | } ; | ||
235 | |||
236 | return operation_strs[type]; | ||
237 | } | ||
238 | |||
239 | static const char *string_glob_str(enum string_glob_type type) | ||
240 | { | ||
241 | static const char *string_glob_strs[] = { | ||
242 | [GL_UNSET] = "UNSET", | ||
243 | [GL_PLAIN] = "plain", | ||
244 | [GL_GLOB] = "glob", | ||
245 | [GL_SPLIT] = "split", | ||
246 | [GL_SPLIT_GLOB] = "split-glob", | ||
247 | [GL_SOMETHING] = "split-glob", | ||
248 | }; | ||
249 | |||
250 | return string_glob_strs[type]; | ||
251 | } | ||
252 | |||
253 | static const char *token_str(enum token_type type) | ||
254 | { | ||
255 | static const char *token_strs[] = { | ||
256 | [TK_UNSET] = "UNSET", | ||
257 | [TK_RULE] = "RULE", | ||
258 | |||
259 | [TK_M_ACTION] = "M ACTION", | ||
260 | [TK_M_DEVPATH] = "M DEVPATH", | ||
261 | [TK_M_KERNEL] = "M KERNEL", | ||
262 | [TK_M_DEVLINK] = "M DEVLINK", | ||
263 | [TK_M_NAME] = "M NAME", | ||
264 | [TK_M_ENV] = "M ENV", | ||
265 | [TK_M_TAG] = "M TAG", | ||
266 | [TK_M_SUBSYSTEM] = "M SUBSYSTEM", | ||
267 | [TK_M_DRIVER] = "M DRIVER", | ||
268 | [TK_M_WAITFOR] = "M WAITFOR", | ||
269 | [TK_M_ATTR] = "M ATTR", | ||
270 | |||
271 | [TK_M_PARENTS_MIN] = "M PARENTS_MIN", | ||
272 | [TK_M_KERNELS] = "M KERNELS", | ||
273 | [TK_M_SUBSYSTEMS] = "M SUBSYSTEMS", | ||
274 | [TK_M_DRIVERS] = "M DRIVERS", | ||
275 | [TK_M_ATTRS] = "M ATTRS", | ||
276 | [TK_M_TAGS] = "M TAGS", | ||
277 | [TK_M_PARENTS_MAX] = "M PARENTS_MAX", | ||
278 | |||
279 | [TK_M_TEST] = "M TEST", | ||
280 | [TK_M_EVENT_TIMEOUT] = "M EVENT_TIMEOUT", | ||
281 | [TK_M_PROGRAM] = "M PROGRAM", | ||
282 | [TK_M_IMPORT_FILE] = "M IMPORT_FILE", | ||
283 | [TK_M_IMPORT_PROG] = "M IMPORT_PROG", | ||
284 | [TK_M_IMPORT_BUILTIN] = "M IMPORT_BUILTIN", | ||
285 | [TK_M_IMPORT_DB] = "M IMPORT_DB", | ||
286 | [TK_M_IMPORT_CMDLINE] = "M IMPORT_CMDLINE", | ||
287 | [TK_M_IMPORT_PARENT] = "M IMPORT_PARENT", | ||
288 | [TK_M_RESULT] = "M RESULT", | ||
289 | [TK_M_MAX] = "M MAX", | ||
290 | |||
291 | [TK_A_STRING_ESCAPE_NONE] = "A STRING_ESCAPE_NONE", | ||
292 | [TK_A_STRING_ESCAPE_REPLACE] = "A STRING_ESCAPE_REPLACE", | ||
293 | [TK_A_DB_PERSIST] = "A DB_PERSIST", | ||
294 | [TK_A_INOTIFY_WATCH] = "A INOTIFY_WATCH", | ||
295 | [TK_A_DEVLINK_PRIO] = "A DEVLINK_PRIO", | ||
296 | [TK_A_OWNER] = "A OWNER", | ||
297 | [TK_A_GROUP] = "A GROUP", | ||
298 | [TK_A_MODE] = "A MODE", | ||
299 | [TK_A_OWNER_ID] = "A OWNER_ID", | ||
300 | [TK_A_GROUP_ID] = "A GROUP_ID", | ||
301 | [TK_A_STATIC_NODE] = "A STATIC_NODE", | ||
302 | [TK_A_MODE_ID] = "A MODE_ID", | ||
303 | [TK_A_ENV] = "A ENV", | ||
304 | [TK_A_TAG] = "A ENV", | ||
305 | [TK_A_NAME] = "A NAME", | ||
306 | [TK_A_DEVLINK] = "A DEVLINK", | ||
307 | [TK_A_ATTR] = "A ATTR", | ||
308 | [TK_A_RUN] = "A RUN", | ||
309 | [TK_A_GOTO] = "A GOTO", | ||
310 | |||
311 | [TK_END] = "END", | ||
312 | }; | ||
313 | |||
314 | return token_strs[type]; | ||
315 | } | ||
316 | |||
317 | static void dump_token(struct udev_rules *rules, struct token *token) | ||
318 | { | ||
319 | enum token_type type = token->type; | ||
320 | enum operation_type op = token->key.op; | ||
321 | enum string_glob_type glob = token->key.glob; | ||
322 | const char *value = &rules->buf[token->key.value_off]; | ||
323 | const char *attr = &rules->buf[token->key.attr_off]; | ||
324 | |||
325 | switch (type) { | ||
326 | case TK_RULE: | ||
327 | { | ||
328 | const char *tks_ptr = (char *)rules->tokens; | ||
329 | const char *tk_ptr = (char *)token; | ||
330 | unsigned int idx = (tk_ptr - tks_ptr) / sizeof(struct token); | ||
331 | |||
332 | dbg(rules->udev, "* RULE %s:%u, token: %u, count: %u, label: '%s'\n", | ||
333 | &rules->buf[token->rule.filename_off], token->rule.filename_line, | ||
334 | idx, token->rule.token_count, | ||
335 | &rules->buf[token->rule.label_off]); | ||
336 | break; | ||
337 | } | ||
338 | case TK_M_ACTION: | ||
339 | case TK_M_DEVPATH: | ||
340 | case TK_M_KERNEL: | ||
341 | case TK_M_SUBSYSTEM: | ||
342 | case TK_M_DRIVER: | ||
343 | case TK_M_WAITFOR: | ||
344 | case TK_M_DEVLINK: | ||
345 | case TK_M_NAME: | ||
346 | case TK_M_KERNELS: | ||
347 | case TK_M_SUBSYSTEMS: | ||
348 | case TK_M_DRIVERS: | ||
349 | case TK_M_TAGS: | ||
350 | case TK_M_PROGRAM: | ||
351 | case TK_M_IMPORT_FILE: | ||
352 | case TK_M_IMPORT_PROG: | ||
353 | case TK_M_IMPORT_DB: | ||
354 | case TK_M_IMPORT_CMDLINE: | ||
355 | case TK_M_IMPORT_PARENT: | ||
356 | case TK_M_RESULT: | ||
357 | case TK_A_NAME: | ||
358 | case TK_A_DEVLINK: | ||
359 | case TK_A_OWNER: | ||
360 | case TK_A_GROUP: | ||
361 | case TK_A_MODE: | ||
362 | case TK_A_RUN: | ||
363 | dbg(rules->udev, "%s %s '%s'(%s)\n", | ||
364 | token_str(type), operation_str(op), value, string_glob_str(glob)); | ||
365 | break; | ||
366 | case TK_M_IMPORT_BUILTIN: | ||
367 | dbg(rules->udev, "%s %i '%s'\n", token_str(type), token->key.builtin_cmd, value); | ||
368 | break; | ||
369 | case TK_M_ATTR: | ||
370 | case TK_M_ATTRS: | ||
371 | case TK_M_ENV: | ||
372 | case TK_A_ATTR: | ||
373 | case TK_A_ENV: | ||
374 | dbg(rules->udev, "%s %s '%s' '%s'(%s)\n", | ||
375 | token_str(type), operation_str(op), attr, value, string_glob_str(glob)); | ||
376 | break; | ||
377 | case TK_M_TAG: | ||
378 | case TK_A_TAG: | ||
379 | dbg(rules->udev, "%s %s '%s'\n", token_str(type), operation_str(op), value); | ||
380 | break; | ||
381 | case TK_A_STRING_ESCAPE_NONE: | ||
382 | case TK_A_STRING_ESCAPE_REPLACE: | ||
383 | case TK_A_DB_PERSIST: | ||
384 | dbg(rules->udev, "%s\n", token_str(type)); | ||
385 | break; | ||
386 | case TK_M_TEST: | ||
387 | dbg(rules->udev, "%s %s '%s'(%s) %#o\n", | ||
388 | token_str(type), operation_str(op), value, string_glob_str(glob), token->key.mode); | ||
389 | break; | ||
390 | case TK_A_INOTIFY_WATCH: | ||
391 | dbg(rules->udev, "%s %u\n", token_str(type), token->key.watch); | ||
392 | break; | ||
393 | case TK_A_DEVLINK_PRIO: | ||
394 | dbg(rules->udev, "%s %u\n", token_str(type), token->key.devlink_prio); | ||
395 | break; | ||
396 | case TK_A_OWNER_ID: | ||
397 | dbg(rules->udev, "%s %s %u\n", token_str(type), operation_str(op), token->key.uid); | ||
398 | break; | ||
399 | case TK_A_GROUP_ID: | ||
400 | dbg(rules->udev, "%s %s %u\n", token_str(type), operation_str(op), token->key.gid); | ||
401 | break; | ||
402 | case TK_A_MODE_ID: | ||
403 | dbg(rules->udev, "%s %s %#o\n", token_str(type), operation_str(op), token->key.mode); | ||
404 | break; | ||
405 | case TK_A_STATIC_NODE: | ||
406 | dbg(rules->udev, "%s '%s'\n", token_str(type), value); | ||
407 | break; | ||
408 | case TK_M_EVENT_TIMEOUT: | ||
409 | dbg(rules->udev, "%s %u\n", token_str(type), token->key.event_timeout); | ||
410 | break; | ||
411 | case TK_A_GOTO: | ||
412 | dbg(rules->udev, "%s '%s' %u\n", token_str(type), value, token->key.rule_goto); | ||
413 | break; | ||
414 | case TK_END: | ||
415 | dbg(rules->udev, "* %s\n", token_str(type)); | ||
416 | break; | ||
417 | case TK_M_PARENTS_MIN: | ||
418 | case TK_M_PARENTS_MAX: | ||
419 | case TK_M_MAX: | ||
420 | case TK_UNSET: | ||
421 | dbg(rules->udev, "unknown type %u\n", type); | ||
422 | break; | ||
423 | } | ||
424 | } | ||
425 | |||
426 | static void dump_rules(struct udev_rules *rules) | ||
427 | { | ||
428 | unsigned int i; | ||
429 | |||
430 | dbg(rules->udev, "dumping %u (%zu bytes) tokens, %u (%zu bytes) strings\n", | ||
431 | rules->token_cur, | ||
432 | rules->token_cur * sizeof(struct token), | ||
433 | rules->buf_count, | ||
434 | rules->buf_cur); | ||
435 | for(i = 0; i < rules->token_cur; i++) | ||
436 | dump_token(rules, &rules->tokens[i]); | ||
437 | } | ||
438 | #else | ||
439 | static inline const char *operation_str(enum operation_type type) { return NULL; } | ||
440 | static inline const char *token_str(enum token_type type) { return NULL; } | ||
441 | static inline void dump_token(struct udev_rules *rules, struct token *token) {} | ||
442 | static inline void dump_rules(struct udev_rules *rules) {} | ||
443 | #endif /* ENABLE_DEBUG */ | ||
444 | |||
445 | static int add_new_string(struct udev_rules *rules, const char *str, size_t bytes) | ||
446 | { | ||
447 | int off; | ||
448 | |||
449 | /* grow buffer if needed */ | ||
450 | if (rules->buf_cur + bytes+1 >= rules->buf_max) { | ||
451 | char *buf; | ||
452 | unsigned int add; | ||
453 | |||
454 | /* double the buffer size */ | ||
455 | add = rules->buf_max; | ||
456 | if (add < bytes * 8) | ||
457 | add = bytes * 8; | ||
458 | |||
459 | buf = realloc(rules->buf, rules->buf_max + add); | ||
460 | if (buf == NULL) | ||
461 | return -1; | ||
462 | dbg(rules->udev, "extend buffer from %zu to %zu\n", rules->buf_max, rules->buf_max + add); | ||
463 | rules->buf = buf; | ||
464 | rules->buf_max += add; | ||
465 | } | ||
466 | off = rules->buf_cur; | ||
467 | memcpy(&rules->buf[rules->buf_cur], str, bytes); | ||
468 | rules->buf_cur += bytes; | ||
469 | rules->buf_count++; | ||
470 | return off; | ||
471 | } | ||
472 | |||
473 | static int add_string(struct udev_rules *rules, const char *str) | ||
474 | { | ||
475 | unsigned int node_idx; | ||
476 | struct trie_node *new_node; | ||
477 | unsigned int new_node_idx; | ||
478 | unsigned char key; | ||
479 | unsigned short len; | ||
480 | unsigned int depth; | ||
481 | unsigned int off; | ||
482 | struct trie_node *parent; | ||
483 | |||
484 | /* walk trie, start from last character of str to find matching tails */ | ||
485 | len = strlen(str); | ||
486 | key = str[len-1]; | ||
487 | node_idx = 0; | ||
488 | for (depth = 0; depth <= len; depth++) { | ||
489 | struct trie_node *node; | ||
490 | unsigned int child_idx; | ||
491 | |||
492 | node = &rules->trie_nodes[node_idx]; | ||
493 | off = node->value_off + node->value_len - len; | ||
494 | |||
495 | /* match against current node */ | ||
496 | if (depth == len || (node->value_len >= len && memcmp(&rules->buf[off], str, len) == 0)) | ||
497 | return off; | ||
498 | |||
499 | /* lookup child node */ | ||
500 | key = str[len - 1 - depth]; | ||
501 | child_idx = node->child_idx; | ||
502 | while (child_idx > 0) { | ||
503 | struct trie_node *child; | ||
504 | |||
505 | child = &rules->trie_nodes[child_idx]; | ||
506 | if (child->key == key) | ||
507 | break; | ||
508 | child_idx = child->next_child_idx; | ||
509 | } | ||
510 | if (child_idx == 0) | ||
511 | break; | ||
512 | node_idx = child_idx; | ||
513 | } | ||
514 | |||
515 | /* string not found, add it */ | ||
516 | off = add_new_string(rules, str, len + 1); | ||
517 | |||
518 | /* grow trie nodes if needed */ | ||
519 | if (rules->trie_nodes_cur >= rules->trie_nodes_max) { | ||
520 | struct trie_node *nodes; | ||
521 | unsigned int add; | ||
522 | |||
523 | /* double the buffer size */ | ||
524 | add = rules->trie_nodes_max; | ||
525 | if (add < 8) | ||
526 | add = 8; | ||
527 | |||
528 | nodes = realloc(rules->trie_nodes, (rules->trie_nodes_max + add) * sizeof(struct trie_node)); | ||
529 | if (nodes == NULL) | ||
530 | return -1; | ||
531 | dbg(rules->udev, "extend trie nodes from %u to %u\n", | ||
532 | rules->trie_nodes_max, rules->trie_nodes_max + add); | ||
533 | rules->trie_nodes = nodes; | ||
534 | rules->trie_nodes_max += add; | ||
535 | } | ||
536 | |||
537 | /* get a new node */ | ||
538 | new_node_idx = rules->trie_nodes_cur; | ||
539 | rules->trie_nodes_cur++; | ||
540 | new_node = &rules->trie_nodes[new_node_idx]; | ||
541 | memset(new_node, 0x00, sizeof(struct trie_node)); | ||
542 | new_node->value_off = off; | ||
543 | new_node->value_len = len; | ||
544 | new_node->key = key; | ||
545 | |||
546 | /* join the parent's child list */ | ||
547 | parent = &rules->trie_nodes[node_idx]; | ||
548 | if (parent->child_idx == 0) { | ||
549 | parent->child_idx = new_node_idx; | ||
550 | } else { | ||
551 | struct trie_node *last_child; | ||
552 | |||
553 | last_child = &rules->trie_nodes[parent->last_child_idx]; | ||
554 | last_child->next_child_idx = new_node_idx; | ||
555 | } | ||
556 | parent->last_child_idx = new_node_idx; | ||
557 | return off; | ||
558 | } | ||
559 | |||
560 | static int add_token(struct udev_rules *rules, struct token *token) | ||
561 | { | ||
562 | /* grow buffer if needed */ | ||
563 | if (rules->token_cur+1 >= rules->token_max) { | ||
564 | struct token *tokens; | ||
565 | unsigned int add; | ||
566 | |||
567 | /* double the buffer size */ | ||
568 | add = rules->token_max; | ||
569 | if (add < 8) | ||
570 | add = 8; | ||
571 | |||
572 | tokens = realloc(rules->tokens, (rules->token_max + add ) * sizeof(struct token)); | ||
573 | if (tokens == NULL) | ||
574 | return -1; | ||
575 | dbg(rules->udev, "extend tokens from %u to %u\n", rules->token_max, rules->token_max + add); | ||
576 | rules->tokens = tokens; | ||
577 | rules->token_max += add; | ||
578 | } | ||
579 | memcpy(&rules->tokens[rules->token_cur], token, sizeof(struct token)); | ||
580 | rules->token_cur++; | ||
581 | return 0; | ||
582 | } | ||
583 | |||
584 | static uid_t add_uid(struct udev_rules *rules, const char *owner) | ||
585 | { | ||
586 | unsigned int i; | ||
587 | uid_t uid; | ||
588 | unsigned int off; | ||
589 | |||
590 | /* lookup, if we know it already */ | ||
591 | for (i = 0; i < rules->uids_cur; i++) { | ||
592 | off = rules->uids[i].name_off; | ||
593 | if (strcmp(&rules->buf[off], owner) == 0) { | ||
594 | uid = rules->uids[i].uid; | ||
595 | dbg(rules->udev, "return existing %u for '%s'\n", uid, owner); | ||
596 | return uid; | ||
597 | } | ||
598 | } | ||
599 | uid = util_lookup_user(rules->udev, owner); | ||
600 | |||
601 | /* grow buffer if needed */ | ||
602 | if (rules->uids_cur+1 >= rules->uids_max) { | ||
603 | struct uid_gid *uids; | ||
604 | unsigned int add; | ||
605 | |||
606 | /* double the buffer size */ | ||
607 | add = rules->uids_max; | ||
608 | if (add < 1) | ||
609 | add = 8; | ||
610 | |||
611 | uids = realloc(rules->uids, (rules->uids_max + add ) * sizeof(struct uid_gid)); | ||
612 | if (uids == NULL) | ||
613 | return uid; | ||
614 | dbg(rules->udev, "extend uids from %u to %u\n", rules->uids_max, rules->uids_max + add); | ||
615 | rules->uids = uids; | ||
616 | rules->uids_max += add; | ||
617 | } | ||
618 | rules->uids[rules->uids_cur].uid = uid; | ||
619 | off = add_string(rules, owner); | ||
620 | if (off <= 0) | ||
621 | return uid; | ||
622 | rules->uids[rules->uids_cur].name_off = off; | ||
623 | rules->uids_cur++; | ||
624 | return uid; | ||
625 | } | ||
626 | |||
627 | static gid_t add_gid(struct udev_rules *rules, const char *group) | ||
628 | { | ||
629 | unsigned int i; | ||
630 | gid_t gid; | ||
631 | unsigned int off; | ||
632 | |||
633 | /* lookup, if we know it already */ | ||
634 | for (i = 0; i < rules->gids_cur; i++) { | ||
635 | off = rules->gids[i].name_off; | ||
636 | if (strcmp(&rules->buf[off], group) == 0) { | ||
637 | gid = rules->gids[i].gid; | ||
638 | dbg(rules->udev, "return existing %u for '%s'\n", gid, group); | ||
639 | return gid; | ||
640 | } | ||
641 | } | ||
642 | gid = util_lookup_group(rules->udev, group); | ||
643 | |||
644 | /* grow buffer if needed */ | ||
645 | if (rules->gids_cur+1 >= rules->gids_max) { | ||
646 | struct uid_gid *gids; | ||
647 | unsigned int add; | ||
648 | |||
649 | /* double the buffer size */ | ||
650 | add = rules->gids_max; | ||
651 | if (add < 1) | ||
652 | add = 8; | ||
653 | |||
654 | gids = realloc(rules->gids, (rules->gids_max + add ) * sizeof(struct uid_gid)); | ||
655 | if (gids == NULL) | ||
656 | return gid; | ||
657 | dbg(rules->udev, "extend gids from %u to %u\n", rules->gids_max, rules->gids_max + add); | ||
658 | rules->gids = gids; | ||
659 | rules->gids_max += add; | ||
660 | } | ||
661 | rules->gids[rules->gids_cur].gid = gid; | ||
662 | off = add_string(rules, group); | ||
663 | if (off <= 0) | ||
664 | return gid; | ||
665 | rules->gids[rules->gids_cur].name_off = off; | ||
666 | rules->gids_cur++; | ||
667 | return gid; | ||
668 | } | ||
669 | |||
670 | static int import_property_from_string(struct udev_device *dev, char *line) | ||
671 | { | ||
672 | struct udev *udev = udev_device_get_udev(dev); | ||
673 | char *key; | ||
674 | char *val; | ||
675 | size_t len; | ||
676 | |||
677 | /* find key */ | ||
678 | key = line; | ||
679 | while (isspace(key[0])) | ||
680 | key++; | ||
681 | |||
682 | /* comment or empty line */ | ||
683 | if (key[0] == '#' || key[0] == '\0') | ||
684 | return -1; | ||
685 | |||
686 | /* split key/value */ | ||
687 | val = strchr(key, '='); | ||
688 | if (val == NULL) | ||
689 | return -1; | ||
690 | val[0] = '\0'; | ||
691 | val++; | ||
692 | |||
693 | /* find value */ | ||
694 | while (isspace(val[0])) | ||
695 | val++; | ||
696 | |||
697 | /* terminate key */ | ||
698 | len = strlen(key); | ||
699 | if (len == 0) | ||
700 | return -1; | ||
701 | while (isspace(key[len-1])) | ||
702 | len--; | ||
703 | key[len] = '\0'; | ||
704 | |||
705 | /* terminate value */ | ||
706 | len = strlen(val); | ||
707 | if (len == 0) | ||
708 | return -1; | ||
709 | while (isspace(val[len-1])) | ||
710 | len--; | ||
711 | val[len] = '\0'; | ||
712 | |||
713 | if (len == 0) | ||
714 | return -1; | ||
715 | |||
716 | /* unquote */ | ||
717 | if (val[0] == '"' || val[0] == '\'') { | ||
718 | if (val[len-1] != val[0]) { | ||
719 | info(udev, "inconsistent quoting: '%s', skip\n", line); | ||
720 | return -1; | ||
721 | } | ||
722 | val[len-1] = '\0'; | ||
723 | val++; | ||
724 | } | ||
725 | |||
726 | dbg(udev, "adding '%s'='%s'\n", key, val); | ||
727 | |||
728 | /* handle device, renamed by external tool, returning new path */ | ||
729 | if (strcmp(key, "DEVPATH") == 0) { | ||
730 | char syspath[UTIL_PATH_SIZE]; | ||
731 | |||
732 | info(udev, "updating devpath from '%s' to '%s'\n", | ||
733 | udev_device_get_devpath(dev), val); | ||
734 | util_strscpyl(syspath, sizeof(syspath), udev_get_sys_path(udev), val, NULL); | ||
735 | udev_device_set_syspath(dev, syspath); | ||
736 | } else { | ||
737 | struct udev_list_entry *entry; | ||
738 | |||
739 | entry = udev_device_add_property(dev, key, val); | ||
740 | /* store in db, skip private keys */ | ||
741 | if (key[0] != '.') | ||
742 | udev_list_entry_set_num(entry, true); | ||
743 | } | ||
744 | return 0; | ||
745 | } | ||
746 | |||
747 | static int import_file_into_properties(struct udev_device *dev, const char *filename) | ||
748 | { | ||
749 | FILE *f; | ||
750 | char line[UTIL_LINE_SIZE]; | ||
751 | |||
752 | f = fopen(filename, "r"); | ||
753 | if (f == NULL) | ||
754 | return -1; | ||
755 | while (fgets(line, sizeof(line), f) != NULL) | ||
756 | import_property_from_string(dev, line); | ||
757 | fclose(f); | ||
758 | return 0; | ||
759 | } | ||
760 | |||
761 | static int import_program_into_properties(struct udev_event *event, const char *program, const sigset_t *sigmask) | ||
762 | { | ||
763 | struct udev_device *dev = event->dev; | ||
764 | char **envp; | ||
765 | char result[UTIL_LINE_SIZE]; | ||
766 | char *line; | ||
767 | int err; | ||
768 | |||
769 | envp = udev_device_get_properties_envp(dev); | ||
770 | err = udev_event_spawn(event, program, envp, sigmask, result, sizeof(result)); | ||
771 | if (err < 0) | ||
772 | return err; | ||
773 | |||
774 | line = result; | ||
775 | while (line != NULL) { | ||
776 | char *pos; | ||
777 | |||
778 | pos = strchr(line, '\n'); | ||
779 | if (pos != NULL) { | ||
780 | pos[0] = '\0'; | ||
781 | pos = &pos[1]; | ||
782 | } | ||
783 | import_property_from_string(dev, line); | ||
784 | line = pos; | ||
785 | } | ||
786 | return 0; | ||
787 | } | ||
788 | |||
789 | static int import_parent_into_properties(struct udev_device *dev, const char *filter) | ||
790 | { | ||
791 | struct udev *udev = udev_device_get_udev(dev); | ||
792 | struct udev_device *dev_parent; | ||
793 | struct udev_list_entry *list_entry; | ||
794 | |||
795 | dev_parent = udev_device_get_parent(dev); | ||
796 | if (dev_parent == NULL) | ||
797 | return -1; | ||
798 | |||
799 | dbg(udev, "found parent '%s', get the node name\n", udev_device_get_syspath(dev_parent)); | ||
800 | udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev_parent)) { | ||
801 | const char *key = udev_list_entry_get_name(list_entry); | ||
802 | const char *val = udev_list_entry_get_value(list_entry); | ||
803 | |||
804 | if (fnmatch(filter, key, 0) == 0) { | ||
805 | struct udev_list_entry *entry; | ||
806 | |||
807 | dbg(udev, "import key '%s=%s'\n", key, val); | ||
808 | entry = udev_device_add_property(dev, key, val); | ||
809 | /* store in db, skip private keys */ | ||
810 | if (key[0] != '.') | ||
811 | udev_list_entry_set_num(entry, true); | ||
812 | } | ||
813 | } | ||
814 | return 0; | ||
815 | } | ||
816 | |||
817 | #define WAIT_LOOP_PER_SECOND 50 | ||
818 | static int wait_for_file(struct udev_device *dev, const char *file, int timeout) | ||
819 | { | ||
820 | struct udev *udev = udev_device_get_udev(dev); | ||
821 | char filepath[UTIL_PATH_SIZE]; | ||
822 | char devicepath[UTIL_PATH_SIZE]; | ||
823 | struct stat stats; | ||
824 | int loop = timeout * WAIT_LOOP_PER_SECOND; | ||
825 | |||
826 | /* a relative path is a device attribute */ | ||
827 | devicepath[0] = '\0'; | ||
828 | if (file[0] != '/') { | ||
829 | util_strscpyl(devicepath, sizeof(devicepath), | ||
830 | udev_get_sys_path(udev), udev_device_get_devpath(dev), NULL); | ||
831 | util_strscpyl(filepath, sizeof(filepath), devicepath, "/", file, NULL); | ||
832 | file = filepath; | ||
833 | } | ||
834 | |||
835 | dbg(udev, "will wait %i sec for '%s'\n", timeout, file); | ||
836 | while (--loop) { | ||
837 | const struct timespec duration = { 0, 1000 * 1000 * 1000 / WAIT_LOOP_PER_SECOND }; | ||
838 | |||
839 | /* lookup file */ | ||
840 | if (stat(file, &stats) == 0) { | ||
841 | info(udev, "file '%s' appeared after %i loops\n", file, (timeout * WAIT_LOOP_PER_SECOND) - loop-1); | ||
842 | return 0; | ||
843 | } | ||
844 | /* make sure, the device did not disappear in the meantime */ | ||
845 | if (devicepath[0] != '\0' && stat(devicepath, &stats) != 0) { | ||
846 | info(udev, "device disappeared while waiting for '%s'\n", file); | ||
847 | return -2; | ||
848 | } | ||
849 | info(udev, "wait for '%s' for %i mseconds\n", file, 1000 / WAIT_LOOP_PER_SECOND); | ||
850 | nanosleep(&duration, NULL); | ||
851 | } | ||
852 | info(udev, "waiting for '%s' failed\n", file); | ||
853 | return -1; | ||
854 | } | ||
855 | |||
856 | static int attr_subst_subdir(char *attr, size_t len) | ||
857 | { | ||
858 | bool found = false; | ||
859 | |||
860 | if (strstr(attr, "/*/")) { | ||
861 | char *pos; | ||
862 | char dirname[UTIL_PATH_SIZE]; | ||
863 | const char *tail; | ||
864 | DIR *dir; | ||
865 | |||
866 | util_strscpy(dirname, sizeof(dirname), attr); | ||
867 | pos = strstr(dirname, "/*/"); | ||
868 | if (pos == NULL) | ||
869 | return -1; | ||
870 | pos[0] = '\0'; | ||
871 | tail = &pos[2]; | ||
872 | dir = opendir(dirname); | ||
873 | if (dir != NULL) { | ||
874 | struct dirent *dent; | ||
875 | |||
876 | for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { | ||
877 | struct stat stats; | ||
878 | |||
879 | if (dent->d_name[0] == '.') | ||
880 | continue; | ||
881 | util_strscpyl(attr, len, dirname, "/", dent->d_name, tail, NULL); | ||
882 | if (stat(attr, &stats) == 0) { | ||
883 | found = true; | ||
884 | break; | ||
885 | } | ||
886 | } | ||
887 | closedir(dir); | ||
888 | } | ||
889 | } | ||
890 | |||
891 | return found; | ||
892 | } | ||
893 | |||
894 | static int get_key(struct udev *udev, char **line, char **key, enum operation_type *op, char **value) | ||
895 | { | ||
896 | char *linepos; | ||
897 | char *temp; | ||
898 | |||
899 | linepos = *line; | ||
900 | if (linepos == NULL || linepos[0] == '\0') | ||
901 | return -1; | ||
902 | |||
903 | /* skip whitespace */ | ||
904 | while (isspace(linepos[0]) || linepos[0] == ',') | ||
905 | linepos++; | ||
906 | |||
907 | /* get the key */ | ||
908 | if (linepos[0] == '\0') | ||
909 | return -1; | ||
910 | *key = linepos; | ||
911 | |||
912 | for (;;) { | ||
913 | linepos++; | ||
914 | if (linepos[0] == '\0') | ||
915 | return -1; | ||
916 | if (isspace(linepos[0])) | ||
917 | break; | ||
918 | if (linepos[0] == '=') | ||
919 | break; | ||
920 | if ((linepos[0] == '+') || (linepos[0] == '!') || (linepos[0] == ':')) | ||
921 | if (linepos[1] == '=') | ||
922 | break; | ||
923 | } | ||
924 | |||
925 | /* remember end of key */ | ||
926 | temp = linepos; | ||
927 | |||
928 | /* skip whitespace after key */ | ||
929 | while (isspace(linepos[0])) | ||
930 | linepos++; | ||
931 | if (linepos[0] == '\0') | ||
932 | return -1; | ||
933 | |||
934 | /* get operation type */ | ||
935 | if (linepos[0] == '=' && linepos[1] == '=') { | ||
936 | *op = OP_MATCH; | ||
937 | linepos += 2; | ||
938 | } else if (linepos[0] == '!' && linepos[1] == '=') { | ||
939 | *op = OP_NOMATCH; | ||
940 | linepos += 2; | ||
941 | } else if (linepos[0] == '+' && linepos[1] == '=') { | ||
942 | *op = OP_ADD; | ||
943 | linepos += 2; | ||
944 | } else if (linepos[0] == '=') { | ||
945 | *op = OP_ASSIGN; | ||
946 | linepos++; | ||
947 | } else if (linepos[0] == ':' && linepos[1] == '=') { | ||
948 | *op = OP_ASSIGN_FINAL; | ||
949 | linepos += 2; | ||
950 | } else | ||
951 | return -1; | ||
952 | |||
953 | /* terminate key */ | ||
954 | temp[0] = '\0'; | ||
955 | |||
956 | /* skip whitespace after operator */ | ||
957 | while (isspace(linepos[0])) | ||
958 | linepos++; | ||
959 | if (linepos[0] == '\0') | ||
960 | return -1; | ||
961 | |||
962 | /* get the value */ | ||
963 | if (linepos[0] == '"') | ||
964 | linepos++; | ||
965 | else | ||
966 | return -1; | ||
967 | *value = linepos; | ||
968 | |||
969 | /* terminate */ | ||
970 | temp = strchr(linepos, '"'); | ||
971 | if (!temp) | ||
972 | return -1; | ||
973 | temp[0] = '\0'; | ||
974 | temp++; | ||
975 | dbg(udev, "%s '%s'-'%s'\n", operation_str(*op), *key, *value); | ||
976 | |||
977 | /* move line to next key */ | ||
978 | *line = temp; | ||
979 | return 0; | ||
980 | } | ||
981 | |||
982 | /* extract possible KEY{attr} */ | ||
983 | static char *get_key_attribute(struct udev *udev, char *str) | ||
984 | { | ||
985 | char *pos; | ||
986 | char *attr; | ||
987 | |||
988 | attr = strchr(str, '{'); | ||
989 | if (attr != NULL) { | ||
990 | attr++; | ||
991 | pos = strchr(attr, '}'); | ||
992 | if (pos == NULL) { | ||
993 | err(udev, "missing closing brace for format\n"); | ||
994 | return NULL; | ||
995 | } | ||
996 | pos[0] = '\0'; | ||
997 | dbg(udev, "attribute='%s'\n", attr); | ||
998 | return attr; | ||
999 | } | ||
1000 | return NULL; | ||
1001 | } | ||
1002 | |||
1003 | static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, | ||
1004 | enum operation_type op, | ||
1005 | const char *value, const void *data) | ||
1006 | { | ||
1007 | struct token *token = &rule_tmp->token[rule_tmp->token_cur]; | ||
1008 | const char *attr = NULL; | ||
1009 | |||
1010 | memset(token, 0x00, sizeof(struct token)); | ||
1011 | |||
1012 | switch (type) { | ||
1013 | case TK_M_ACTION: | ||
1014 | case TK_M_DEVPATH: | ||
1015 | case TK_M_KERNEL: | ||
1016 | case TK_M_SUBSYSTEM: | ||
1017 | case TK_M_DRIVER: | ||
1018 | case TK_M_WAITFOR: | ||
1019 | case TK_M_DEVLINK: | ||
1020 | case TK_M_NAME: | ||
1021 | case TK_M_KERNELS: | ||
1022 | case TK_M_SUBSYSTEMS: | ||
1023 | case TK_M_DRIVERS: | ||
1024 | case TK_M_TAGS: | ||
1025 | case TK_M_PROGRAM: | ||
1026 | case TK_M_IMPORT_FILE: | ||
1027 | case TK_M_IMPORT_PROG: | ||
1028 | case TK_M_IMPORT_DB: | ||
1029 | case TK_M_IMPORT_CMDLINE: | ||
1030 | case TK_M_IMPORT_PARENT: | ||
1031 | case TK_M_RESULT: | ||
1032 | case TK_A_OWNER: | ||
1033 | case TK_A_GROUP: | ||
1034 | case TK_A_MODE: | ||
1035 | case TK_A_NAME: | ||
1036 | case TK_A_GOTO: | ||
1037 | case TK_M_TAG: | ||
1038 | case TK_A_TAG: | ||
1039 | token->key.value_off = add_string(rule_tmp->rules, value); | ||
1040 | break; | ||
1041 | case TK_M_IMPORT_BUILTIN: | ||
1042 | token->key.value_off = add_string(rule_tmp->rules, value); | ||
1043 | token->key.builtin_cmd = *(enum udev_builtin_cmd *)data; | ||
1044 | break; | ||
1045 | case TK_M_ENV: | ||
1046 | case TK_M_ATTR: | ||
1047 | case TK_M_ATTRS: | ||
1048 | case TK_A_ATTR: | ||
1049 | case TK_A_ENV: | ||
1050 | attr = data; | ||
1051 | token->key.value_off = add_string(rule_tmp->rules, value); | ||
1052 | token->key.attr_off = add_string(rule_tmp->rules, attr); | ||
1053 | break; | ||
1054 | case TK_A_DEVLINK: | ||
1055 | token->key.value_off = add_string(rule_tmp->rules, value); | ||
1056 | token->key.devlink_unique = *(int *)data; | ||
1057 | break; | ||
1058 | case TK_M_TEST: | ||
1059 | token->key.value_off = add_string(rule_tmp->rules, value); | ||
1060 | if (data != NULL) | ||
1061 | token->key.mode = *(mode_t *)data; | ||
1062 | break; | ||
1063 | case TK_A_STRING_ESCAPE_NONE: | ||
1064 | case TK_A_STRING_ESCAPE_REPLACE: | ||
1065 | case TK_A_DB_PERSIST: | ||
1066 | break; | ||
1067 | case TK_A_RUN: | ||
1068 | token->key.value_off = add_string(rule_tmp->rules, value); | ||
1069 | break; | ||
1070 | case TK_A_INOTIFY_WATCH: | ||
1071 | case TK_A_DEVLINK_PRIO: | ||
1072 | token->key.devlink_prio = *(int *)data; | ||
1073 | break; | ||
1074 | case TK_A_OWNER_ID: | ||
1075 | token->key.uid = *(uid_t *)data; | ||
1076 | break; | ||
1077 | case TK_A_GROUP_ID: | ||
1078 | token->key.gid = *(gid_t *)data; | ||
1079 | break; | ||
1080 | case TK_A_MODE_ID: | ||
1081 | token->key.mode = *(mode_t *)data; | ||
1082 | break; | ||
1083 | case TK_A_STATIC_NODE: | ||
1084 | token->key.value_off = add_string(rule_tmp->rules, value); | ||
1085 | break; | ||
1086 | case TK_M_EVENT_TIMEOUT: | ||
1087 | token->key.event_timeout = *(int *)data; | ||
1088 | break; | ||
1089 | case TK_RULE: | ||
1090 | case TK_M_PARENTS_MIN: | ||
1091 | case TK_M_PARENTS_MAX: | ||
1092 | case TK_M_MAX: | ||
1093 | case TK_END: | ||
1094 | case TK_UNSET: | ||
1095 | err(rule_tmp->rules->udev, "wrong type %u\n", type); | ||
1096 | return -1; | ||
1097 | } | ||
1098 | |||
1099 | if (value != NULL && type < TK_M_MAX) { | ||
1100 | /* check if we need to split or call fnmatch() while matching rules */ | ||
1101 | enum string_glob_type glob; | ||
1102 | int has_split; | ||
1103 | int has_glob; | ||
1104 | |||
1105 | has_split = (strchr(value, '|') != NULL); | ||
1106 | has_glob = (strchr(value, '*') != NULL || strchr(value, '?') != NULL || strchr(value, '[') != NULL); | ||
1107 | if (has_split && has_glob) { | ||
1108 | glob = GL_SPLIT_GLOB; | ||
1109 | } else if (has_split) { | ||
1110 | glob = GL_SPLIT; | ||
1111 | } else if (has_glob) { | ||
1112 | if (strcmp(value, "?*") == 0) | ||
1113 | glob = GL_SOMETHING; | ||
1114 | else | ||
1115 | glob = GL_GLOB; | ||
1116 | } else { | ||
1117 | glob = GL_PLAIN; | ||
1118 | } | ||
1119 | token->key.glob = glob; | ||
1120 | } | ||
1121 | |||
1122 | if (value != NULL && type > TK_M_MAX) { | ||
1123 | /* check if assigned value has substitution chars */ | ||
1124 | if (value[0] == '[') | ||
1125 | token->key.subst = SB_SUBSYS; | ||
1126 | else if (strchr(value, '%') != NULL || strchr(value, '$') != NULL) | ||
1127 | token->key.subst = SB_FORMAT; | ||
1128 | else | ||
1129 | token->key.subst = SB_NONE; | ||
1130 | } | ||
1131 | |||
1132 | if (attr != NULL) { | ||
1133 | /* check if property/attribut name has substitution chars */ | ||
1134 | if (attr[0] == '[') | ||
1135 | token->key.attrsubst = SB_SUBSYS; | ||
1136 | else if (strchr(attr, '%') != NULL || strchr(attr, '$') != NULL) | ||
1137 | token->key.attrsubst = SB_FORMAT; | ||
1138 | else | ||
1139 | token->key.attrsubst = SB_NONE; | ||
1140 | } | ||
1141 | |||
1142 | token->key.type = type; | ||
1143 | token->key.op = op; | ||
1144 | rule_tmp->token_cur++; | ||
1145 | if (rule_tmp->token_cur >= ARRAY_SIZE(rule_tmp->token)) { | ||
1146 | err(rule_tmp->rules->udev, "temporary rule array too small\n"); | ||
1147 | return -1; | ||
1148 | } | ||
1149 | return 0; | ||
1150 | } | ||
1151 | |||
1152 | static int sort_token(struct udev_rules *rules, struct rule_tmp *rule_tmp) | ||
1153 | { | ||
1154 | unsigned int i; | ||
1155 | unsigned int start = 0; | ||
1156 | unsigned int end = rule_tmp->token_cur; | ||
1157 | |||
1158 | for (i = 0; i < rule_tmp->token_cur; i++) { | ||
1159 | enum token_type next_val = TK_UNSET; | ||
1160 | unsigned int next_idx = 0; | ||
1161 | unsigned int j; | ||
1162 | |||
1163 | /* find smallest value */ | ||
1164 | for (j = start; j < end; j++) { | ||
1165 | if (rule_tmp->token[j].type == TK_UNSET) | ||
1166 | continue; | ||
1167 | if (next_val == TK_UNSET || rule_tmp->token[j].type < next_val) { | ||
1168 | next_val = rule_tmp->token[j].type; | ||
1169 | next_idx = j; | ||
1170 | } | ||
1171 | } | ||
1172 | |||
1173 | /* add token and mark done */ | ||
1174 | if (add_token(rules, &rule_tmp->token[next_idx]) != 0) | ||
1175 | return -1; | ||
1176 | rule_tmp->token[next_idx].type = TK_UNSET; | ||
1177 | |||
1178 | /* shrink range */ | ||
1179 | if (next_idx == start) | ||
1180 | start++; | ||
1181 | if (next_idx+1 == end) | ||
1182 | end--; | ||
1183 | } | ||
1184 | return 0; | ||
1185 | } | ||
1186 | |||
1187 | static int add_rule(struct udev_rules *rules, char *line, | ||
1188 | const char *filename, unsigned int filename_off, unsigned int lineno) | ||
1189 | { | ||
1190 | char *linepos; | ||
1191 | char *attr; | ||
1192 | struct rule_tmp rule_tmp; | ||
1193 | |||
1194 | memset(&rule_tmp, 0x00, sizeof(struct rule_tmp)); | ||
1195 | rule_tmp.rules = rules; | ||
1196 | rule_tmp.rule.type = TK_RULE; | ||
1197 | rule_tmp.rule.rule.filename_off = filename_off; | ||
1198 | rule_tmp.rule.rule.filename_line = lineno; | ||
1199 | |||
1200 | linepos = line; | ||
1201 | for (;;) { | ||
1202 | char *key; | ||
1203 | char *value; | ||
1204 | enum operation_type op; | ||
1205 | |||
1206 | if (get_key(rules->udev, &linepos, &key, &op, &value) != 0) | ||
1207 | break; | ||
1208 | |||
1209 | if (strcmp(key, "ACTION") == 0) { | ||
1210 | if (op > OP_MATCH_MAX) { | ||
1211 | err(rules->udev, "invalid ACTION operation\n"); | ||
1212 | goto invalid; | ||
1213 | } | ||
1214 | rule_add_key(&rule_tmp, TK_M_ACTION, op, value, NULL); | ||
1215 | continue; | ||
1216 | } | ||
1217 | |||
1218 | if (strcmp(key, "DEVPATH") == 0) { | ||
1219 | if (op > OP_MATCH_MAX) { | ||
1220 | err(rules->udev, "invalid DEVPATH operation\n"); | ||
1221 | goto invalid; | ||
1222 | } | ||
1223 | rule_add_key(&rule_tmp, TK_M_DEVPATH, op, value, NULL); | ||
1224 | continue; | ||
1225 | } | ||
1226 | |||
1227 | if (strcmp(key, "KERNEL") == 0) { | ||
1228 | if (op > OP_MATCH_MAX) { | ||
1229 | err(rules->udev, "invalid KERNEL operation\n"); | ||
1230 | goto invalid; | ||
1231 | } | ||
1232 | rule_add_key(&rule_tmp, TK_M_KERNEL, op, value, NULL); | ||
1233 | continue; | ||
1234 | } | ||
1235 | |||
1236 | if (strcmp(key, "SUBSYSTEM") == 0) { | ||
1237 | if (op > OP_MATCH_MAX) { | ||
1238 | err(rules->udev, "invalid SUBSYSTEM operation\n"); | ||
1239 | goto invalid; | ||
1240 | } | ||
1241 | /* bus, class, subsystem events should all be the same */ | ||
1242 | if (strcmp(value, "subsystem") == 0 || | ||
1243 | strcmp(value, "bus") == 0 || | ||
1244 | strcmp(value, "class") == 0) { | ||
1245 | if (strcmp(value, "bus") == 0 || strcmp(value, "class") == 0) | ||
1246 | err(rules->udev, "'%s' must be specified as 'subsystem' \n" | ||
1247 | "please fix it in %s:%u", value, filename, lineno); | ||
1248 | rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, "subsystem|class|bus", NULL); | ||
1249 | } else | ||
1250 | rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, value, NULL); | ||
1251 | continue; | ||
1252 | } | ||
1253 | |||
1254 | if (strcmp(key, "DRIVER") == 0) { | ||
1255 | if (op > OP_MATCH_MAX) { | ||
1256 | err(rules->udev, "invalid DRIVER operation\n"); | ||
1257 | goto invalid; | ||
1258 | } | ||
1259 | rule_add_key(&rule_tmp, TK_M_DRIVER, op, value, NULL); | ||
1260 | continue; | ||
1261 | } | ||
1262 | |||
1263 | if (strncmp(key, "ATTR{", sizeof("ATTR{")-1) == 0) { | ||
1264 | attr = get_key_attribute(rules->udev, key + sizeof("ATTR")-1); | ||
1265 | if (attr == NULL) { | ||
1266 | err(rules->udev, "error parsing ATTR attribute\n"); | ||
1267 | goto invalid; | ||
1268 | } | ||
1269 | if (op < OP_MATCH_MAX) { | ||
1270 | rule_add_key(&rule_tmp, TK_M_ATTR, op, value, attr); | ||
1271 | } else { | ||
1272 | rule_add_key(&rule_tmp, TK_A_ATTR, op, value, attr); | ||
1273 | } | ||
1274 | continue; | ||
1275 | } | ||
1276 | |||
1277 | if (strcmp(key, "KERNELS") == 0) { | ||
1278 | if (op > OP_MATCH_MAX) { | ||
1279 | err(rules->udev, "invalid KERNELS operation\n"); | ||
1280 | goto invalid; | ||
1281 | } | ||
1282 | rule_add_key(&rule_tmp, TK_M_KERNELS, op, value, NULL); | ||
1283 | continue; | ||
1284 | } | ||
1285 | |||
1286 | if (strcmp(key, "SUBSYSTEMS") == 0) { | ||
1287 | if (op > OP_MATCH_MAX) { | ||
1288 | err(rules->udev, "invalid SUBSYSTEMS operation\n"); | ||
1289 | goto invalid; | ||
1290 | } | ||
1291 | rule_add_key(&rule_tmp, TK_M_SUBSYSTEMS, op, value, NULL); | ||
1292 | continue; | ||
1293 | } | ||
1294 | |||
1295 | if (strcmp(key, "DRIVERS") == 0) { | ||
1296 | if (op > OP_MATCH_MAX) { | ||
1297 | err(rules->udev, "invalid DRIVERS operation\n"); | ||
1298 | goto invalid; | ||
1299 | } | ||
1300 | rule_add_key(&rule_tmp, TK_M_DRIVERS, op, value, NULL); | ||
1301 | continue; | ||
1302 | } | ||
1303 | |||
1304 | if (strncmp(key, "ATTRS{", sizeof("ATTRS{")-1) == 0) { | ||
1305 | if (op > OP_MATCH_MAX) { | ||
1306 | err(rules->udev, "invalid ATTRS operation\n"); | ||
1307 | goto invalid; | ||
1308 | } | ||
1309 | attr = get_key_attribute(rules->udev, key + sizeof("ATTRS")-1); | ||
1310 | if (attr == NULL) { | ||
1311 | err(rules->udev, "error parsing ATTRS attribute\n"); | ||
1312 | goto invalid; | ||
1313 | } | ||
1314 | if (strncmp(attr, "device/", 7) == 0) | ||
1315 | err(rules->udev, "the 'device' link may not be available in a future kernel, " | ||
1316 | "please fix it in %s:%u", filename, lineno); | ||
1317 | else if (strstr(attr, "../") != NULL) | ||
1318 | err(rules->udev, "do not reference parent sysfs directories directly, " | ||
1319 | "it may break with a future kernel, please fix it in %s:%u", filename, lineno); | ||
1320 | rule_add_key(&rule_tmp, TK_M_ATTRS, op, value, attr); | ||
1321 | continue; | ||
1322 | } | ||
1323 | |||
1324 | if (strcmp(key, "TAGS") == 0) { | ||
1325 | if (op > OP_MATCH_MAX) { | ||
1326 | err(rules->udev, "invalid TAGS operation\n"); | ||
1327 | goto invalid; | ||
1328 | } | ||
1329 | rule_add_key(&rule_tmp, TK_M_TAGS, op, value, NULL); | ||
1330 | continue; | ||
1331 | } | ||
1332 | |||
1333 | if (strncmp(key, "ENV{", sizeof("ENV{")-1) == 0) { | ||
1334 | attr = get_key_attribute(rules->udev, key + sizeof("ENV")-1); | ||
1335 | if (attr == NULL) { | ||
1336 | err(rules->udev, "error parsing ENV attribute\n"); | ||
1337 | goto invalid; | ||
1338 | } | ||
1339 | if (op < OP_MATCH_MAX) { | ||
1340 | if (rule_add_key(&rule_tmp, TK_M_ENV, op, value, attr) != 0) | ||
1341 | goto invalid; | ||
1342 | } else { | ||
1343 | static const char *blacklist[] = { | ||
1344 | "ACTION", | ||
1345 | "SUBSYSTEM", | ||
1346 | "DEVTYPE", | ||
1347 | "MAJOR", | ||
1348 | "MINOR", | ||
1349 | "DRIVER", | ||
1350 | "IFINDEX", | ||
1351 | "DEVNAME", | ||
1352 | "DEVLINKS", | ||
1353 | "DEVPATH", | ||
1354 | "TAGS", | ||
1355 | }; | ||
1356 | unsigned int i; | ||
1357 | |||
1358 | for (i = 0; i < ARRAY_SIZE(blacklist); i++) | ||
1359 | if (strcmp(attr, blacklist[i]) == 0) { | ||
1360 | err(rules->udev, "invalid ENV attribute, '%s' can not be set %s:%u\n", attr, filename, lineno); | ||
1361 | continue; | ||
1362 | } | ||
1363 | if (rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr) != 0) | ||
1364 | goto invalid; | ||
1365 | } | ||
1366 | continue; | ||
1367 | } | ||
1368 | |||
1369 | if (strcmp(key, "TAG") == 0) { | ||
1370 | if (op < OP_MATCH_MAX) | ||
1371 | rule_add_key(&rule_tmp, TK_M_TAG, op, value, NULL); | ||
1372 | else | ||
1373 | rule_add_key(&rule_tmp, TK_A_TAG, op, value, NULL); | ||
1374 | continue; | ||
1375 | } | ||
1376 | |||
1377 | if (strcmp(key, "PROGRAM") == 0) { | ||
1378 | rule_add_key(&rule_tmp, TK_M_PROGRAM, op, value, NULL); | ||
1379 | continue; | ||
1380 | } | ||
1381 | |||
1382 | if (strcmp(key, "RESULT") == 0) { | ||
1383 | if (op > OP_MATCH_MAX) { | ||
1384 | err(rules->udev, "invalid RESULT operation\n"); | ||
1385 | goto invalid; | ||
1386 | } | ||
1387 | rule_add_key(&rule_tmp, TK_M_RESULT, op, value, NULL); | ||
1388 | continue; | ||
1389 | } | ||
1390 | |||
1391 | if (strncmp(key, "IMPORT", sizeof("IMPORT")-1) == 0) { | ||
1392 | attr = get_key_attribute(rules->udev, key + sizeof("IMPORT")-1); | ||
1393 | if (attr == NULL) { | ||
1394 | err(rules->udev, "IMPORT{} type missing, ignoring IMPORT %s:%u\n", filename, lineno); | ||
1395 | continue; | ||
1396 | } | ||
1397 | if (strstr(attr, "program")) { | ||
1398 | /* find known built-in command */ | ||
1399 | if (value[0] != '/') { | ||
1400 | enum udev_builtin_cmd cmd; | ||
1401 | |||
1402 | cmd = udev_builtin_lookup(value); | ||
1403 | if (cmd < UDEV_BUILTIN_MAX) { | ||
1404 | info(rules->udev, "IMPORT found builtin '%s', replacing %s:%u\n", | ||
1405 | value, filename, lineno); | ||
1406 | rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd); | ||
1407 | continue; | ||
1408 | } | ||
1409 | } | ||
1410 | dbg(rules->udev, "IMPORT will be executed\n"); | ||
1411 | rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL); | ||
1412 | } else if (strstr(attr, "builtin")) { | ||
1413 | enum udev_builtin_cmd cmd = udev_builtin_lookup(value); | ||
1414 | |||
1415 | dbg(rules->udev, "IMPORT execute builtin\n"); | ||
1416 | if (cmd < UDEV_BUILTIN_MAX) | ||
1417 | rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd); | ||
1418 | else | ||
1419 | err(rules->udev, "IMPORT{builtin}: '%s' unknown %s:%u\n", value, filename, lineno); | ||
1420 | } else if (strstr(attr, "file")) { | ||
1421 | dbg(rules->udev, "IMPORT will be included as file\n"); | ||
1422 | rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL); | ||
1423 | } else if (strstr(attr, "db")) { | ||
1424 | dbg(rules->udev, "IMPORT will include db values\n"); | ||
1425 | rule_add_key(&rule_tmp, TK_M_IMPORT_DB, op, value, NULL); | ||
1426 | } else if (strstr(attr, "cmdline")) { | ||
1427 | dbg(rules->udev, "IMPORT will include db values\n"); | ||
1428 | rule_add_key(&rule_tmp, TK_M_IMPORT_CMDLINE, op, value, NULL); | ||
1429 | } else if (strstr(attr, "parent")) { | ||
1430 | dbg(rules->udev, "IMPORT will include the parent values\n"); | ||
1431 | rule_add_key(&rule_tmp, TK_M_IMPORT_PARENT, op, value, NULL); | ||
1432 | } | ||
1433 | continue; | ||
1434 | } | ||
1435 | |||
1436 | if (strncmp(key, "TEST", sizeof("TEST")-1) == 0) { | ||
1437 | mode_t mode = 0; | ||
1438 | |||
1439 | if (op > OP_MATCH_MAX) { | ||
1440 | err(rules->udev, "invalid TEST operation\n"); | ||
1441 | goto invalid; | ||
1442 | } | ||
1443 | attr = get_key_attribute(rules->udev, key + sizeof("TEST")-1); | ||
1444 | if (attr != NULL) { | ||
1445 | mode = strtol(attr, NULL, 8); | ||
1446 | rule_add_key(&rule_tmp, TK_M_TEST, op, value, &mode); | ||
1447 | } else { | ||
1448 | rule_add_key(&rule_tmp, TK_M_TEST, op, value, NULL); | ||
1449 | } | ||
1450 | continue; | ||
1451 | } | ||
1452 | |||
1453 | if (strcmp(key, "RUN") == 0) { | ||
1454 | rule_add_key(&rule_tmp, TK_A_RUN, op, value, NULL); | ||
1455 | continue; | ||
1456 | } | ||
1457 | |||
1458 | if (strcmp(key, "WAIT_FOR") == 0 || strcmp(key, "WAIT_FOR_SYSFS") == 0) { | ||
1459 | rule_add_key(&rule_tmp, TK_M_WAITFOR, 0, value, NULL); | ||
1460 | continue; | ||
1461 | } | ||
1462 | |||
1463 | if (strcmp(key, "LABEL") == 0) { | ||
1464 | rule_tmp.rule.rule.label_off = add_string(rules, value); | ||
1465 | continue; | ||
1466 | } | ||
1467 | |||
1468 | if (strcmp(key, "GOTO") == 0) { | ||
1469 | rule_add_key(&rule_tmp, TK_A_GOTO, 0, value, NULL); | ||
1470 | continue; | ||
1471 | } | ||
1472 | |||
1473 | if (strncmp(key, "NAME", sizeof("NAME")-1) == 0) { | ||
1474 | if (op < OP_MATCH_MAX) { | ||
1475 | rule_add_key(&rule_tmp, TK_M_NAME, op, value, NULL); | ||
1476 | } else { | ||
1477 | if (strcmp(value, "%k") == 0) { | ||
1478 | err(rules->udev, "NAME=\"%%k\" is ignored, because it breaks kernel supplied names, " | ||
1479 | "please remove it from %s:%u\n", filename, lineno); | ||
1480 | continue; | ||
1481 | } | ||
1482 | if (value[0] == '\0') { | ||
1483 | info(rules->udev, "NAME=\"\" is ignored, because udev will not delete any device nodes, " | ||
1484 | "please remove it from %s:%u\n", filename, lineno); | ||
1485 | continue; | ||
1486 | } | ||
1487 | rule_add_key(&rule_tmp, TK_A_NAME, op, value, NULL); | ||
1488 | } | ||
1489 | rule_tmp.rule.rule.can_set_name = true; | ||
1490 | continue; | ||
1491 | } | ||
1492 | |||
1493 | if (strncmp(key, "SYMLINK", sizeof("SYMLINK")-1) == 0) { | ||
1494 | if (op < OP_MATCH_MAX) { | ||
1495 | rule_add_key(&rule_tmp, TK_M_DEVLINK, op, value, NULL); | ||
1496 | } else { | ||
1497 | int flag = 0; | ||
1498 | |||
1499 | attr = get_key_attribute(rules->udev, key + sizeof("SYMLINK")-1); | ||
1500 | if (attr != NULL && strstr(attr, "unique") != NULL) | ||
1501 | flag = 1; | ||
1502 | rule_add_key(&rule_tmp, TK_A_DEVLINK, op, value, &flag); | ||
1503 | } | ||
1504 | rule_tmp.rule.rule.can_set_name = true; | ||
1505 | continue; | ||
1506 | } | ||
1507 | |||
1508 | if (strcmp(key, "OWNER") == 0) { | ||
1509 | uid_t uid; | ||
1510 | char *endptr; | ||
1511 | |||
1512 | uid = strtoul(value, &endptr, 10); | ||
1513 | if (endptr[0] == '\0') { | ||
1514 | rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); | ||
1515 | } else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) { | ||
1516 | uid = add_uid(rules, value); | ||
1517 | rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); | ||
1518 | } else if (rules->resolve_names >= 0) { | ||
1519 | rule_add_key(&rule_tmp, TK_A_OWNER, op, value, NULL); | ||
1520 | } | ||
1521 | rule_tmp.rule.rule.can_set_name = true; | ||
1522 | continue; | ||
1523 | } | ||
1524 | |||
1525 | if (strcmp(key, "GROUP") == 0) { | ||
1526 | gid_t gid; | ||
1527 | char *endptr; | ||
1528 | |||
1529 | gid = strtoul(value, &endptr, 10); | ||
1530 | if (endptr[0] == '\0') { | ||
1531 | rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); | ||
1532 | } else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) { | ||
1533 | gid = add_gid(rules, value); | ||
1534 | rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); | ||
1535 | } else if (rules->resolve_names >= 0) { | ||
1536 | rule_add_key(&rule_tmp, TK_A_GROUP, op, value, NULL); | ||
1537 | } | ||
1538 | rule_tmp.rule.rule.can_set_name = true; | ||
1539 | continue; | ||
1540 | } | ||
1541 | |||
1542 | if (strcmp(key, "MODE") == 0) { | ||
1543 | mode_t mode; | ||
1544 | char *endptr; | ||
1545 | |||
1546 | mode = strtol(value, &endptr, 8); | ||
1547 | if (endptr[0] == '\0') | ||
1548 | rule_add_key(&rule_tmp, TK_A_MODE_ID, op, NULL, &mode); | ||
1549 | else | ||
1550 | rule_add_key(&rule_tmp, TK_A_MODE, op, value, NULL); | ||
1551 | rule_tmp.rule.rule.can_set_name = true; | ||
1552 | continue; | ||
1553 | } | ||
1554 | |||
1555 | if (strcmp(key, "OPTIONS") == 0) { | ||
1556 | const char *pos; | ||
1557 | |||
1558 | pos = strstr(value, "link_priority="); | ||
1559 | if (pos != NULL) { | ||
1560 | int prio = atoi(&pos[strlen("link_priority=")]); | ||
1561 | |||
1562 | rule_add_key(&rule_tmp, TK_A_DEVLINK_PRIO, op, NULL, &prio); | ||
1563 | dbg(rules->udev, "link priority=%i\n", prio); | ||
1564 | } | ||
1565 | |||
1566 | pos = strstr(value, "event_timeout="); | ||
1567 | if (pos != NULL) { | ||
1568 | int tout = atoi(&pos[strlen("event_timeout=")]); | ||
1569 | |||
1570 | rule_add_key(&rule_tmp, TK_M_EVENT_TIMEOUT, op, NULL, &tout); | ||
1571 | dbg(rules->udev, "event timeout=%i\n", tout); | ||
1572 | } | ||
1573 | |||
1574 | pos = strstr(value, "string_escape="); | ||
1575 | if (pos != NULL) { | ||
1576 | pos = &pos[strlen("string_escape=")]; | ||
1577 | if (strncmp(pos, "none", strlen("none")) == 0) | ||
1578 | rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_NONE, op, NULL, NULL); | ||
1579 | else if (strncmp(pos, "replace", strlen("replace")) == 0) | ||
1580 | rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_REPLACE, op, NULL, NULL); | ||
1581 | } | ||
1582 | |||
1583 | pos = strstr(value, "db_persist"); | ||
1584 | if (pos != NULL) | ||
1585 | rule_add_key(&rule_tmp, TK_A_DB_PERSIST, op, NULL, NULL); | ||
1586 | |||
1587 | pos = strstr(value, "nowatch"); | ||
1588 | if (pos != NULL) { | ||
1589 | const int off = 0; | ||
1590 | |||
1591 | rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &off); | ||
1592 | dbg(rules->udev, "inotify watch of device disabled\n"); | ||
1593 | } else { | ||
1594 | pos = strstr(value, "watch"); | ||
1595 | if (pos != NULL) { | ||
1596 | const int on = 1; | ||
1597 | |||
1598 | rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &on); | ||
1599 | dbg(rules->udev, "inotify watch of device requested\n"); | ||
1600 | } | ||
1601 | } | ||
1602 | |||
1603 | pos = strstr(value, "static_node="); | ||
1604 | if (pos != NULL) { | ||
1605 | rule_add_key(&rule_tmp, TK_A_STATIC_NODE, op, &pos[strlen("static_node=")], NULL); | ||
1606 | rule_tmp.rule.rule.has_static_node = true; | ||
1607 | } | ||
1608 | |||
1609 | continue; | ||
1610 | } | ||
1611 | |||
1612 | err(rules->udev, "unknown key '%s' in %s:%u\n", key, filename, lineno); | ||
1613 | goto invalid; | ||
1614 | } | ||
1615 | |||
1616 | /* add rule token */ | ||
1617 | rule_tmp.rule.rule.token_count = 1 + rule_tmp.token_cur; | ||
1618 | if (add_token(rules, &rule_tmp.rule) != 0) | ||
1619 | goto invalid; | ||
1620 | |||
1621 | /* add tokens to list, sorted by type */ | ||
1622 | if (sort_token(rules, &rule_tmp) != 0) | ||
1623 | goto invalid; | ||
1624 | |||
1625 | return 0; | ||
1626 | invalid: | ||
1627 | err(rules->udev, "invalid rule '%s:%u'\n", filename, lineno); | ||
1628 | return -1; | ||
1629 | } | ||
1630 | |||
1631 | static int parse_file(struct udev_rules *rules, const char *filename, unsigned short filename_off) | ||
1632 | { | ||
1633 | FILE *f; | ||
1634 | unsigned int first_token; | ||
1635 | char line[UTIL_LINE_SIZE]; | ||
1636 | int line_nr = 0; | ||
1637 | unsigned int i; | ||
1638 | |||
1639 | info(rules->udev, "reading '%s' as rules file\n", filename); | ||
1640 | |||
1641 | f = fopen(filename, "r"); | ||
1642 | if (f == NULL) | ||
1643 | return -1; | ||
1644 | |||
1645 | first_token = rules->token_cur; | ||
1646 | |||
1647 | while (fgets(line, sizeof(line), f) != NULL) { | ||
1648 | char *key; | ||
1649 | size_t len; | ||
1650 | |||
1651 | /* skip whitespace */ | ||
1652 | line_nr++; | ||
1653 | key = line; | ||
1654 | while (isspace(key[0])) | ||
1655 | key++; | ||
1656 | |||
1657 | /* comment */ | ||
1658 | if (key[0] == '#') | ||
1659 | continue; | ||
1660 | |||
1661 | len = strlen(line); | ||
1662 | if (len < 3) | ||
1663 | continue; | ||
1664 | |||
1665 | /* continue reading if backslash+newline is found */ | ||
1666 | while (line[len-2] == '\\') { | ||
1667 | if (fgets(&line[len-2], (sizeof(line)-len)+2, f) == NULL) | ||
1668 | break; | ||
1669 | if (strlen(&line[len-2]) < 2) | ||
1670 | break; | ||
1671 | line_nr++; | ||
1672 | len = strlen(line); | ||
1673 | } | ||
1674 | |||
1675 | if (len+1 >= sizeof(line)) { | ||
1676 | err(rules->udev, "line too long '%s':%u, ignored\n", filename, line_nr); | ||
1677 | continue; | ||
1678 | } | ||
1679 | add_rule(rules, key, filename, filename_off, line_nr); | ||
1680 | } | ||
1681 | fclose(f); | ||
1682 | |||
1683 | /* link GOTOs to LABEL rules in this file to be able to fast-forward */ | ||
1684 | for (i = first_token+1; i < rules->token_cur; i++) { | ||
1685 | if (rules->tokens[i].type == TK_A_GOTO) { | ||
1686 | char *label = &rules->buf[rules->tokens[i].key.value_off]; | ||
1687 | unsigned int j; | ||
1688 | |||
1689 | for (j = i+1; j < rules->token_cur; j++) { | ||
1690 | if (rules->tokens[j].type != TK_RULE) | ||
1691 | continue; | ||
1692 | if (rules->tokens[j].rule.label_off == 0) | ||
1693 | continue; | ||
1694 | if (strcmp(label, &rules->buf[rules->tokens[j].rule.label_off]) != 0) | ||
1695 | continue; | ||
1696 | rules->tokens[i].key.rule_goto = j; | ||
1697 | break; | ||
1698 | } | ||
1699 | if (rules->tokens[i].key.rule_goto == 0) | ||
1700 | err(rules->udev, "GOTO '%s' has no matching label in: '%s'\n", label, filename); | ||
1701 | } | ||
1702 | } | ||
1703 | return 0; | ||
1704 | } | ||
1705 | |||
1706 | static int add_matching_files(struct udev *udev, struct udev_list *file_list, const char *dirname, const char *suffix) | ||
1707 | { | ||
1708 | DIR *dir; | ||
1709 | struct dirent *dent; | ||
1710 | char filename[UTIL_PATH_SIZE]; | ||
1711 | |||
1712 | dbg(udev, "open directory '%s'\n", dirname); | ||
1713 | dir = opendir(dirname); | ||
1714 | if (dir == NULL) { | ||
1715 | info(udev, "unable to open '%s': %m\n", dirname); | ||
1716 | return -1; | ||
1717 | } | ||
1718 | |||
1719 | for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { | ||
1720 | if (dent->d_name[0] == '.') | ||
1721 | continue; | ||
1722 | |||
1723 | /* look for file matching with specified suffix */ | ||
1724 | if (suffix != NULL) { | ||
1725 | const char *ext; | ||
1726 | |||
1727 | ext = strrchr(dent->d_name, '.'); | ||
1728 | if (ext == NULL) | ||
1729 | continue; | ||
1730 | if (strcmp(ext, suffix) != 0) | ||
1731 | continue; | ||
1732 | } | ||
1733 | util_strscpyl(filename, sizeof(filename), dirname, "/", dent->d_name, NULL); | ||
1734 | dbg(udev, "put file '%s' into list\n", filename); | ||
1735 | /* | ||
1736 | * the basename is the key, the filename the value | ||
1737 | * identical basenames from different directories override each other | ||
1738 | * entries are sorted after basename | ||
1739 | */ | ||
1740 | udev_list_entry_add(file_list, dent->d_name, filename); | ||
1741 | } | ||
1742 | |||
1743 | closedir(dir); | ||
1744 | return 0; | ||
1745 | } | ||
1746 | |||
1747 | struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) | ||
1748 | { | ||
1749 | struct udev_rules *rules; | ||
1750 | struct udev_list file_list; | ||
1751 | struct udev_list_entry *file_loop; | ||
1752 | struct token end_token; | ||
1753 | char **s; | ||
1754 | |||
1755 | rules = calloc(1, sizeof(struct udev_rules)); | ||
1756 | if (rules == NULL) | ||
1757 | return NULL; | ||
1758 | rules->udev = udev; | ||
1759 | rules->resolve_names = resolve_names; | ||
1760 | udev_list_init(udev, &file_list, true); | ||
1761 | |||
1762 | /* init token array and string buffer */ | ||
1763 | rules->tokens = malloc(PREALLOC_TOKEN * sizeof(struct token)); | ||
1764 | if (rules->tokens == NULL) { | ||
1765 | free(rules); | ||
1766 | return NULL; | ||
1767 | } | ||
1768 | rules->token_max = PREALLOC_TOKEN; | ||
1769 | |||
1770 | rules->buf = malloc(PREALLOC_STRBUF); | ||
1771 | if (rules->buf == NULL) { | ||
1772 | free(rules->tokens); | ||
1773 | free(rules); | ||
1774 | return NULL; | ||
1775 | } | ||
1776 | rules->buf_max = PREALLOC_STRBUF; | ||
1777 | /* offset 0 is always '\0' */ | ||
1778 | rules->buf[0] = '\0'; | ||
1779 | rules->buf_cur = 1; | ||
1780 | dbg(udev, "prealloc %zu bytes tokens (%u * %zu bytes), %zu bytes buffer\n", | ||
1781 | rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->buf_max); | ||
1782 | |||
1783 | rules->trie_nodes = malloc(PREALLOC_TRIE * sizeof(struct trie_node)); | ||
1784 | if (rules->trie_nodes == NULL) { | ||
1785 | free(rules->buf); | ||
1786 | free(rules->tokens); | ||
1787 | free(rules); | ||
1788 | return NULL; | ||
1789 | } | ||
1790 | rules->trie_nodes_max = PREALLOC_TRIE; | ||
1791 | /* offset 0 is the trie root, with an empty string */ | ||
1792 | memset(rules->trie_nodes, 0x00, sizeof(struct trie_node)); | ||
1793 | rules->trie_nodes_cur = 1; | ||
1794 | |||
1795 | for (udev_get_rules_path(udev, &s, NULL); *s != NULL; s++) | ||
1796 | add_matching_files(udev, &file_list, *s, ".rules"); | ||
1797 | |||
1798 | /* add all filenames to the string buffer */ | ||
1799 | udev_list_entry_foreach(file_loop, udev_list_get_entry(&file_list)) { | ||
1800 | const char *filename = udev_list_entry_get_value(file_loop); | ||
1801 | unsigned int filename_off; | ||
1802 | |||
1803 | filename_off = add_string(rules, filename); | ||
1804 | /* the offset in the rule is limited to unsigned short */ | ||
1805 | if (filename_off < USHRT_MAX) | ||
1806 | udev_list_entry_set_num(file_loop, filename_off); | ||
1807 | } | ||
1808 | |||
1809 | /* parse all rules files */ | ||
1810 | udev_list_entry_foreach(file_loop, udev_list_get_entry(&file_list)) { | ||
1811 | const char *filename = udev_list_entry_get_value(file_loop); | ||
1812 | unsigned int filename_off = udev_list_entry_get_num(file_loop); | ||
1813 | struct stat st; | ||
1814 | |||
1815 | if (stat(filename, &st) != 0) { | ||
1816 | err(udev, "can not find '%s': %m\n", filename); | ||
1817 | continue; | ||
1818 | } | ||
1819 | if (S_ISREG(st.st_mode) && st.st_size <= 0) { | ||
1820 | info(udev, "ignore empty '%s'\n", filename); | ||
1821 | continue; | ||
1822 | } | ||
1823 | if (S_ISCHR(st.st_mode)) { | ||
1824 | info(udev, "ignore masked '%s'\n", filename); | ||
1825 | continue; | ||
1826 | } | ||
1827 | parse_file(rules, filename, filename_off); | ||
1828 | } | ||
1829 | udev_list_cleanup(&file_list); | ||
1830 | |||
1831 | memset(&end_token, 0x00, sizeof(struct token)); | ||
1832 | end_token.type = TK_END; | ||
1833 | add_token(rules, &end_token); | ||
1834 | |||
1835 | /* shrink allocated token and string buffer */ | ||
1836 | if (rules->token_cur < rules->token_max) { | ||
1837 | struct token *tokens; | ||
1838 | |||
1839 | tokens = realloc(rules->tokens, rules->token_cur * sizeof(struct token)); | ||
1840 | if (tokens != NULL || rules->token_cur == 0) { | ||
1841 | rules->tokens = tokens; | ||
1842 | rules->token_max = rules->token_cur; | ||
1843 | } | ||
1844 | } | ||
1845 | if (rules->buf_cur < rules->buf_max) { | ||
1846 | char *buf; | ||
1847 | |||
1848 | buf = realloc(rules->buf, rules->buf_cur); | ||
1849 | if (buf != NULL || rules->buf_cur == 0) { | ||
1850 | rules->buf = buf; | ||
1851 | rules->buf_max = rules->buf_cur; | ||
1852 | } | ||
1853 | } | ||
1854 | info(udev, "rules use %zu bytes tokens (%u * %zu bytes), %zu bytes buffer\n", | ||
1855 | rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->buf_max); | ||
1856 | info(udev, "temporary index used %zu bytes (%u * %zu bytes)\n", | ||
1857 | rules->trie_nodes_cur * sizeof(struct trie_node), | ||
1858 | rules->trie_nodes_cur, sizeof(struct trie_node)); | ||
1859 | |||
1860 | /* cleanup trie */ | ||
1861 | free(rules->trie_nodes); | ||
1862 | rules->trie_nodes = NULL; | ||
1863 | rules->trie_nodes_cur = 0; | ||
1864 | rules->trie_nodes_max = 0; | ||
1865 | |||
1866 | /* cleanup uid/gid cache */ | ||
1867 | free(rules->uids); | ||
1868 | rules->uids = NULL; | ||
1869 | rules->uids_cur = 0; | ||
1870 | rules->uids_max = 0; | ||
1871 | free(rules->gids); | ||
1872 | rules->gids = NULL; | ||
1873 | rules->gids_cur = 0; | ||
1874 | rules->gids_max = 0; | ||
1875 | |||
1876 | dump_rules(rules); | ||
1877 | return rules; | ||
1878 | } | ||
1879 | |||
1880 | struct udev_rules *udev_rules_unref(struct udev_rules *rules) | ||
1881 | { | ||
1882 | if (rules == NULL) | ||
1883 | return NULL; | ||
1884 | free(rules->tokens); | ||
1885 | free(rules->buf); | ||
1886 | free(rules->trie_nodes); | ||
1887 | free(rules->uids); | ||
1888 | free(rules->gids); | ||
1889 | free(rules); | ||
1890 | return NULL; | ||
1891 | } | ||
1892 | |||
1893 | static int match_key(struct udev_rules *rules, struct token *token, const char *val) | ||
1894 | { | ||
1895 | char *key_value = &rules->buf[token->key.value_off]; | ||
1896 | char *pos; | ||
1897 | bool match = false; | ||
1898 | |||
1899 | if (val == NULL) | ||
1900 | val = ""; | ||
1901 | |||
1902 | switch (token->key.glob) { | ||
1903 | case GL_PLAIN: | ||
1904 | match = (strcmp(key_value, val) == 0); | ||
1905 | break; | ||
1906 | case GL_GLOB: | ||
1907 | match = (fnmatch(key_value, val, 0) == 0); | ||
1908 | break; | ||
1909 | case GL_SPLIT: | ||
1910 | { | ||
1911 | const char *split; | ||
1912 | size_t len; | ||
1913 | |||
1914 | split = &rules->buf[token->key.value_off]; | ||
1915 | len = strlen(val); | ||
1916 | for (;;) { | ||
1917 | const char *next; | ||
1918 | |||
1919 | next = strchr(split, '|'); | ||
1920 | if (next != NULL) { | ||
1921 | size_t matchlen = (size_t)(next - split); | ||
1922 | |||
1923 | match = (matchlen == len && strncmp(split, val, matchlen) == 0); | ||
1924 | if (match) | ||
1925 | break; | ||
1926 | } else { | ||
1927 | match = (strcmp(split, val) == 0); | ||
1928 | break; | ||
1929 | } | ||
1930 | split = &next[1]; | ||
1931 | } | ||
1932 | break; | ||
1933 | } | ||
1934 | case GL_SPLIT_GLOB: | ||
1935 | { | ||
1936 | char value[UTIL_PATH_SIZE]; | ||
1937 | |||
1938 | util_strscpy(value, sizeof(value), &rules->buf[token->key.value_off]); | ||
1939 | key_value = value; | ||
1940 | while (key_value != NULL) { | ||
1941 | pos = strchr(key_value, '|'); | ||
1942 | if (pos != NULL) { | ||
1943 | pos[0] = '\0'; | ||
1944 | pos = &pos[1]; | ||
1945 | } | ||
1946 | dbg(rules->udev, "match %s '%s' <-> '%s'\n", token_str(token->type), key_value, val); | ||
1947 | match = (fnmatch(key_value, val, 0) == 0); | ||
1948 | if (match) | ||
1949 | break; | ||
1950 | key_value = pos; | ||
1951 | } | ||
1952 | break; | ||
1953 | } | ||
1954 | case GL_SOMETHING: | ||
1955 | match = (val[0] != '\0'); | ||
1956 | break; | ||
1957 | case GL_UNSET: | ||
1958 | return -1; | ||
1959 | } | ||
1960 | |||
1961 | if (match && (token->key.op == OP_MATCH)) { | ||
1962 | dbg(rules->udev, "%s is true (matching value)\n", token_str(token->type)); | ||
1963 | return 0; | ||
1964 | } | ||
1965 | if (!match && (token->key.op == OP_NOMATCH)) { | ||
1966 | dbg(rules->udev, "%s is true (non-matching value)\n", token_str(token->type)); | ||
1967 | return 0; | ||
1968 | } | ||
1969 | dbg(rules->udev, "%s is not true\n", token_str(token->type)); | ||
1970 | return -1; | ||
1971 | } | ||
1972 | |||
1973 | static int match_attr(struct udev_rules *rules, struct udev_device *dev, struct udev_event *event, struct token *cur) | ||
1974 | { | ||
1975 | const char *name; | ||
1976 | char nbuf[UTIL_NAME_SIZE]; | ||
1977 | const char *value; | ||
1978 | char vbuf[UTIL_NAME_SIZE]; | ||
1979 | size_t len; | ||
1980 | |||
1981 | name = &rules->buf[cur->key.attr_off]; | ||
1982 | switch (cur->key.attrsubst) { | ||
1983 | case SB_FORMAT: | ||
1984 | udev_event_apply_format(event, name, nbuf, sizeof(nbuf)); | ||
1985 | name = nbuf; | ||
1986 | /* fall through */ | ||
1987 | case SB_NONE: | ||
1988 | value = udev_device_get_sysattr_value(dev, name); | ||
1989 | if (value == NULL) | ||
1990 | return -1; | ||
1991 | break; | ||
1992 | case SB_SUBSYS: | ||
1993 | if (util_resolve_subsys_kernel(event->udev, name, vbuf, sizeof(vbuf), 1) != 0) | ||
1994 | return -1; | ||
1995 | value = vbuf; | ||
1996 | break; | ||
1997 | default: | ||
1998 | return -1; | ||
1999 | } | ||
2000 | |||
2001 | /* remove trailing whitespace, if not asked to match for it */ | ||
2002 | len = strlen(value); | ||
2003 | if (len > 0 && isspace(value[len-1])) { | ||
2004 | const char *key_value; | ||
2005 | size_t klen; | ||
2006 | |||
2007 | key_value = &rules->buf[cur->key.value_off]; | ||
2008 | klen = strlen(key_value); | ||
2009 | if (klen > 0 && !isspace(key_value[klen-1])) { | ||
2010 | if (value != vbuf) { | ||
2011 | util_strscpy(vbuf, sizeof(vbuf), value); | ||
2012 | value = vbuf; | ||
2013 | } | ||
2014 | while (len > 0 && isspace(vbuf[--len])) | ||
2015 | vbuf[len] = '\0'; | ||
2016 | dbg(rules->udev, "removed trailing whitespace from '%s'\n", value); | ||
2017 | } | ||
2018 | } | ||
2019 | |||
2020 | return match_key(rules, cur, value); | ||
2021 | } | ||
2022 | |||
2023 | enum escape_type { | ||
2024 | ESCAPE_UNSET, | ||
2025 | ESCAPE_NONE, | ||
2026 | ESCAPE_REPLACE, | ||
2027 | }; | ||
2028 | |||
2029 | int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, const sigset_t *sigmask) | ||
2030 | { | ||
2031 | struct token *cur; | ||
2032 | struct token *rule; | ||
2033 | enum escape_type esc = ESCAPE_UNSET; | ||
2034 | bool can_set_name; | ||
2035 | |||
2036 | if (rules->tokens == NULL) | ||
2037 | return -1; | ||
2038 | |||
2039 | can_set_name = ((strcmp(udev_device_get_action(event->dev), "remove") != 0) && | ||
2040 | (major(udev_device_get_devnum(event->dev)) > 0 || | ||
2041 | udev_device_get_ifindex(event->dev) > 0)); | ||
2042 | |||
2043 | /* loop through token list, match, run actions or forward to next rule */ | ||
2044 | cur = &rules->tokens[0]; | ||
2045 | rule = cur; | ||
2046 | for (;;) { | ||
2047 | dump_token(rules, cur); | ||
2048 | switch (cur->type) { | ||
2049 | case TK_RULE: | ||
2050 | /* current rule */ | ||
2051 | rule = cur; | ||
2052 | /* possibly skip rules which want to set NAME, SYMLINK, OWNER, GROUP, MODE */ | ||
2053 | if (!can_set_name && rule->rule.can_set_name) | ||
2054 | goto nomatch; | ||
2055 | esc = ESCAPE_UNSET; | ||
2056 | break; | ||
2057 | case TK_M_ACTION: | ||
2058 | if (match_key(rules, cur, udev_device_get_action(event->dev)) != 0) | ||
2059 | goto nomatch; | ||
2060 | break; | ||
2061 | case TK_M_DEVPATH: | ||
2062 | if (match_key(rules, cur, udev_device_get_devpath(event->dev)) != 0) | ||
2063 | goto nomatch; | ||
2064 | break; | ||
2065 | case TK_M_KERNEL: | ||
2066 | if (match_key(rules, cur, udev_device_get_sysname(event->dev)) != 0) | ||
2067 | goto nomatch; | ||
2068 | break; | ||
2069 | case TK_M_DEVLINK: { | ||
2070 | size_t devlen = strlen(udev_get_dev_path(event->udev))+1; | ||
2071 | struct udev_list_entry *list_entry; | ||
2072 | bool match = false; | ||
2073 | |||
2074 | udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(event->dev)) { | ||
2075 | const char *devlink; | ||
2076 | |||
2077 | devlink = &udev_list_entry_get_name(list_entry)[devlen]; | ||
2078 | if (match_key(rules, cur, devlink) == 0) { | ||
2079 | match = true; | ||
2080 | break; | ||
2081 | } | ||
2082 | } | ||
2083 | if (!match) | ||
2084 | goto nomatch; | ||
2085 | break; | ||
2086 | } | ||
2087 | case TK_M_NAME: | ||
2088 | if (match_key(rules, cur, event->name) != 0) | ||
2089 | goto nomatch; | ||
2090 | break; | ||
2091 | case TK_M_ENV: { | ||
2092 | const char *key_name = &rules->buf[cur->key.attr_off]; | ||
2093 | const char *value; | ||
2094 | |||
2095 | value = udev_device_get_property_value(event->dev, key_name); | ||
2096 | if (value == NULL) { | ||
2097 | dbg(event->udev, "ENV{%s} is not set, treat as empty\n", key_name); | ||
2098 | value = ""; | ||
2099 | } | ||
2100 | if (match_key(rules, cur, value)) | ||
2101 | goto nomatch; | ||
2102 | break; | ||
2103 | } | ||
2104 | case TK_M_TAG: { | ||
2105 | struct udev_list_entry *list_entry; | ||
2106 | bool match = false; | ||
2107 | |||
2108 | udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(event->dev)) { | ||
2109 | if (strcmp(&rules->buf[cur->key.value_off], udev_list_entry_get_name(list_entry)) == 0) { | ||
2110 | match = true; | ||
2111 | break; | ||
2112 | } | ||
2113 | } | ||
2114 | if (!match && (cur->key.op != OP_NOMATCH)) | ||
2115 | goto nomatch; | ||
2116 | break; | ||
2117 | } | ||
2118 | case TK_M_SUBSYSTEM: | ||
2119 | if (match_key(rules, cur, udev_device_get_subsystem(event->dev)) != 0) | ||
2120 | goto nomatch; | ||
2121 | break; | ||
2122 | case TK_M_DRIVER: | ||
2123 | if (match_key(rules, cur, udev_device_get_driver(event->dev)) != 0) | ||
2124 | goto nomatch; | ||
2125 | break; | ||
2126 | case TK_M_WAITFOR: { | ||
2127 | char filename[UTIL_PATH_SIZE]; | ||
2128 | int found; | ||
2129 | |||
2130 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], filename, sizeof(filename)); | ||
2131 | found = (wait_for_file(event->dev, filename, 10) == 0); | ||
2132 | if (!found && (cur->key.op != OP_NOMATCH)) | ||
2133 | goto nomatch; | ||
2134 | break; | ||
2135 | } | ||
2136 | case TK_M_ATTR: | ||
2137 | if (match_attr(rules, event->dev, event, cur) != 0) | ||
2138 | goto nomatch; | ||
2139 | break; | ||
2140 | case TK_M_KERNELS: | ||
2141 | case TK_M_SUBSYSTEMS: | ||
2142 | case TK_M_DRIVERS: | ||
2143 | case TK_M_ATTRS: | ||
2144 | case TK_M_TAGS: { | ||
2145 | struct token *next; | ||
2146 | |||
2147 | /* get whole sequence of parent matches */ | ||
2148 | next = cur; | ||
2149 | while (next->type > TK_M_PARENTS_MIN && next->type < TK_M_PARENTS_MAX) | ||
2150 | next++; | ||
2151 | |||
2152 | /* loop over parents */ | ||
2153 | event->dev_parent = event->dev; | ||
2154 | for (;;) { | ||
2155 | struct token *key; | ||
2156 | |||
2157 | dbg(event->udev, "parent: '%s'\n", udev_device_get_syspath(event->dev_parent)); | ||
2158 | /* loop over sequence of parent match keys */ | ||
2159 | for (key = cur; key < next; key++ ) { | ||
2160 | dump_token(rules, key); | ||
2161 | switch(key->type) { | ||
2162 | case TK_M_KERNELS: | ||
2163 | if (match_key(rules, key, udev_device_get_sysname(event->dev_parent)) != 0) | ||
2164 | goto try_parent; | ||
2165 | break; | ||
2166 | case TK_M_SUBSYSTEMS: | ||
2167 | if (match_key(rules, key, udev_device_get_subsystem(event->dev_parent)) != 0) | ||
2168 | goto try_parent; | ||
2169 | break; | ||
2170 | case TK_M_DRIVERS: | ||
2171 | if (match_key(rules, key, udev_device_get_driver(event->dev_parent)) != 0) | ||
2172 | goto try_parent; | ||
2173 | break; | ||
2174 | case TK_M_ATTRS: | ||
2175 | if (match_attr(rules, event->dev_parent, event, key) != 0) | ||
2176 | goto try_parent; | ||
2177 | break; | ||
2178 | case TK_M_TAGS: { | ||
2179 | bool match = udev_device_has_tag(event->dev_parent, &rules->buf[cur->key.value_off]); | ||
2180 | |||
2181 | if (match && key->key.op == OP_NOMATCH) | ||
2182 | goto try_parent; | ||
2183 | if (!match && key->key.op == OP_MATCH) | ||
2184 | goto try_parent; | ||
2185 | break; | ||
2186 | } | ||
2187 | default: | ||
2188 | goto nomatch; | ||
2189 | } | ||
2190 | dbg(event->udev, "parent key matched\n"); | ||
2191 | } | ||
2192 | dbg(event->udev, "all parent keys matched\n"); | ||
2193 | break; | ||
2194 | |||
2195 | try_parent: | ||
2196 | event->dev_parent = udev_device_get_parent(event->dev_parent); | ||
2197 | if (event->dev_parent == NULL) | ||
2198 | goto nomatch; | ||
2199 | } | ||
2200 | /* move behind our sequence of parent match keys */ | ||
2201 | cur = next; | ||
2202 | continue; | ||
2203 | } | ||
2204 | case TK_M_TEST: { | ||
2205 | char filename[UTIL_PATH_SIZE]; | ||
2206 | struct stat statbuf; | ||
2207 | int match; | ||
2208 | |||
2209 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], filename, sizeof(filename)); | ||
2210 | if (util_resolve_subsys_kernel(event->udev, filename, filename, sizeof(filename), 0) != 0) { | ||
2211 | if (filename[0] != '/') { | ||
2212 | char tmp[UTIL_PATH_SIZE]; | ||
2213 | |||
2214 | util_strscpy(tmp, sizeof(tmp), filename); | ||
2215 | util_strscpyl(filename, sizeof(filename), | ||
2216 | udev_device_get_syspath(event->dev), "/", tmp, NULL); | ||
2217 | } | ||
2218 | } | ||
2219 | attr_subst_subdir(filename, sizeof(filename)); | ||
2220 | |||
2221 | match = (stat(filename, &statbuf) == 0); | ||
2222 | dbg(event->udev, "'%s' %s", filename, match ? "exists\n" : "does not exist\n"); | ||
2223 | if (match && cur->key.mode > 0) { | ||
2224 | match = ((statbuf.st_mode & cur->key.mode) > 0); | ||
2225 | dbg(event->udev, "'%s' has mode=%#o and %s %#o\n", filename, statbuf.st_mode, | ||
2226 | match ? "matches" : "does not match", cur->key.mode); | ||
2227 | } | ||
2228 | if (match && cur->key.op == OP_NOMATCH) | ||
2229 | goto nomatch; | ||
2230 | if (!match && cur->key.op == OP_MATCH) | ||
2231 | goto nomatch; | ||
2232 | break; | ||
2233 | } | ||
2234 | case TK_M_EVENT_TIMEOUT: | ||
2235 | info(event->udev, "OPTIONS event_timeout=%u\n", cur->key.event_timeout); | ||
2236 | event->timeout_usec = cur->key.event_timeout * 1000 * 1000; | ||
2237 | break; | ||
2238 | case TK_M_PROGRAM: { | ||
2239 | char program[UTIL_PATH_SIZE]; | ||
2240 | char **envp; | ||
2241 | char result[UTIL_PATH_SIZE]; | ||
2242 | |||
2243 | free(event->program_result); | ||
2244 | event->program_result = NULL; | ||
2245 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], program, sizeof(program)); | ||
2246 | envp = udev_device_get_properties_envp(event->dev); | ||
2247 | info(event->udev, "PROGRAM '%s' %s:%u\n", | ||
2248 | program, | ||
2249 | &rules->buf[rule->rule.filename_off], | ||
2250 | rule->rule.filename_line); | ||
2251 | |||
2252 | if (udev_event_spawn(event, program, envp, sigmask, result, sizeof(result)) < 0) { | ||
2253 | if (cur->key.op != OP_NOMATCH) | ||
2254 | goto nomatch; | ||
2255 | } else { | ||
2256 | int count; | ||
2257 | |||
2258 | util_remove_trailing_chars(result, '\n'); | ||
2259 | if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) { | ||
2260 | count = util_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT); | ||
2261 | if (count > 0) | ||
2262 | info(event->udev, "%i character(s) replaced\n" , count); | ||
2263 | } | ||
2264 | event->program_result = strdup(result); | ||
2265 | dbg(event->udev, "storing result '%s'\n", event->program_result); | ||
2266 | if (cur->key.op == OP_NOMATCH) | ||
2267 | goto nomatch; | ||
2268 | } | ||
2269 | break; | ||
2270 | } | ||
2271 | case TK_M_IMPORT_FILE: { | ||
2272 | char import[UTIL_PATH_SIZE]; | ||
2273 | |||
2274 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], import, sizeof(import)); | ||
2275 | if (import_file_into_properties(event->dev, import) != 0) | ||
2276 | if (cur->key.op != OP_NOMATCH) | ||
2277 | goto nomatch; | ||
2278 | break; | ||
2279 | } | ||
2280 | case TK_M_IMPORT_PROG: { | ||
2281 | char import[UTIL_PATH_SIZE]; | ||
2282 | |||
2283 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], import, sizeof(import)); | ||
2284 | info(event->udev, "IMPORT '%s' %s:%u\n", | ||
2285 | import, | ||
2286 | &rules->buf[rule->rule.filename_off], | ||
2287 | rule->rule.filename_line); | ||
2288 | |||
2289 | if (import_program_into_properties(event, import, sigmask) != 0) | ||
2290 | if (cur->key.op != OP_NOMATCH) | ||
2291 | goto nomatch; | ||
2292 | break; | ||
2293 | } | ||
2294 | case TK_M_IMPORT_BUILTIN: { | ||
2295 | char command[UTIL_PATH_SIZE]; | ||
2296 | |||
2297 | if (udev_builtin_run_once(cur->key.builtin_cmd)) { | ||
2298 | /* check if we ran already */ | ||
2299 | if (event->builtin_run & (1 << cur->key.builtin_cmd)) { | ||
2300 | info(event->udev, "IMPORT builtin skip '%s' %s:%u\n", | ||
2301 | udev_builtin_name(cur->key.builtin_cmd), | ||
2302 | &rules->buf[rule->rule.filename_off], | ||
2303 | rule->rule.filename_line); | ||
2304 | /* return the result from earlier run */ | ||
2305 | if (event->builtin_ret & (1 << cur->key.builtin_cmd)) | ||
2306 | if (cur->key.op != OP_NOMATCH) | ||
2307 | goto nomatch; | ||
2308 | break; | ||
2309 | } | ||
2310 | /* mark as ran */ | ||
2311 | event->builtin_run |= (1 << cur->key.builtin_cmd); | ||
2312 | } | ||
2313 | |||
2314 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], command, sizeof(command)); | ||
2315 | info(event->udev, "IMPORT builtin '%s' %s:%u\n", | ||
2316 | udev_builtin_name(cur->key.builtin_cmd), | ||
2317 | &rules->buf[rule->rule.filename_off], | ||
2318 | rule->rule.filename_line); | ||
2319 | |||
2320 | if (udev_builtin_run(event->dev, cur->key.builtin_cmd, command, false) != 0) { | ||
2321 | /* remember failure */ | ||
2322 | info(rules->udev, "IMPORT builtin '%s' returned non-zero\n", | ||
2323 | udev_builtin_name(cur->key.builtin_cmd)); | ||
2324 | event->builtin_ret |= (1 << cur->key.builtin_cmd); | ||
2325 | if (cur->key.op != OP_NOMATCH) | ||
2326 | goto nomatch; | ||
2327 | } | ||
2328 | break; | ||
2329 | } | ||
2330 | case TK_M_IMPORT_DB: { | ||
2331 | const char *key = &rules->buf[cur->key.value_off]; | ||
2332 | const char *value; | ||
2333 | |||
2334 | value = udev_device_get_property_value(event->dev_db, key); | ||
2335 | if (value != NULL) { | ||
2336 | struct udev_list_entry *entry; | ||
2337 | |||
2338 | entry = udev_device_add_property(event->dev, key, value); | ||
2339 | udev_list_entry_set_num(entry, true); | ||
2340 | } else { | ||
2341 | if (cur->key.op != OP_NOMATCH) | ||
2342 | goto nomatch; | ||
2343 | } | ||
2344 | break; | ||
2345 | } | ||
2346 | case TK_M_IMPORT_CMDLINE: { | ||
2347 | FILE *f; | ||
2348 | bool imported = false; | ||
2349 | |||
2350 | f = fopen("/proc/cmdline", "r"); | ||
2351 | if (f != NULL) { | ||
2352 | char cmdline[4096]; | ||
2353 | |||
2354 | if (fgets(cmdline, sizeof(cmdline), f) != NULL) { | ||
2355 | const char *key = &rules->buf[cur->key.value_off]; | ||
2356 | char *pos; | ||
2357 | |||
2358 | pos = strstr(cmdline, key); | ||
2359 | if (pos != NULL) { | ||
2360 | struct udev_list_entry *entry; | ||
2361 | |||
2362 | pos += strlen(key); | ||
2363 | if (pos[0] == '\0' || isspace(pos[0])) { | ||
2364 | /* we import simple flags as 'FLAG=1' */ | ||
2365 | entry = udev_device_add_property(event->dev, key, "1"); | ||
2366 | udev_list_entry_set_num(entry, true); | ||
2367 | imported = true; | ||
2368 | } else if (pos[0] == '=') { | ||
2369 | const char *value; | ||
2370 | |||
2371 | pos++; | ||
2372 | value = pos; | ||
2373 | while (pos[0] != '\0' && !isspace(pos[0])) | ||
2374 | pos++; | ||
2375 | pos[0] = '\0'; | ||
2376 | entry = udev_device_add_property(event->dev, key, value); | ||
2377 | udev_list_entry_set_num(entry, true); | ||
2378 | imported = true; | ||
2379 | } | ||
2380 | } | ||
2381 | } | ||
2382 | fclose(f); | ||
2383 | } | ||
2384 | if (!imported && cur->key.op != OP_NOMATCH) | ||
2385 | goto nomatch; | ||
2386 | break; | ||
2387 | } | ||
2388 | case TK_M_IMPORT_PARENT: { | ||
2389 | char import[UTIL_PATH_SIZE]; | ||
2390 | |||
2391 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], import, sizeof(import)); | ||
2392 | if (import_parent_into_properties(event->dev, import) != 0) | ||
2393 | if (cur->key.op != OP_NOMATCH) | ||
2394 | goto nomatch; | ||
2395 | break; | ||
2396 | } | ||
2397 | case TK_M_RESULT: | ||
2398 | if (match_key(rules, cur, event->program_result) != 0) | ||
2399 | goto nomatch; | ||
2400 | break; | ||
2401 | case TK_A_STRING_ESCAPE_NONE: | ||
2402 | esc = ESCAPE_NONE; | ||
2403 | break; | ||
2404 | case TK_A_STRING_ESCAPE_REPLACE: | ||
2405 | esc = ESCAPE_REPLACE; | ||
2406 | break; | ||
2407 | case TK_A_DB_PERSIST: | ||
2408 | udev_device_set_db_persist(event->dev); | ||
2409 | break; | ||
2410 | case TK_A_INOTIFY_WATCH: | ||
2411 | if (event->inotify_watch_final) | ||
2412 | break; | ||
2413 | if (cur->key.op == OP_ASSIGN_FINAL) | ||
2414 | event->inotify_watch_final = true; | ||
2415 | event->inotify_watch = cur->key.watch; | ||
2416 | break; | ||
2417 | case TK_A_DEVLINK_PRIO: | ||
2418 | udev_device_set_devlink_priority(event->dev, cur->key.devlink_prio); | ||
2419 | break; | ||
2420 | case TK_A_OWNER: { | ||
2421 | char owner[UTIL_NAME_SIZE]; | ||
2422 | |||
2423 | if (event->owner_final) | ||
2424 | break; | ||
2425 | if (cur->key.op == OP_ASSIGN_FINAL) | ||
2426 | event->owner_final = true; | ||
2427 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], owner, sizeof(owner)); | ||
2428 | event->uid = util_lookup_user(event->udev, owner); | ||
2429 | info(event->udev, "OWNER %u %s:%u\n", | ||
2430 | event->uid, | ||
2431 | &rules->buf[rule->rule.filename_off], | ||
2432 | rule->rule.filename_line); | ||
2433 | break; | ||
2434 | } | ||
2435 | case TK_A_GROUP: { | ||
2436 | char group[UTIL_NAME_SIZE]; | ||
2437 | |||
2438 | if (event->group_final) | ||
2439 | break; | ||
2440 | if (cur->key.op == OP_ASSIGN_FINAL) | ||
2441 | event->group_final = true; | ||
2442 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], group, sizeof(group)); | ||
2443 | event->gid = util_lookup_group(event->udev, group); | ||
2444 | info(event->udev, "GROUP %u %s:%u\n", | ||
2445 | event->gid, | ||
2446 | &rules->buf[rule->rule.filename_off], | ||
2447 | rule->rule.filename_line); | ||
2448 | break; | ||
2449 | } | ||
2450 | case TK_A_MODE: { | ||
2451 | char mode_str[UTIL_NAME_SIZE]; | ||
2452 | mode_t mode; | ||
2453 | char *endptr; | ||
2454 | |||
2455 | if (event->mode_final) | ||
2456 | break; | ||
2457 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], mode_str, sizeof(mode_str)); | ||
2458 | mode = strtol(mode_str, &endptr, 8); | ||
2459 | if (endptr[0] != '\0') { | ||
2460 | err(event->udev, "ignoring invalid mode '%s'\n", mode_str); | ||
2461 | break; | ||
2462 | } | ||
2463 | if (cur->key.op == OP_ASSIGN_FINAL) | ||
2464 | event->mode_final = true; | ||
2465 | event->mode_set = true; | ||
2466 | event->mode = mode; | ||
2467 | info(event->udev, "MODE %#o %s:%u\n", | ||
2468 | event->mode, | ||
2469 | &rules->buf[rule->rule.filename_off], | ||
2470 | rule->rule.filename_line); | ||
2471 | break; | ||
2472 | } | ||
2473 | case TK_A_OWNER_ID: | ||
2474 | if (event->owner_final) | ||
2475 | break; | ||
2476 | if (cur->key.op == OP_ASSIGN_FINAL) | ||
2477 | event->owner_final = true; | ||
2478 | event->uid = cur->key.uid; | ||
2479 | info(event->udev, "OWNER %u %s:%u\n", | ||
2480 | event->uid, | ||
2481 | &rules->buf[rule->rule.filename_off], | ||
2482 | rule->rule.filename_line); | ||
2483 | break; | ||
2484 | case TK_A_GROUP_ID: | ||
2485 | if (event->group_final) | ||
2486 | break; | ||
2487 | if (cur->key.op == OP_ASSIGN_FINAL) | ||
2488 | event->group_final = true; | ||
2489 | event->gid = cur->key.gid; | ||
2490 | info(event->udev, "GROUP %u %s:%u\n", | ||
2491 | event->gid, | ||
2492 | &rules->buf[rule->rule.filename_off], | ||
2493 | rule->rule.filename_line); | ||
2494 | break; | ||
2495 | case TK_A_MODE_ID: | ||
2496 | if (event->mode_final) | ||
2497 | break; | ||
2498 | if (cur->key.op == OP_ASSIGN_FINAL) | ||
2499 | event->mode_final = true; | ||
2500 | event->mode_set = true; | ||
2501 | event->mode = cur->key.mode; | ||
2502 | info(event->udev, "MODE %#o %s:%u\n", | ||
2503 | event->mode, | ||
2504 | &rules->buf[rule->rule.filename_off], | ||
2505 | rule->rule.filename_line); | ||
2506 | break; | ||
2507 | case TK_A_ENV: { | ||
2508 | const char *name = &rules->buf[cur->key.attr_off]; | ||
2509 | char *value = &rules->buf[cur->key.value_off]; | ||
2510 | |||
2511 | if (value[0] != '\0') { | ||
2512 | char temp_value[UTIL_NAME_SIZE]; | ||
2513 | struct udev_list_entry *entry; | ||
2514 | |||
2515 | udev_event_apply_format(event, value, temp_value, sizeof(temp_value)); | ||
2516 | entry = udev_device_add_property(event->dev, name, temp_value); | ||
2517 | /* store in db, skip private keys */ | ||
2518 | if (name[0] != '.') | ||
2519 | udev_list_entry_set_num(entry, true); | ||
2520 | } else { | ||
2521 | udev_device_add_property(event->dev, name, NULL); | ||
2522 | } | ||
2523 | break; | ||
2524 | } | ||
2525 | case TK_A_TAG: { | ||
2526 | char tag[UTIL_PATH_SIZE]; | ||
2527 | const char *p; | ||
2528 | |||
2529 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], tag, sizeof(tag)); | ||
2530 | if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) | ||
2531 | udev_device_cleanup_tags_list(event->dev); | ||
2532 | for (p = tag; *p != '\0'; p++) { | ||
2533 | if ((*p >= 'a' && *p <= 'z') || | ||
2534 | (*p >= 'A' && *p <= 'Z') || | ||
2535 | (*p >= '0' && *p <= '9') || | ||
2536 | *p == '-' || *p == '_') | ||
2537 | continue; | ||
2538 | err(event->udev, "ignoring invalid tag name '%s'\n", tag); | ||
2539 | break; | ||
2540 | } | ||
2541 | udev_device_add_tag(event->dev, tag); | ||
2542 | break; | ||
2543 | } | ||
2544 | case TK_A_NAME: { | ||
2545 | const char *name = &rules->buf[cur->key.value_off]; | ||
2546 | |||
2547 | char name_str[UTIL_PATH_SIZE]; | ||
2548 | int count; | ||
2549 | |||
2550 | if (event->name_final) | ||
2551 | break; | ||
2552 | if (cur->key.op == OP_ASSIGN_FINAL) | ||
2553 | event->name_final = true; | ||
2554 | udev_event_apply_format(event, name, name_str, sizeof(name_str)); | ||
2555 | if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) { | ||
2556 | count = util_replace_chars(name_str, "/"); | ||
2557 | if (count > 0) | ||
2558 | info(event->udev, "%i character(s) replaced\n", count); | ||
2559 | } | ||
2560 | if (major(udev_device_get_devnum(event->dev))) { | ||
2561 | size_t devlen = strlen(udev_get_dev_path(event->udev))+1; | ||
2562 | |||
2563 | if (strcmp(name_str, &udev_device_get_devnode(event->dev)[devlen]) != 0) { | ||
2564 | err(event->udev, "NAME=\"%s\" ignored, kernel device nodes " | ||
2565 | "can not be renamed; please fix it in %s:%u\n", name, | ||
2566 | &rules->buf[rule->rule.filename_off], rule->rule.filename_line); | ||
2567 | break; | ||
2568 | } | ||
2569 | } | ||
2570 | free(event->name); | ||
2571 | event->name = strdup(name_str); | ||
2572 | info(event->udev, "NAME '%s' %s:%u\n", | ||
2573 | event->name, | ||
2574 | &rules->buf[rule->rule.filename_off], | ||
2575 | rule->rule.filename_line); | ||
2576 | break; | ||
2577 | } | ||
2578 | case TK_A_DEVLINK: { | ||
2579 | char temp[UTIL_PATH_SIZE]; | ||
2580 | char filename[UTIL_PATH_SIZE]; | ||
2581 | char *pos, *next; | ||
2582 | int count = 0; | ||
2583 | |||
2584 | if (event->devlink_final) | ||
2585 | break; | ||
2586 | if (major(udev_device_get_devnum(event->dev)) == 0) | ||
2587 | break; | ||
2588 | if (cur->key.op == OP_ASSIGN_FINAL) | ||
2589 | event->devlink_final = true; | ||
2590 | if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) | ||
2591 | udev_device_cleanup_devlinks_list(event->dev); | ||
2592 | |||
2593 | /* allow multiple symlinks separated by spaces */ | ||
2594 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], temp, sizeof(temp)); | ||
2595 | if (esc == ESCAPE_UNSET) | ||
2596 | count = util_replace_chars(temp, "/ "); | ||
2597 | else if (esc == ESCAPE_REPLACE) | ||
2598 | count = util_replace_chars(temp, "/"); | ||
2599 | if (count > 0) | ||
2600 | info(event->udev, "%i character(s) replaced\n" , count); | ||
2601 | dbg(event->udev, "rule applied, added symlink(s) '%s'\n", temp); | ||
2602 | pos = temp; | ||
2603 | while (isspace(pos[0])) | ||
2604 | pos++; | ||
2605 | next = strchr(pos, ' '); | ||
2606 | while (next != NULL) { | ||
2607 | next[0] = '\0'; | ||
2608 | info(event->udev, "LINK '%s' %s:%u\n", pos, | ||
2609 | &rules->buf[rule->rule.filename_off], rule->rule.filename_line); | ||
2610 | util_strscpyl(filename, sizeof(filename), udev_get_dev_path(event->udev), "/", pos, NULL); | ||
2611 | udev_device_add_devlink(event->dev, filename, cur->key.devlink_unique); | ||
2612 | while (isspace(next[1])) | ||
2613 | next++; | ||
2614 | pos = &next[1]; | ||
2615 | next = strchr(pos, ' '); | ||
2616 | } | ||
2617 | if (pos[0] != '\0') { | ||
2618 | info(event->udev, "LINK '%s' %s:%u\n", pos, | ||
2619 | &rules->buf[rule->rule.filename_off], rule->rule.filename_line); | ||
2620 | util_strscpyl(filename, sizeof(filename), udev_get_dev_path(event->udev), "/", pos, NULL); | ||
2621 | udev_device_add_devlink(event->dev, filename, cur->key.devlink_unique); | ||
2622 | } | ||
2623 | break; | ||
2624 | } | ||
2625 | case TK_A_ATTR: { | ||
2626 | const char *key_name = &rules->buf[cur->key.attr_off]; | ||
2627 | char attr[UTIL_PATH_SIZE]; | ||
2628 | char value[UTIL_NAME_SIZE]; | ||
2629 | FILE *f; | ||
2630 | |||
2631 | if (util_resolve_subsys_kernel(event->udev, key_name, attr, sizeof(attr), 0) != 0) | ||
2632 | util_strscpyl(attr, sizeof(attr), udev_device_get_syspath(event->dev), "/", key_name, NULL); | ||
2633 | attr_subst_subdir(attr, sizeof(attr)); | ||
2634 | |||
2635 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], value, sizeof(value)); | ||
2636 | info(event->udev, "ATTR '%s' writing '%s' %s:%u\n", attr, value, | ||
2637 | &rules->buf[rule->rule.filename_off], | ||
2638 | rule->rule.filename_line); | ||
2639 | f = fopen(attr, "w"); | ||
2640 | if (f != NULL) { | ||
2641 | if (fprintf(f, "%s", value) <= 0) | ||
2642 | err(event->udev, "error writing ATTR{%s}: %m\n", attr); | ||
2643 | fclose(f); | ||
2644 | } else { | ||
2645 | err(event->udev, "error opening ATTR{%s} for writing: %m\n", attr); | ||
2646 | } | ||
2647 | break; | ||
2648 | } | ||
2649 | case TK_A_RUN: { | ||
2650 | if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) | ||
2651 | udev_list_cleanup(&event->run_list); | ||
2652 | info(event->udev, "RUN '%s' %s:%u\n", | ||
2653 | &rules->buf[cur->key.value_off], | ||
2654 | &rules->buf[rule->rule.filename_off], | ||
2655 | rule->rule.filename_line); | ||
2656 | udev_list_entry_add(&event->run_list, &rules->buf[cur->key.value_off], NULL); | ||
2657 | break; | ||
2658 | } | ||
2659 | case TK_A_GOTO: | ||
2660 | if (cur->key.rule_goto == 0) | ||
2661 | break; | ||
2662 | cur = &rules->tokens[cur->key.rule_goto]; | ||
2663 | continue; | ||
2664 | case TK_END: | ||
2665 | return 0; | ||
2666 | |||
2667 | case TK_M_PARENTS_MIN: | ||
2668 | case TK_M_PARENTS_MAX: | ||
2669 | case TK_M_MAX: | ||
2670 | case TK_UNSET: | ||
2671 | err(rules->udev, "wrong type %u\n", cur->type); | ||
2672 | goto nomatch; | ||
2673 | } | ||
2674 | |||
2675 | cur++; | ||
2676 | continue; | ||
2677 | nomatch: | ||
2678 | /* fast-forward to next rule */ | ||
2679 | cur = rule + rule->rule.token_count; | ||
2680 | dbg(rules->udev, "forward to rule: %u\n", | ||
2681 | (unsigned int) (cur - rules->tokens)); | ||
2682 | } | ||
2683 | } | ||
2684 | |||
2685 | void udev_rules_apply_static_dev_perms(struct udev_rules *rules) | ||
2686 | { | ||
2687 | struct token *cur; | ||
2688 | struct token *rule; | ||
2689 | uid_t uid = 0; | ||
2690 | gid_t gid = 0; | ||
2691 | mode_t mode = 0; | ||
2692 | |||
2693 | if (rules->tokens == NULL) | ||
2694 | return; | ||
2695 | |||
2696 | cur = &rules->tokens[0]; | ||
2697 | rule = cur; | ||
2698 | for (;;) { | ||
2699 | switch (cur->type) { | ||
2700 | case TK_RULE: | ||
2701 | /* current rule */ | ||
2702 | rule = cur; | ||
2703 | |||
2704 | /* skip rules without a static_node tag */ | ||
2705 | if (!rule->rule.has_static_node) | ||
2706 | goto next; | ||
2707 | |||
2708 | uid = 0; | ||
2709 | gid = 0; | ||
2710 | mode = 0; | ||
2711 | break; | ||
2712 | case TK_A_OWNER_ID: | ||
2713 | uid = cur->key.uid; | ||
2714 | break; | ||
2715 | case TK_A_GROUP_ID: | ||
2716 | gid = cur->key.gid; | ||
2717 | break; | ||
2718 | case TK_A_MODE_ID: | ||
2719 | mode = cur->key.mode; | ||
2720 | break; | ||
2721 | case TK_A_STATIC_NODE: { | ||
2722 | char filename[UTIL_PATH_SIZE]; | ||
2723 | struct stat stats; | ||
2724 | |||
2725 | /* we assure, that the permissions tokens are sorted before the static token */ | ||
2726 | if (mode == 0 && uid == 0 && gid == 0) | ||
2727 | goto next; | ||
2728 | util_strscpyl(filename, sizeof(filename), udev_get_dev_path(rules->udev), "/", | ||
2729 | &rules->buf[cur->key.value_off], NULL); | ||
2730 | if (stat(filename, &stats) != 0) | ||
2731 | goto next; | ||
2732 | if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode)) | ||
2733 | goto next; | ||
2734 | if (mode == 0) { | ||
2735 | if (gid > 0) | ||
2736 | mode = 0660; | ||
2737 | else | ||
2738 | mode = 0600; | ||
2739 | } | ||
2740 | if (mode != (stats.st_mode & 01777)) { | ||
2741 | chmod(filename, mode); | ||
2742 | info(rules->udev, "chmod '%s' %#o\n", filename, mode); | ||
2743 | } | ||
2744 | |||
2745 | if ((uid != 0 && uid != stats.st_uid) || (gid != 0 && gid != stats.st_gid)) { | ||
2746 | chown(filename, uid, gid); | ||
2747 | info(rules->udev, "chown '%s' %u %u\n", filename, uid, gid); | ||
2748 | } | ||
2749 | |||
2750 | utimensat(AT_FDCWD, filename, NULL, 0); | ||
2751 | break; | ||
2752 | } | ||
2753 | case TK_END: | ||
2754 | return; | ||
2755 | } | ||
2756 | |||
2757 | cur++; | ||
2758 | continue; | ||
2759 | next: | ||
2760 | /* fast-forward to next rule */ | ||
2761 | cur = rule + rule->rule.token_count; | ||
2762 | continue; | ||
2763 | } | ||
2764 | } |
File src/udev-watch.c added (mode: 100644) (index 0000000..fba7028) | |||
1 | /* | ||
2 | * Copyright (C) 2004-2010 Kay Sievers <kay.sievers@vrfy.org> | ||
3 | * Copyright (C) 2009 Canonical Ltd. | ||
4 | * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com> | ||
5 | * | ||
6 | * This program is free software: you can redistribute it and/or modify | ||
7 | * it 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 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | |||
20 | #include <sys/types.h> | ||
21 | #include <errno.h> | ||
22 | #include <fcntl.h> | ||
23 | #include <stdio.h> | ||
24 | #include <dirent.h> | ||
25 | #include <stddef.h> | ||
26 | #include <stdlib.h> | ||
27 | #include <string.h> | ||
28 | #include <unistd.h> | ||
29 | #include <sys/inotify.h> | ||
30 | |||
31 | #include "udev.h" | ||
32 | |||
33 | static int inotify_fd = -1; | ||
34 | |||
35 | /* inotify descriptor, will be shared with rules directory; | ||
36 | * set to cloexec since we need our children to be able to add | ||
37 | * watches for us | ||
38 | */ | ||
39 | int udev_watch_init(struct udev *udev) | ||
40 | { | ||
41 | inotify_fd = inotify_init1(IN_CLOEXEC); | ||
42 | if (inotify_fd < 0) | ||
43 | err(udev, "inotify_init failed: %m\n"); | ||
44 | return inotify_fd; | ||
45 | } | ||
46 | |||
47 | /* move any old watches directory out of the way, and then restore | ||
48 | * the watches | ||
49 | */ | ||
50 | void udev_watch_restore(struct udev *udev) | ||
51 | { | ||
52 | char filename[UTIL_PATH_SIZE], oldname[UTIL_PATH_SIZE]; | ||
53 | |||
54 | if (inotify_fd < 0) | ||
55 | return; | ||
56 | |||
57 | util_strscpyl(oldname, sizeof(oldname), udev_get_run_path(udev), "/watch.old", NULL); | ||
58 | util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/watch", NULL); | ||
59 | if (rename(filename, oldname) == 0) { | ||
60 | DIR *dir; | ||
61 | struct dirent *ent; | ||
62 | |||
63 | dir = opendir(oldname); | ||
64 | if (dir == NULL) { | ||
65 | err(udev, "unable to open old watches dir '%s', old watches will not be restored: %m", oldname); | ||
66 | return; | ||
67 | } | ||
68 | |||
69 | for (ent = readdir(dir); ent != NULL; ent = readdir(dir)) { | ||
70 | char device[UTIL_PATH_SIZE]; | ||
71 | char *s; | ||
72 | size_t l; | ||
73 | ssize_t len; | ||
74 | struct udev_device *dev; | ||
75 | |||
76 | if (ent->d_name[0] == '.') | ||
77 | continue; | ||
78 | |||
79 | s = device; | ||
80 | l = util_strpcpy(&s, sizeof(device), udev_get_sys_path(udev)); | ||
81 | len = readlinkat(dirfd(dir), ent->d_name, s, l); | ||
82 | if (len <= 0 || len == (ssize_t)l) | ||
83 | goto unlink; | ||
84 | s[len] = '\0'; | ||
85 | |||
86 | dev = udev_device_new_from_device_id(udev, s); | ||
87 | if (dev == NULL) | ||
88 | goto unlink; | ||
89 | |||
90 | info(udev, "restoring old watch on '%s'\n", udev_device_get_devnode(dev)); | ||
91 | udev_watch_begin(udev, dev); | ||
92 | udev_device_unref(dev); | ||
93 | unlink: | ||
94 | unlinkat(dirfd(dir), ent->d_name, 0); | ||
95 | } | ||
96 | |||
97 | closedir(dir); | ||
98 | rmdir(oldname); | ||
99 | |||
100 | } else if (errno != ENOENT) { | ||
101 | err(udev, "unable to move watches dir '%s', old watches will not be restored: %m", filename); | ||
102 | } | ||
103 | } | ||
104 | |||
105 | void udev_watch_begin(struct udev *udev, struct udev_device *dev) | ||
106 | { | ||
107 | char filename[UTIL_PATH_SIZE]; | ||
108 | int wd; | ||
109 | |||
110 | if (inotify_fd < 0) | ||
111 | return; | ||
112 | |||
113 | info(udev, "adding watch on '%s'\n", udev_device_get_devnode(dev)); | ||
114 | wd = inotify_add_watch(inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE); | ||
115 | if (wd < 0) { | ||
116 | err(udev, "inotify_add_watch(%d, %s, %o) failed: %m\n", | ||
117 | inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE); | ||
118 | return; | ||
119 | } | ||
120 | |||
121 | snprintf(filename, sizeof(filename), "%s/watch/%d", udev_get_run_path(udev), wd); | ||
122 | util_create_path(udev, filename); | ||
123 | unlink(filename); | ||
124 | symlink(udev_device_get_id_filename(dev), filename); | ||
125 | |||
126 | udev_device_set_watch_handle(dev, wd); | ||
127 | } | ||
128 | |||
129 | void udev_watch_end(struct udev *udev, struct udev_device *dev) | ||
130 | { | ||
131 | int wd; | ||
132 | char filename[UTIL_PATH_SIZE]; | ||
133 | |||
134 | if (inotify_fd < 0) | ||
135 | return; | ||
136 | |||
137 | wd = udev_device_get_watch_handle(dev); | ||
138 | if (wd < 0) | ||
139 | return; | ||
140 | |||
141 | info(udev, "removing watch on '%s'\n", udev_device_get_devnode(dev)); | ||
142 | inotify_rm_watch(inotify_fd, wd); | ||
143 | |||
144 | snprintf(filename, sizeof(filename), "%s/watch/%d", udev_get_run_path(udev), wd); | ||
145 | unlink(filename); | ||
146 | |||
147 | udev_device_set_watch_handle(dev, -1); | ||
148 | } | ||
149 | |||
150 | struct udev_device *udev_watch_lookup(struct udev *udev, int wd) | ||
151 | { | ||
152 | char filename[UTIL_PATH_SIZE]; | ||
153 | char majmin[UTIL_PATH_SIZE]; | ||
154 | char *s; | ||
155 | size_t l; | ||
156 | ssize_t len; | ||
157 | |||
158 | if (inotify_fd < 0 || wd < 0) | ||
159 | return NULL; | ||
160 | |||
161 | snprintf(filename, sizeof(filename), "%s/watch/%d", udev_get_run_path(udev), wd); | ||
162 | s = majmin; | ||
163 | l = util_strpcpy(&s, sizeof(majmin), udev_get_sys_path(udev)); | ||
164 | len = readlink(filename, s, l); | ||
165 | if (len <= 0 || (size_t)len == l) | ||
166 | return NULL; | ||
167 | s[len] = '\0'; | ||
168 | |||
169 | return udev_device_new_from_device_id(udev, s); | ||
170 | } |
File src/udev.conf added (mode: 100644) (index 0000000..f39253e) | |||
1 | # see udev(7) for details | ||
2 | |||
3 | #udev_log="info" |
File src/udev.h added (mode: 100644) (index 0000000..ecf8cc5) | |||
1 | /* | ||
2 | * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com> | ||
3 | * Copyright (C) 2003-2010 Kay Sievers <kay.sievers@vrfy.org> | ||
4 | * | ||
5 | * This program is free software: you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation, either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | |||
19 | #ifndef _UDEV_H_ | ||
20 | #define _UDEV_H_ | ||
21 | |||
22 | #include <sys/types.h> | ||
23 | #include <sys/param.h> | ||
24 | #include <signal.h> | ||
25 | |||
26 | #include "libudev.h" | ||
27 | #include "libudev-private.h" | ||
28 | |||
29 | struct udev_event { | ||
30 | struct udev *udev; | ||
31 | struct udev_device *dev; | ||
32 | struct udev_device *dev_parent; | ||
33 | struct udev_device *dev_db; | ||
34 | char *name; | ||
35 | char *program_result; | ||
36 | mode_t mode; | ||
37 | uid_t uid; | ||
38 | gid_t gid; | ||
39 | struct udev_list run_list; | ||
40 | int exec_delay; | ||
41 | unsigned long long birth_usec; | ||
42 | unsigned long long timeout_usec; | ||
43 | int fd_signal; | ||
44 | unsigned int builtin_run; | ||
45 | unsigned int builtin_ret; | ||
46 | bool sigterm; | ||
47 | bool inotify_watch; | ||
48 | bool inotify_watch_final; | ||
49 | bool group_final; | ||
50 | bool owner_final; | ||
51 | bool mode_set; | ||
52 | bool mode_final; | ||
53 | bool name_final; | ||
54 | bool devlink_final; | ||
55 | bool run_final; | ||
56 | }; | ||
57 | |||
58 | struct udev_watch { | ||
59 | struct udev_list_node node; | ||
60 | int handle; | ||
61 | char *name; | ||
62 | }; | ||
63 | |||
64 | /* udev-rules.c */ | ||
65 | struct udev_rules; | ||
66 | struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names); | ||
67 | struct udev_rules *udev_rules_unref(struct udev_rules *rules); | ||
68 | int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, const sigset_t *sigmask); | ||
69 | void udev_rules_apply_static_dev_perms(struct udev_rules *rules); | ||
70 | |||
71 | /* udev-event.c */ | ||
72 | struct udev_event *udev_event_new(struct udev_device *dev); | ||
73 | void udev_event_unref(struct udev_event *event); | ||
74 | size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size); | ||
75 | int udev_event_apply_subsys_kernel(struct udev_event *event, const char *string, | ||
76 | char *result, size_t maxsize, int read_value); | ||
77 | int udev_event_spawn(struct udev_event *event, | ||
78 | const char *cmd, char **envp, const sigset_t *sigmask, | ||
79 | char *result, size_t ressize); | ||
80 | int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules, const sigset_t *sigset); | ||
81 | int udev_event_execute_run(struct udev_event *event, const sigset_t *sigset); | ||
82 | int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]); | ||
83 | |||
84 | /* udev-watch.c */ | ||
85 | int udev_watch_init(struct udev *udev); | ||
86 | void udev_watch_restore(struct udev *udev); | ||
87 | void udev_watch_begin(struct udev *udev, struct udev_device *dev); | ||
88 | void udev_watch_end(struct udev *udev, struct udev_device *dev); | ||
89 | struct udev_device *udev_watch_lookup(struct udev *udev, int wd); | ||
90 | |||
91 | /* udev-node.c */ | ||
92 | void udev_node_add(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid); | ||
93 | void udev_node_remove(struct udev_device *dev); | ||
94 | void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old); | ||
95 | |||
96 | /* udev-ctrl.c */ | ||
97 | struct udev_ctrl; | ||
98 | struct udev_ctrl *udev_ctrl_new(struct udev *udev); | ||
99 | struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd); | ||
100 | int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl); | ||
101 | struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl); | ||
102 | struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl); | ||
103 | int udev_ctrl_cleanup(struct udev_ctrl *uctrl); | ||
104 | struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl); | ||
105 | int udev_ctrl_get_fd(struct udev_ctrl *uctrl); | ||
106 | int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout); | ||
107 | int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout); | ||
108 | int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout); | ||
109 | int udev_ctrl_send_reload(struct udev_ctrl *uctrl, int timeout); | ||
110 | int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout); | ||
111 | int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout); | ||
112 | int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout); | ||
113 | int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout); | ||
114 | struct udev_ctrl_connection; | ||
115 | struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl); | ||
116 | struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn); | ||
117 | struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn); | ||
118 | struct udev_ctrl_msg; | ||
119 | struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn); | ||
120 | struct udev_ctrl_msg *udev_ctrl_msg_ref(struct udev_ctrl_msg *ctrl_msg); | ||
121 | struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg); | ||
122 | int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg); | ||
123 | int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg); | ||
124 | int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg); | ||
125 | int udev_ctrl_get_reload(struct udev_ctrl_msg *ctrl_msg); | ||
126 | int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg); | ||
127 | int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg); | ||
128 | const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg); | ||
129 | int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg); | ||
130 | |||
131 | /* built-in commands */ | ||
132 | enum udev_builtin_cmd { | ||
133 | UDEV_BUILTIN_BLKID, | ||
134 | UDEV_BUILTIN_FIRMWARE, | ||
135 | UDEV_BUILTIN_INPUT_ID, | ||
136 | UDEV_BUILTIN_KMOD, | ||
137 | UDEV_BUILTIN_PATH_ID, | ||
138 | UDEV_BUILTIN_PCI_DB, | ||
139 | UDEV_BUILTIN_USB_DB, | ||
140 | UDEV_BUILTIN_USB_ID, | ||
141 | UDEV_BUILTIN_MAX | ||
142 | }; | ||
143 | struct udev_builtin { | ||
144 | const char *name; | ||
145 | int (*cmd)(struct udev_device *dev, int argc, char *argv[], bool test); | ||
146 | const char *help; | ||
147 | int (*init)(struct udev *udev); | ||
148 | void (*exit)(struct udev *udev); | ||
149 | bool (*validate)(struct udev *udev); | ||
150 | bool run_once; | ||
151 | }; | ||
152 | extern const struct udev_builtin udev_builtin_blkid; | ||
153 | extern const struct udev_builtin udev_builtin_firmware; | ||
154 | extern const struct udev_builtin udev_builtin_input_id; | ||
155 | extern const struct udev_builtin udev_builtin_kmod; | ||
156 | extern const struct udev_builtin udev_builtin_path_id; | ||
157 | extern const struct udev_builtin udev_builtin_pci_db; | ||
158 | extern const struct udev_builtin udev_builtin_usb_db; | ||
159 | extern const struct udev_builtin udev_builtin_usb_id; | ||
160 | int udev_builtin_init(struct udev *udev); | ||
161 | void udev_builtin_exit(struct udev *udev); | ||
162 | enum udev_builtin_cmd udev_builtin_lookup(const char *command); | ||
163 | const char *udev_builtin_name(enum udev_builtin_cmd cmd); | ||
164 | bool udev_builtin_run_once(enum udev_builtin_cmd cmd); | ||
165 | int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test); | ||
166 | void udev_builtin_list(struct udev *udev); | ||
167 | bool udev_builtin_validate(struct udev *udev); | ||
168 | int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val); | ||
169 | |||
170 | /* udev logging */ | ||
171 | void udev_main_log(struct udev *udev, int priority, | ||
172 | const char *file, int line, const char *fn, | ||
173 | const char *format, va_list args); | ||
174 | |||
175 | /* udevadm commands */ | ||
176 | struct udevadm_cmd { | ||
177 | const char *name; | ||
178 | int (*cmd)(struct udev *udev, int argc, char *argv[]); | ||
179 | const char *help; | ||
180 | int debug; | ||
181 | }; | ||
182 | extern const struct udevadm_cmd udevadm_info; | ||
183 | extern const struct udevadm_cmd udevadm_trigger; | ||
184 | extern const struct udevadm_cmd udevadm_settle; | ||
185 | extern const struct udevadm_cmd udevadm_control; | ||
186 | extern const struct udevadm_cmd udevadm_monitor; | ||
187 | extern const struct udevadm_cmd udevadm_test; | ||
188 | extern const struct udevadm_cmd udevadm_test_builtin; | ||
189 | #endif |
File src/udev.pc.in added (mode: 100644) (index 0000000..0b04c02) | |||
1 | Name: udev | ||
2 | Description: udev | ||
3 | Version: @VERSION@ | ||
4 | |||
5 | udevdir=@pkglibexecdir@ |
File src/udev.xml added (mode: 100644) (index 0000000..8eb583a) | |||
1 | <?xml version='1.0'?> | ||
2 | <?xml-stylesheet type="text/xsl" href="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl"?> | ||
3 | <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" | ||
4 | "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> | ||
5 | |||
6 | <refentry id="udev"> | ||
7 | <refentryinfo> | ||
8 | <title>udev</title> | ||
9 | <productname>udev</productname> | ||
10 | </refentryinfo> | ||
11 | |||
12 | <refmeta> | ||
13 | <refentrytitle>udev</refentrytitle> | ||
14 | <manvolnum>7</manvolnum> | ||
15 | </refmeta> | ||
16 | |||
17 | <refnamediv> | ||
18 | <refname>udev</refname> | ||
19 | <refpurpose>Linux dynamic device management</refpurpose> | ||
20 | </refnamediv> | ||
21 | |||
22 | <refsect1><title>Description</title> | ||
23 | <para>udev supplies the system software with device events, manages permissions | ||
24 | of device nodes and may create additional symlinks in the <filename>/dev</filename> | ||
25 | directory, or renames network interfaces. The kernel usually just assigns unpredictable | ||
26 | device names based on the order of discovery. Meaningful symlinks or network device | ||
27 | names provide a way to reliably identify devices based on their properties or | ||
28 | current configuration.</para> | ||
29 | |||
30 | <para>The udev daemon, <citerefentry><refentrytitle>udevd</refentrytitle> | ||
31 | <manvolnum>8</manvolnum></citerefentry>, receives device uevents directly from | ||
32 | the kernel whenever a device is added or removed from the system, or it changes its | ||
33 | state. When udev receives a device event, it matches its configured set of rules | ||
34 | against various device attributes to identify the device. Rules that match may | ||
35 | provide additional device information to be stored in the udev database or | ||
36 | to be used to create meaningful symlink names.</para> | ||
37 | |||
38 | <para>All device information udev processes is stored in the udev database and | ||
39 | sent out to possible event subscribers. Access to all stored data and the event | ||
40 | sources is provided by the library libudev.</para> | ||
41 | </refsect1> | ||
42 | |||
43 | <refsect1><title>Configuration</title> | ||
44 | <para>udev configuration files are placed in <filename>/etc/udev</filename> | ||
45 | and <filename>/usr/lib/udev</filename>. All empty lines or lines beginning with | ||
46 | '#' are ignored.</para> | ||
47 | |||
48 | <refsect2><title>Configuration file</title> | ||
49 | <para>udev expects its main configuration file at <filename>/etc/udev/udev.conf</filename>. | ||
50 | It consists of a set of variables allowing the user to override default udev values. | ||
51 | The following variables can be set:</para> | ||
52 | <variablelist> | ||
53 | <varlistentry> | ||
54 | <term><option>udev_root</option></term> | ||
55 | <listitem> | ||
56 | <para>Specifies where to place the device nodes in the filesystem. | ||
57 | The default value is <filename>/dev</filename>.</para> | ||
58 | </listitem> | ||
59 | </varlistentry> | ||
60 | |||
61 | <varlistentry> | ||
62 | <term><option>udev_log</option></term> | ||
63 | <listitem> | ||
64 | <para>The logging priority. Valid values are the numerical syslog priorities | ||
65 | or their textual representations: <option>err</option>, <option>info</option> | ||
66 | and <option>debug</option>.</para> | ||
67 | </listitem> | ||
68 | </varlistentry> | ||
69 | </variablelist> | ||
70 | </refsect2> | ||
71 | |||
72 | <refsect2><title>Rules files</title> | ||
73 | <para>The udev rules are read from the files located in the | ||
74 | system rules directory <filename>/usr/lib/udev/rules.d</filename>, | ||
75 | the volatile runtime directory <filename>/run/udev/rules.d</filename> | ||
76 | and the local administration directory <filename>/etc/udev/rules.d</filename>. | ||
77 | All rules files are collectively sorted and processed in lexical order, | ||
78 | regardless of the directories in which they live. However, files with | ||
79 | identical file names replace each other. Files in <filename>/etc</filename> | ||
80 | have the highest priority, files in <filename>/run</filename> take precedence | ||
81 | over files with the same name in <filename>/lib</filename>. This can be | ||
82 | used to override a system-supplied rules file with a local file if needed; | ||
83 | a symlink in <filename>/etc</filename> with the same name as a rules file in | ||
84 | <filename>/lib</filename>, pointing to <filename>/dev/null</filename>, | ||
85 | disables the rules file entirely.</para> | ||
86 | |||
87 | <para>Rule files must have the extension <filename>.rules</filename>; other | ||
88 | extensions are ignored.</para> | ||
89 | |||
90 | <para>Every line in the rules file contains at least one key-value pair. | ||
91 | There are two kind of keys: match and assignment. | ||
92 | If all match keys are matching against its value, the rule gets applied and the | ||
93 | assignment keys get the specified value assigned.</para> | ||
94 | |||
95 | <para>A matching rule may rename a network interface, add symlinks | ||
96 | pointing to the device node, or run a specified program as part of | ||
97 | the event handling.</para> | ||
98 | |||
99 | <para>A rule consists of a comma-separated list of one or more key-value pairs. | ||
100 | Each key has a distinct operation, depending on the used operator. Valid | ||
101 | operators are:</para> | ||
102 | <variablelist> | ||
103 | <varlistentry> | ||
104 | <term><option>==</option></term> | ||
105 | <listitem> | ||
106 | <para>Compare for equality.</para> | ||
107 | </listitem> | ||
108 | </varlistentry> | ||
109 | |||
110 | <varlistentry> | ||
111 | <term><option>!=</option></term> | ||
112 | <listitem> | ||
113 | <para>Compare for inequality.</para> | ||
114 | </listitem> | ||
115 | </varlistentry> | ||
116 | |||
117 | <varlistentry> | ||
118 | <term><option>=</option></term> | ||
119 | <listitem> | ||
120 | <para>Assign a value to a key. Keys that represent a list are reset | ||
121 | and only this single value is assigned.</para> | ||
122 | </listitem> | ||
123 | </varlistentry> | ||
124 | |||
125 | <varlistentry> | ||
126 | <term><option>+=</option></term> | ||
127 | <listitem> | ||
128 | <para>Add the value to a key that holds a list of entries.</para> | ||
129 | </listitem> | ||
130 | </varlistentry> | ||
131 | |||
132 | <varlistentry> | ||
133 | <term><option>:=</option></term> | ||
134 | <listitem> | ||
135 | <para>Assign a value to a key finally; disallow any later changes.</para> | ||
136 | </listitem> | ||
137 | </varlistentry> | ||
138 | </variablelist> | ||
139 | |||
140 | <para>The following key names can be used to match against device properties. | ||
141 | Some of the keys also match against properties of the parent devices in sysfs, | ||
142 | not only the device that has generated the event. If multiple keys that match | ||
143 | a parent device are specified in a single rule, all these keys must match at | ||
144 | one and the same parent device.</para> | ||
145 | <variablelist> | ||
146 | <varlistentry> | ||
147 | <term><option>ACTION</option></term> | ||
148 | <listitem> | ||
149 | <para>Match the name of the event action.</para> | ||
150 | </listitem> | ||
151 | </varlistentry> | ||
152 | |||
153 | <varlistentry> | ||
154 | <term><option>DEVPATH</option></term> | ||
155 | <listitem> | ||
156 | <para>Match the devpath of the event device.</para> | ||
157 | </listitem> | ||
158 | </varlistentry> | ||
159 | |||
160 | <varlistentry> | ||
161 | <term><option>KERNEL</option></term> | ||
162 | <listitem> | ||
163 | <para>Match the name of the event device.</para> | ||
164 | </listitem> | ||
165 | </varlistentry> | ||
166 | |||
167 | <varlistentry> | ||
168 | <term><option>NAME</option></term> | ||
169 | <listitem> | ||
170 | <para>Match the name of a network interface. It can be used once the | ||
171 | NAME key has been set in one of the preceding rules.</para> | ||
172 | </listitem> | ||
173 | </varlistentry> | ||
174 | |||
175 | <varlistentry> | ||
176 | <term><option>SYMLINK</option></term> | ||
177 | <listitem> | ||
178 | <para>Match the name of a symlink targeting the node. It can | ||
179 | be used once a SYMLINK key has been set in one of the preceding | ||
180 | rules. There may be multiple symlinks; only one needs to match. | ||
181 | </para> | ||
182 | </listitem> | ||
183 | </varlistentry> | ||
184 | |||
185 | <varlistentry> | ||
186 | <term><option>SUBSYSTEM</option></term> | ||
187 | <listitem> | ||
188 | <para>Match the subsystem of the event device.</para> | ||
189 | </listitem> | ||
190 | </varlistentry> | ||
191 | <varlistentry> | ||
192 | <term><option>DRIVER</option></term> | ||
193 | <listitem> | ||
194 | <para>Match the driver name of the event device. Only set this key for devices | ||
195 | which are bound to a driver at the time the event is generated.</para> | ||
196 | </listitem> | ||
197 | </varlistentry> | ||
198 | <varlistentry> | ||
199 | <term><option>ATTR{<replaceable>filename</replaceable>}</option></term> | ||
200 | <listitem> | ||
201 | <para>Match sysfs attribute values of the event device. Trailing | ||
202 | whitespace in the attribute values is ignored unless the specified match | ||
203 | value itself contains trailing whitespace. | ||
204 | </para> | ||
205 | </listitem> | ||
206 | </varlistentry> | ||
207 | |||
208 | <varlistentry> | ||
209 | <term><option>KERNELS</option></term> | ||
210 | <listitem> | ||
211 | <para>Search the devpath upwards for a matching device name.</para> | ||
212 | </listitem> | ||
213 | </varlistentry> | ||
214 | |||
215 | <varlistentry> | ||
216 | <term><option>SUBSYSTEMS</option></term> | ||
217 | <listitem> | ||
218 | <para>Search the devpath upwards for a matching device subsystem name.</para> | ||
219 | </listitem> | ||
220 | </varlistentry> | ||
221 | |||
222 | <varlistentry> | ||
223 | <term><option>DRIVERS</option></term> | ||
224 | <listitem> | ||
225 | <para>Search the devpath upwards for a matching device driver name.</para> | ||
226 | </listitem> | ||
227 | </varlistentry> | ||
228 | |||
229 | <varlistentry> | ||
230 | <term><option>ATTRS{<replaceable>filename</replaceable>}</option></term> | ||
231 | <listitem> | ||
232 | <para>Search the devpath upwards for a device with matching sysfs attribute values. | ||
233 | If multiple <option>ATTRS</option> matches are specified, all of them | ||
234 | must match on the same device. Trailing whitespace in the attribute values is ignored | ||
235 | unless the specified match value itself contains trailing whitespace.</para> | ||
236 | </listitem> | ||
237 | </varlistentry> | ||
238 | |||
239 | <varlistentry> | ||
240 | <term><option>TAGS</option></term> | ||
241 | <listitem> | ||
242 | <para>Search the devpath upwards for a device with matching tag.</para> | ||
243 | </listitem> | ||
244 | </varlistentry> | ||
245 | |||
246 | <varlistentry> | ||
247 | <term><option>ENV{<replaceable>key</replaceable>}</option></term> | ||
248 | <listitem> | ||
249 | <para>Match against a device property value.</para> | ||
250 | </listitem> | ||
251 | </varlistentry> | ||
252 | |||
253 | <varlistentry> | ||
254 | <term><option>TAG</option></term> | ||
255 | <listitem> | ||
256 | <para>Match against a device tag.</para> | ||
257 | </listitem> | ||
258 | </varlistentry> | ||
259 | |||
260 | <varlistentry> | ||
261 | <term><option>TEST{<replaceable>octal mode mask</replaceable>}</option></term> | ||
262 | <listitem> | ||
263 | <para>Test the existence of a file. An octal mode mask can be specified | ||
264 | if needed.</para> | ||
265 | </listitem> | ||
266 | </varlistentry> | ||
267 | |||
268 | <varlistentry> | ||
269 | <term><option>PROGRAM</option></term> | ||
270 | <listitem> | ||
271 | <para>Execute a program to determine whether there | ||
272 | is a match; the key is true if the program returns | ||
273 | successfully. The device properties are made available to the | ||
274 | executed program in the environment. The program's stdout | ||
275 | is available in the RESULT key.</para> | ||
276 | </listitem> | ||
277 | </varlistentry> | ||
278 | |||
279 | <varlistentry> | ||
280 | <term><option>RESULT</option></term> | ||
281 | <listitem> | ||
282 | <para>Match the returned string of the last PROGRAM call. This key can | ||
283 | be used in the same or in any later rule after a PROGRAM call.</para> | ||
284 | </listitem> | ||
285 | </varlistentry> | ||
286 | </variablelist> | ||
287 | |||
288 | <para>Most of the fields support shell-style pattern matching. The following | ||
289 | pattern characters are supported:</para> | ||
290 | <variablelist> | ||
291 | <varlistentry> | ||
292 | <term><option>*</option></term> | ||
293 | <listitem> | ||
294 | <para>Matches zero or more characters.</para> | ||
295 | </listitem> | ||
296 | </varlistentry> | ||
297 | <varlistentry> | ||
298 | <term><option>?</option></term> | ||
299 | <listitem> | ||
300 | <para>Matches any single character.</para> | ||
301 | </listitem> | ||
302 | </varlistentry> | ||
303 | <varlistentry> | ||
304 | <term><option>[]</option></term> | ||
305 | <listitem> | ||
306 | <para>Matches any single character specified within the brackets. For | ||
307 | example, the pattern string 'tty[SR]' would match either 'ttyS' or 'ttyR'. | ||
308 | Ranges are also supported via the '-' character. | ||
309 | For example, to match on the range of all digits, the pattern [0-9] could | ||
310 | be used. If the first character following the '[' is a '!', any characters | ||
311 | not enclosed are matched.</para> | ||
312 | </listitem> | ||
313 | </varlistentry> | ||
314 | </variablelist> | ||
315 | |||
316 | <para>The following keys can get values assigned:</para> | ||
317 | <variablelist> | ||
318 | <varlistentry> | ||
319 | <term><option>NAME</option></term> | ||
320 | <listitem> | ||
321 | <para>The name to use for a network interface. The name of a device node | ||
322 | can not be changed by udev, only additional symlinks can be created.</para> | ||
323 | </listitem> | ||
324 | </varlistentry> | ||
325 | |||
326 | <varlistentry> | ||
327 | <term><option>SYMLINK</option></term> | ||
328 | <listitem> | ||
329 | <para>The name of a symlink targeting the node. Every matching rule adds | ||
330 | this value to the list of symlinks to be created. Multiple symlinks may be | ||
331 | specified by separating the names by the space character. In case multiple | ||
332 | devices claim the same name, the link always points to the device with | ||
333 | the highest link_priority. If the current device goes away, the links are | ||
334 | re-evaluated and the device with the next highest link_priority becomes the owner of | ||
335 | the link. If no link_priority is specified, the order of the devices (and | ||
336 | which one of them owns the link) is undefined. Also, symlink names must | ||
337 | never conflict with the kernel's default device node names, as that would | ||
338 | result in unpredictable behavior. | ||
339 | </para> | ||
340 | </listitem> | ||
341 | </varlistentry> | ||
342 | |||
343 | <varlistentry> | ||
344 | <term><option>OWNER, GROUP, MODE</option></term> | ||
345 | <listitem> | ||
346 | <para>The permissions for the device node. Every specified value overrides | ||
347 | the compiled-in default value.</para> | ||
348 | </listitem> | ||
349 | </varlistentry> | ||
350 | |||
351 | <varlistentry> | ||
352 | <term><option>ATTR{<replaceable>key</replaceable>}</option></term> | ||
353 | <listitem> | ||
354 | <para>The value that should be written to a sysfs attribute of the | ||
355 | event device.</para> | ||
356 | </listitem> | ||
357 | </varlistentry> | ||
358 | |||
359 | <varlistentry> | ||
360 | <term><option>ENV{<replaceable>key</replaceable>}</option></term> | ||
361 | <listitem> | ||
362 | <para>Set a device property value. Property names with a leading '.' | ||
363 | are neither stored in the database nor exported to events or | ||
364 | external tools (run by, say, the PROGRAM match key).</para> | ||
365 | </listitem> | ||
366 | </varlistentry> | ||
367 | |||
368 | <varlistentry> | ||
369 | <term><option>TAG</option></term> | ||
370 | <listitem> | ||
371 | <para>Attach a tag to a device. This is used to filter events for users | ||
372 | of libudev's monitor functionality, or to enumerate a group of tagged | ||
373 | devices. The implementation can only work efficiently if only a few | ||
374 | tags are attached to a device. It is only meant to be used in | ||
375 | contexts with specific device filter requirements, and not as a | ||
376 | general-purpose flag. Excessive use might result in inefficient event | ||
377 | handling.</para> | ||
378 | </listitem> | ||
379 | </varlistentry> | ||
380 | |||
381 | <varlistentry> | ||
382 | <term><option>RUN</option></term> | ||
383 | <listitem> | ||
384 | <para>Add a program to the list of programs to be executed for a specific | ||
385 | device.</para> | ||
386 | <para>If no absolute path is given, the program is expected to live in | ||
387 | /usr/lib/udev, otherwise the absolute path must be specified. The program | ||
388 | name and following arguments are separated by spaces. Single quotes can | ||
389 | be used to specify arguments with spaces.</para> | ||
390 | <para>This can only be used for very short running tasks. Running an | ||
391 | event process for a long period of time may block all further events for | ||
392 | this or a dependent device. Starting daemons or other long running processes | ||
393 | is not appropriate for udev.</para> | ||
394 | </listitem> | ||
395 | </varlistentry> | ||
396 | |||
397 | <varlistentry> | ||
398 | <term><option>LABEL</option></term> | ||
399 | <listitem> | ||
400 | <para>A named label to which a GOTO may jump.</para> | ||
401 | </listitem> | ||
402 | </varlistentry> | ||
403 | |||
404 | <varlistentry> | ||
405 | <term><option>GOTO</option></term> | ||
406 | <listitem> | ||
407 | <para>Jumps to the next LABEL with a matching name.</para> | ||
408 | </listitem> | ||
409 | </varlistentry> | ||
410 | |||
411 | <varlistentry> | ||
412 | <term><option>IMPORT{<replaceable>type</replaceable>}</option></term> | ||
413 | <listitem> | ||
414 | <para>Import a set of variables as device properties, | ||
415 | depending on <replaceable>type</replaceable>:</para> | ||
416 | <variablelist> | ||
417 | <varlistentry> | ||
418 | <term><option>program</option></term> | ||
419 | <listitem> | ||
420 | <para>Execute an external program specified as the assigned value and | ||
421 | import its output, which must be in environment key | ||
422 | format. Path specification, command/argument separation, | ||
423 | and quoting work like in <option>RUN</option>.</para> | ||
424 | </listitem> | ||
425 | </varlistentry> | ||
426 | <varlistentry> | ||
427 | <term><option>file</option></term> | ||
428 | <listitem> | ||
429 | <para>Import a text file specified as the assigned value, the content | ||
430 | of which must be in environment key format.</para> | ||
431 | </listitem> | ||
432 | </varlistentry> | ||
433 | <varlistentry> | ||
434 | <term><option>db</option></term> | ||
435 | <listitem> | ||
436 | <para>Import a single property specified as the assigned value from the | ||
437 | current device database. This works only if the database is already populated | ||
438 | by an earlier event.</para> | ||
439 | </listitem> | ||
440 | </varlistentry> | ||
441 | <varlistentry> | ||
442 | <term><option>cmdline</option></term> | ||
443 | <listitem> | ||
444 | <para>Import a single property from the kernel command line. For simple flags | ||
445 | the value of the property is set to '1'.</para> | ||
446 | </listitem> | ||
447 | </varlistentry> | ||
448 | <varlistentry> | ||
449 | <term><option>parent</option></term> | ||
450 | <listitem> | ||
451 | <para>Import the stored keys from the parent device by reading | ||
452 | the database entry of the parent device. The value assigned to | ||
453 | <option>IMPORT{parent}</option> is used as a filter of key names | ||
454 | to import (with the same shell-style pattern matching used for | ||
455 | comparisons).</para> | ||
456 | </listitem> | ||
457 | </varlistentry> | ||
458 | </variablelist> | ||
459 | </listitem> | ||
460 | </varlistentry> | ||
461 | |||
462 | <varlistentry> | ||
463 | <term><option>WAIT_FOR</option></term> | ||
464 | <listitem> | ||
465 | <para>Wait for a file to become available or until a timeout of | ||
466 | 10 seconds expires. The path is relative to the sysfs device; | ||
467 | if no path is specified, this waits for an attribute to appear.</para> | ||
468 | </listitem> | ||
469 | </varlistentry> | ||
470 | |||
471 | <varlistentry> | ||
472 | <term><option>OPTIONS</option></term> | ||
473 | <listitem> | ||
474 | <para>Rule and device options:</para> | ||
475 | <variablelist> | ||
476 | <varlistentry> | ||
477 | <term><option>link_priority=<replaceable>value</replaceable></option></term> | ||
478 | <listitem> | ||
479 | <para>Specify the priority of the created symlinks. Devices with higher | ||
480 | priorities overwrite existing symlinks of other devices. The default is 0.</para> | ||
481 | </listitem> | ||
482 | </varlistentry> | ||
483 | <varlistentry> | ||
484 | <term><option>event_timeout=</option></term> | ||
485 | <listitem> | ||
486 | <para>Number of seconds an event waits for operations to finish before | ||
487 | giving up and terminating itself.</para> | ||
488 | </listitem> | ||
489 | </varlistentry> | ||
490 | <varlistentry> | ||
491 | <term><option>string_escape=<replaceable>none|replace</replaceable></option></term> | ||
492 | <listitem> | ||
493 | <para>Usually control and other possibly unsafe characters are replaced | ||
494 | in strings used for device naming. The mode of replacement can be specified | ||
495 | with this option.</para> | ||
496 | </listitem> | ||
497 | </varlistentry> | ||
498 | <varlistentry> | ||
499 | <term><option>static_node=</option></term> | ||
500 | <listitem> | ||
501 | <para>Apply the permissions specified in this rule to the static device node with | ||
502 | the specified name. Static device nodes might be provided by kernel modules | ||
503 | or copied from <filename>/usr/lib/udev/devices</filename>. These nodes might not have | ||
504 | a corresponding kernel device at the time udevd is started; they can trigger | ||
505 | automatic kernel module loading.</para> | ||
506 | </listitem> | ||
507 | </varlistentry> | ||
508 | <varlistentry> | ||
509 | <term><option>watch</option></term> | ||
510 | <listitem> | ||
511 | <para>Watch the device node with inotify; when the node is closed after being opened for | ||
512 | writing, a change uevent is synthesized.</para> | ||
513 | </listitem> | ||
514 | </varlistentry> | ||
515 | <varlistentry> | ||
516 | <term><option>nowatch</option></term> | ||
517 | <listitem> | ||
518 | <para>Disable the watching of a device node with inotify.</para> | ||
519 | </listitem> | ||
520 | </varlistentry> | ||
521 | </variablelist> | ||
522 | </listitem> | ||
523 | </varlistentry> | ||
524 | </variablelist> | ||
525 | |||
526 | <para>The <option>NAME</option>, <option>SYMLINK</option>, <option>PROGRAM</option>, | ||
527 | <option>OWNER</option>, <option>GROUP</option>, <option>MODE</option> and <option>RUN</option> | ||
528 | fields support simple string substitutions. The <option>RUN</option> | ||
529 | substitutions are performed after all rules have been processed, right before the program | ||
530 | is executed, allowing for the use of device properties set by earlier matching | ||
531 | rules. For all other fields, substitutions are performed while the individual rule is | ||
532 | being processed. The available substitutions are:</para> | ||
533 | <variablelist> | ||
534 | <varlistentry> | ||
535 | <term><option>$kernel</option>, <option>%k</option></term> | ||
536 | <listitem> | ||
537 | <para>The kernel name for this device.</para> | ||
538 | </listitem> | ||
539 | </varlistentry> | ||
540 | |||
541 | <varlistentry> | ||
542 | <term><option>$number</option>, <option>%n</option></term> | ||
543 | <listitem> | ||
544 | <para>The kernel number for this device. For example, 'sda3' has | ||
545 | kernel number of '3'</para> | ||
546 | </listitem> | ||
547 | </varlistentry> | ||
548 | |||
549 | <varlistentry> | ||
550 | <term><option>$devpath</option>, <option>%p</option></term> | ||
551 | <listitem> | ||
552 | <para>The devpath of the device.</para> | ||
553 | </listitem> | ||
554 | </varlistentry> | ||
555 | |||
556 | <varlistentry> | ||
557 | <term><option>$id</option>, <option>%b</option></term> | ||
558 | <listitem> | ||
559 | <para>The name of the device matched while searching the devpath upwards for | ||
560 | <option>SUBSYSTEMS</option>, <option>KERNELS</option>, <option>DRIVERS</option> and <option>ATTRS</option>. | ||
561 | </para> | ||
562 | </listitem> | ||
563 | </varlistentry> | ||
564 | |||
565 | <varlistentry> | ||
566 | <term><option>$driver</option></term> | ||
567 | <listitem> | ||
568 | <para>The driver name of the device matched while searching the devpath upwards for | ||
569 | <option>SUBSYSTEMS</option>, <option>KERNELS</option>, <option>DRIVERS</option> and <option>ATTRS</option>. | ||
570 | </para> | ||
571 | </listitem> | ||
572 | </varlistentry> | ||
573 | |||
574 | <varlistentry> | ||
575 | <term><option>$attr{<replaceable>file</replaceable>}</option>, <option>%s{<replaceable>file</replaceable>}</option></term> | ||
576 | <listitem> | ||
577 | <para>The value of a sysfs attribute found at the device where | ||
578 | all keys of the rule have matched. If the matching device does not have | ||
579 | such an attribute, and a previous KERNELS, SUBSYSTEMS, DRIVERS, or | ||
580 | ATTRS test selected a parent device, then the attribute from that | ||
581 | parent device is used.</para> | ||
582 | <para>If the attribute is a symlink, the last element of the symlink target is | ||
583 | returned as the value.</para> | ||
584 | </listitem> | ||
585 | </varlistentry> | ||
586 | |||
587 | <varlistentry> | ||
588 | <term><option>$env{<replaceable>key</replaceable>}</option>, <option>%E{<replaceable>key</replaceable>}</option></term> | ||
589 | <listitem> | ||
590 | <para>A device property value.</para> | ||
591 | </listitem> | ||
592 | </varlistentry> | ||
593 | |||
594 | <varlistentry> | ||
595 | <term><option>$major</option>, <option>%M</option></term> | ||
596 | <listitem> | ||
597 | <para>The kernel major number for the device.</para> | ||
598 | </listitem> | ||
599 | </varlistentry> | ||
600 | |||
601 | <varlistentry> | ||
602 | <term><option>$minor</option>, <option>%m</option></term> | ||
603 | <listitem> | ||
604 | <para>The kernel minor number for the device.</para> | ||
605 | </listitem> | ||
606 | </varlistentry> | ||
607 | |||
608 | <varlistentry> | ||
609 | <term><option>$result</option>, <option>%c</option></term> | ||
610 | <listitem> | ||
611 | <para>The string returned by the external program requested with PROGRAM. | ||
612 | A single part of the string, separated by a space character, may be selected | ||
613 | by specifying the part number as an attribute: <option>%c{N}</option>. | ||
614 | If the number is followed by the '+' character, this part plus all remaining parts | ||
615 | of the result string are substituted: <option>%c{N+}</option></para> | ||
616 | </listitem> | ||
617 | </varlistentry> | ||
618 | |||
619 | <varlistentry> | ||
620 | <term><option>$parent</option>, <option>%P</option></term> | ||
621 | <listitem> | ||
622 | <para>The node name of the parent device.</para> | ||
623 | </listitem> | ||
624 | </varlistentry> | ||
625 | |||
626 | <varlistentry> | ||
627 | <term><option>$name</option></term> | ||
628 | <listitem> | ||
629 | <para>The current name of the device. If not changed by a rule, it is the | ||
630 | name of the kernel device.</para> | ||
631 | </listitem> | ||
632 | </varlistentry> | ||
633 | |||
634 | <varlistentry> | ||
635 | <term><option>$links</option></term> | ||
636 | <listitem> | ||
637 | <para>A space-separated list of the current symlinks. The value is | ||
638 | only set during a remove event or if an earlier rule assigned a value.</para> | ||
639 | </listitem> | ||
640 | </varlistentry> | ||
641 | |||
642 | <varlistentry> | ||
643 | <term><option>$root</option>, <option>%r</option></term> | ||
644 | <listitem> | ||
645 | <para>The udev_root value.</para> | ||
646 | </listitem> | ||
647 | </varlistentry> | ||
648 | |||
649 | <varlistentry> | ||
650 | <term><option>$sys</option>, <option>%S</option></term> | ||
651 | <listitem> | ||
652 | <para>The sysfs mount point.</para> | ||
653 | </listitem> | ||
654 | </varlistentry> | ||
655 | |||
656 | <varlistentry> | ||
657 | <term><option>$devnode</option>, <option>%N</option></term> | ||
658 | <listitem> | ||
659 | <para>The name of the device node.</para> | ||
660 | </listitem> | ||
661 | </varlistentry> | ||
662 | |||
663 | <varlistentry> | ||
664 | <term><option>%%</option></term> | ||
665 | <listitem> | ||
666 | <para>The '%' character itself.</para> | ||
667 | </listitem> | ||
668 | </varlistentry> | ||
669 | |||
670 | <varlistentry> | ||
671 | <term><option>$$</option></term> | ||
672 | <listitem> | ||
673 | <para>The '$' character itself.</para> | ||
674 | </listitem> | ||
675 | </varlistentry> | ||
676 | </variablelist> | ||
677 | </refsect2> | ||
678 | </refsect1> | ||
679 | |||
680 | <refsect1><title>Author</title> | ||
681 | <para>Written by Greg Kroah-Hartman <email>greg@kroah.com</email> and | ||
682 | Kay Sievers <email>kay.sievers@vrfy.org</email>. With much help from | ||
683 | Dan Stekloff and many others.</para> | ||
684 | </refsect1> | ||
685 | |||
686 | <refsect1> | ||
687 | <title>See Also</title> | ||
688 | <para><citerefentry> | ||
689 | <refentrytitle>udevd</refentrytitle><manvolnum>8</manvolnum> | ||
690 | </citerefentry>, | ||
691 | <citerefentry> | ||
692 | <refentrytitle>udevadm</refentrytitle><manvolnum>8</manvolnum> | ||
693 | </citerefentry></para> | ||
694 | </refsect1> | ||
695 | </refentry> |
File src/udevadm-control.c added (mode: 100644) (index 0000000..cafa214) | |||
1 | /* | ||
2 | * Copyright (C) 2005-2011 Kay Sievers <kay.sievers@vrfy.org> | ||
3 | * | ||
4 | * This program is free software: you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation, either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | */ | ||
14 | |||
15 | #include <time.h> | ||
16 | #include <errno.h> | ||
17 | #include <stdio.h> | ||
18 | #include <stdlib.h> | ||
19 | #include <stddef.h> | ||
20 | #include <string.h> | ||
21 | #include <unistd.h> | ||
22 | #include <getopt.h> | ||
23 | #include <sys/types.h> | ||
24 | #include <sys/socket.h> | ||
25 | #include <sys/wait.h> | ||
26 | #include <sys/un.h> | ||
27 | |||
28 | #include "udev.h" | ||
29 | |||
30 | static void print_help(void) | ||
31 | { | ||
32 | printf("Usage: udevadm control COMMAND\n" | ||
33 | " --exit instruct the daemon to cleanup and exit\n" | ||
34 | " --log-priority=<level> set the udev log level for the daemon\n" | ||
35 | " --stop-exec-queue do not execute events, queue only\n" | ||
36 | " --start-exec-queue execute events, flush queue\n" | ||
37 | " --reload reload rules and databases\n" | ||
38 | " --property=<KEY>=<value> set a global property for all events\n" | ||
39 | " --children-max=<N> maximum number of children\n" | ||
40 | " --timeout=<seconds> maximum time to block for a reply\n" | ||
41 | " --help print this help text\n\n"); | ||
42 | } | ||
43 | |||
44 | static int adm_control(struct udev *udev, int argc, char *argv[]) | ||
45 | { | ||
46 | struct udev_ctrl *uctrl = NULL; | ||
47 | int timeout = 60; | ||
48 | int rc = 1; | ||
49 | |||
50 | static const struct option options[] = { | ||
51 | { "exit", no_argument, NULL, 'e' }, | ||
52 | { "log-priority", required_argument, NULL, 'l' }, | ||
53 | { "stop-exec-queue", no_argument, NULL, 's' }, | ||
54 | { "start-exec-queue", no_argument, NULL, 'S' }, | ||
55 | { "reload", no_argument, NULL, 'R' }, | ||
56 | { "reload-rules", no_argument, NULL, 'R' }, | ||
57 | { "property", required_argument, NULL, 'p' }, | ||
58 | { "env", required_argument, NULL, 'p' }, | ||
59 | { "children-max", required_argument, NULL, 'm' }, | ||
60 | { "timeout", required_argument, NULL, 't' }, | ||
61 | { "help", no_argument, NULL, 'h' }, | ||
62 | {} | ||
63 | }; | ||
64 | |||
65 | if (getuid() != 0) { | ||
66 | fprintf(stderr, "root privileges required\n"); | ||
67 | return 1; | ||
68 | } | ||
69 | |||
70 | uctrl = udev_ctrl_new(udev); | ||
71 | if (uctrl == NULL) | ||
72 | return 2; | ||
73 | |||
74 | for (;;) { | ||
75 | int option; | ||
76 | |||
77 | option = getopt_long(argc, argv, "el:sSRp:m:h", options, NULL); | ||
78 | if (option == -1) | ||
79 | break; | ||
80 | |||
81 | switch (option) { | ||
82 | case 'e': | ||
83 | if (udev_ctrl_send_exit(uctrl, timeout) < 0) | ||
84 | rc = 2; | ||
85 | else | ||
86 | rc = 0; | ||
87 | break; | ||
88 | case 'l': { | ||
89 | int i; | ||
90 | |||
91 | i = util_log_priority(optarg); | ||
92 | if (i < 0) { | ||
93 | fprintf(stderr, "invalid number '%s'\n", optarg); | ||
94 | goto out; | ||
95 | } | ||
96 | if (udev_ctrl_send_set_log_level(uctrl, util_log_priority(optarg), timeout) < 0) | ||
97 | rc = 2; | ||
98 | else | ||
99 | rc = 0; | ||
100 | break; | ||
101 | } | ||
102 | case 's': | ||
103 | if (udev_ctrl_send_stop_exec_queue(uctrl, timeout) < 0) | ||
104 | rc = 2; | ||
105 | else | ||
106 | rc = 0; | ||
107 | break; | ||
108 | case 'S': | ||
109 | if (udev_ctrl_send_start_exec_queue(uctrl, timeout) < 0) | ||
110 | rc = 2; | ||
111 | else | ||
112 | rc = 0; | ||
113 | break; | ||
114 | case 'R': | ||
115 | if (udev_ctrl_send_reload(uctrl, timeout) < 0) | ||
116 | rc = 2; | ||
117 | else | ||
118 | rc = 0; | ||
119 | break; | ||
120 | case 'p': | ||
121 | if (strchr(optarg, '=') == NULL) { | ||
122 | fprintf(stderr, "expect <KEY>=<value> instead of '%s'\n", optarg); | ||
123 | goto out; | ||
124 | } | ||
125 | if (udev_ctrl_send_set_env(uctrl, optarg, timeout) < 0) | ||
126 | rc = 2; | ||
127 | else | ||
128 | rc = 0; | ||
129 | break; | ||
130 | case 'm': { | ||
131 | char *endp; | ||
132 | int i; | ||
133 | |||
134 | i = strtoul(optarg, &endp, 0); | ||
135 | if (endp[0] != '\0' || i < 1) { | ||
136 | fprintf(stderr, "invalid number '%s'\n", optarg); | ||
137 | goto out; | ||
138 | } | ||
139 | if (udev_ctrl_send_set_children_max(uctrl, i, timeout) < 0) | ||
140 | rc = 2; | ||
141 | else | ||
142 | rc = 0; | ||
143 | break; | ||
144 | } | ||
145 | case 't': { | ||
146 | int seconds; | ||
147 | |||
148 | seconds = atoi(optarg); | ||
149 | if (seconds >= 0) | ||
150 | timeout = seconds; | ||
151 | else | ||
152 | fprintf(stderr, "invalid timeout value\n"); | ||
153 | break; | ||
154 | } | ||
155 | case 'h': | ||
156 | print_help(); | ||
157 | rc = 0; | ||
158 | break; | ||
159 | } | ||
160 | } | ||
161 | |||
162 | if (argv[optind] != NULL) | ||
163 | fprintf(stderr, "unknown option\n"); | ||
164 | else if (optind == 1) | ||
165 | fprintf(stderr, "missing option\n"); | ||
166 | out: | ||
167 | udev_ctrl_unref(uctrl); | ||
168 | return rc; | ||
169 | } | ||
170 | |||
171 | const struct udevadm_cmd udevadm_control = { | ||
172 | .name = "control", | ||
173 | .cmd = adm_control, | ||
174 | .help = "control the udev daemon", | ||
175 | }; |
File src/udevadm-info.c added (mode: 100644) (index 0000000..bbb89f5) | |||
1 | /* | ||
2 | * Copyright (C) 2004-2009 Kay Sievers <kay.sievers@vrfy.org> | ||
3 | * | ||
4 | * This program is free software: you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation, either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #include <stdlib.h> | ||
19 | #include <string.h> | ||
20 | #include <stdio.h> | ||
21 | #include <stddef.h> | ||
22 | #include <ctype.h> | ||
23 | #include <stdarg.h> | ||
24 | #include <unistd.h> | ||
25 | #include <dirent.h> | ||
26 | #include <errno.h> | ||
27 | #include <getopt.h> | ||
28 | #include <fcntl.h> | ||
29 | #include <sys/stat.h> | ||
30 | #include <sys/types.h> | ||
31 | |||
32 | #include "udev.h" | ||
33 | |||
34 | static bool skip_attribute(const char *name) | ||
35 | { | ||
36 | static const char const *skip[] = { | ||
37 | "uevent", | ||
38 | "dev", | ||
39 | "modalias", | ||
40 | "resource", | ||
41 | "driver", | ||
42 | "subsystem", | ||
43 | "module", | ||
44 | }; | ||
45 | unsigned int i; | ||
46 | |||
47 | for (i = 0; i < ARRAY_SIZE(skip); i++) | ||
48 | if (strcmp(name, skip[i]) == 0) | ||
49 | return true; | ||
50 | return false; | ||
51 | } | ||
52 | |||
53 | static void print_all_attributes(struct udev_device *device, const char *key) | ||
54 | { | ||
55 | struct udev *udev = udev_device_get_udev(device); | ||
56 | struct udev_list_entry *sysattr; | ||
57 | |||
58 | udev_list_entry_foreach(sysattr, udev_device_get_sysattr_list_entry(device)) { | ||
59 | const char *name; | ||
60 | const char *value; | ||
61 | size_t len; | ||
62 | |||
63 | name = udev_list_entry_get_name(sysattr); | ||
64 | if (skip_attribute(name)) | ||
65 | continue; | ||
66 | |||
67 | value = udev_device_get_sysattr_value(device, name); | ||
68 | if (value == NULL) | ||
69 | continue; | ||
70 | dbg(udev, "attr '%s'='%s'\n", name, value); | ||
71 | |||
72 | /* skip any values that look like a path */ | ||
73 | if (value[0] == '/') | ||
74 | continue; | ||
75 | |||
76 | /* skip nonprintable attributes */ | ||
77 | len = strlen(value); | ||
78 | while (len > 0 && isprint(value[len-1])) | ||
79 | len--; | ||
80 | if (len > 0) { | ||
81 | dbg(udev, "attribute value of '%s' non-printable, skip\n", name); | ||
82 | continue; | ||
83 | } | ||
84 | |||
85 | printf(" %s{%s}==\"%s\"\n", key, name, value); | ||
86 | } | ||
87 | printf("\n"); | ||
88 | } | ||
89 | |||
90 | static int print_device_chain(struct udev_device *device) | ||
91 | { | ||
92 | struct udev_device *device_parent; | ||
93 | const char *str; | ||
94 | |||
95 | printf("\n" | ||
96 | "Udevadm info starts with the device specified by the devpath and then\n" | ||
97 | "walks up the chain of parent devices. It prints for every device\n" | ||
98 | "found, all possible attributes in the udev rules key format.\n" | ||
99 | "A rule to match, can be composed by the attributes of the device\n" | ||
100 | "and the attributes from one single parent device.\n" | ||
101 | "\n"); | ||
102 | |||
103 | printf(" looking at device '%s':\n", udev_device_get_devpath(device)); | ||
104 | printf(" KERNEL==\"%s\"\n", udev_device_get_sysname(device)); | ||
105 | str = udev_device_get_subsystem(device); | ||
106 | if (str == NULL) | ||
107 | str = ""; | ||
108 | printf(" SUBSYSTEM==\"%s\"\n", str); | ||
109 | str = udev_device_get_driver(device); | ||
110 | if (str == NULL) | ||
111 | str = ""; | ||
112 | printf(" DRIVER==\"%s\"\n", str); | ||
113 | print_all_attributes(device, "ATTR"); | ||
114 | |||
115 | device_parent = device; | ||
116 | do { | ||
117 | device_parent = udev_device_get_parent(device_parent); | ||
118 | if (device_parent == NULL) | ||
119 | break; | ||
120 | printf(" looking at parent device '%s':\n", udev_device_get_devpath(device_parent)); | ||
121 | printf(" KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent)); | ||
122 | str = udev_device_get_subsystem(device_parent); | ||
123 | if (str == NULL) | ||
124 | str = ""; | ||
125 | printf(" SUBSYSTEMS==\"%s\"\n", str); | ||
126 | str = udev_device_get_driver(device_parent); | ||
127 | if (str == NULL) | ||
128 | str = ""; | ||
129 | printf(" DRIVERS==\"%s\"\n", str); | ||
130 | print_all_attributes(device_parent, "ATTRS"); | ||
131 | } while (device_parent != NULL); | ||
132 | |||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | static void print_record(struct udev_device *device) | ||
137 | { | ||
138 | size_t len; | ||
139 | const char *str; | ||
140 | int i; | ||
141 | struct udev_list_entry *list_entry; | ||
142 | |||
143 | printf("P: %s\n", udev_device_get_devpath(device)); | ||
144 | |||
145 | len = strlen(udev_get_dev_path(udev_device_get_udev(device))); | ||
146 | str = udev_device_get_devnode(device); | ||
147 | if (str != NULL) | ||
148 | printf("N: %s\n", &str[len+1]); | ||
149 | |||
150 | i = udev_device_get_devlink_priority(device); | ||
151 | if (i != 0) | ||
152 | printf("L: %i\n", i); | ||
153 | |||
154 | udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) { | ||
155 | len = strlen(udev_get_dev_path(udev_device_get_udev(device))); | ||
156 | printf("S: %s\n", &udev_list_entry_get_name(list_entry)[len+1]); | ||
157 | } | ||
158 | |||
159 | udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) | ||
160 | printf("E: %s=%s\n", | ||
161 | udev_list_entry_get_name(list_entry), | ||
162 | udev_list_entry_get_value(list_entry)); | ||
163 | printf("\n"); | ||
164 | } | ||
165 | |||
166 | static int stat_device(const char *name, bool export, const char *prefix) | ||
167 | { | ||
168 | struct stat statbuf; | ||
169 | |||
170 | if (stat(name, &statbuf) != 0) | ||
171 | return -1; | ||
172 | |||
173 | if (export) { | ||
174 | if (prefix == NULL) | ||
175 | prefix = "INFO_"; | ||
176 | printf("%sMAJOR=%d\n" | ||
177 | "%sMINOR=%d\n", | ||
178 | prefix, major(statbuf.st_dev), | ||
179 | prefix, minor(statbuf.st_dev)); | ||
180 | } else | ||
181 | printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev)); | ||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | static int export_devices(struct udev *udev) | ||
186 | { | ||
187 | struct udev_enumerate *udev_enumerate; | ||
188 | struct udev_list_entry *list_entry; | ||
189 | |||
190 | udev_enumerate = udev_enumerate_new(udev); | ||
191 | if (udev_enumerate == NULL) | ||
192 | return -1; | ||
193 | udev_enumerate_scan_devices(udev_enumerate); | ||
194 | udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) { | ||
195 | struct udev_device *device; | ||
196 | |||
197 | device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry)); | ||
198 | if (device != NULL) { | ||
199 | print_record(device); | ||
200 | udev_device_unref(device); | ||
201 | } | ||
202 | } | ||
203 | udev_enumerate_unref(udev_enumerate); | ||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | static void cleanup_dir(DIR *dir, mode_t mask, int depth) | ||
208 | { | ||
209 | struct dirent *dent; | ||
210 | |||
211 | if (depth <= 0) | ||
212 | return; | ||
213 | |||
214 | for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { | ||
215 | struct stat stats; | ||
216 | |||
217 | if (dent->d_name[0] == '.') | ||
218 | continue; | ||
219 | if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0) | ||
220 | continue; | ||
221 | if ((stats.st_mode & mask) != 0) | ||
222 | continue; | ||
223 | if (S_ISDIR(stats.st_mode)) { | ||
224 | DIR *dir2; | ||
225 | |||
226 | dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); | ||
227 | if (dir2 != NULL) { | ||
228 | cleanup_dir(dir2, mask, depth-1); | ||
229 | closedir(dir2); | ||
230 | } | ||
231 | unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR); | ||
232 | } else { | ||
233 | unlinkat(dirfd(dir), dent->d_name, 0); | ||
234 | } | ||
235 | } | ||
236 | } | ||
237 | |||
238 | static void cleanup_db(struct udev *udev) | ||
239 | { | ||
240 | char filename[UTIL_PATH_SIZE]; | ||
241 | DIR *dir; | ||
242 | |||
243 | util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/queue.bin", NULL); | ||
244 | unlink(filename); | ||
245 | |||
246 | util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data", NULL); | ||
247 | dir = opendir(filename); | ||
248 | if (dir != NULL) { | ||
249 | cleanup_dir(dir, S_ISVTX, 1); | ||
250 | closedir(dir); | ||
251 | } | ||
252 | |||
253 | util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/links", NULL); | ||
254 | dir = opendir(filename); | ||
255 | if (dir != NULL) { | ||
256 | cleanup_dir(dir, 0, 2); | ||
257 | closedir(dir); | ||
258 | } | ||
259 | |||
260 | util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/tags", NULL); | ||
261 | dir = opendir(filename); | ||
262 | if (dir != NULL) { | ||
263 | cleanup_dir(dir, 0, 2); | ||
264 | closedir(dir); | ||
265 | } | ||
266 | |||
267 | util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/watch", NULL); | ||
268 | dir = opendir(filename); | ||
269 | if (dir != NULL) { | ||
270 | cleanup_dir(dir, 0, 1); | ||
271 | closedir(dir); | ||
272 | } | ||
273 | |||
274 | util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/firmware-missing", NULL); | ||
275 | dir = opendir(filename); | ||
276 | if (dir != NULL) { | ||
277 | cleanup_dir(dir, 0, 1); | ||
278 | closedir(dir); | ||
279 | } | ||
280 | } | ||
281 | |||
282 | static struct udev_device *find_device(struct udev *udev, const char *id, const char *prefix) | ||
283 | { | ||
284 | char name[UTIL_PATH_SIZE]; | ||
285 | |||
286 | if (prefix && strncmp(id, prefix, strlen(prefix))) { | ||
287 | util_strscpyl(name, sizeof(name), prefix, id, NULL); | ||
288 | id = name; | ||
289 | } | ||
290 | |||
291 | if (strstr(id, udev_get_dev_path(udev)) == id) { | ||
292 | struct stat statbuf; | ||
293 | char type; | ||
294 | |||
295 | if (stat(id, &statbuf) < 0) | ||
296 | return NULL; | ||
297 | |||
298 | if (S_ISBLK(statbuf.st_mode)) | ||
299 | type = 'b'; | ||
300 | else if (S_ISCHR(statbuf.st_mode)) | ||
301 | type = 'c'; | ||
302 | else | ||
303 | return NULL; | ||
304 | |||
305 | return udev_device_new_from_devnum(udev, type, statbuf.st_rdev); | ||
306 | } else if (strstr(id, udev_get_sys_path(udev)) == id) | ||
307 | return udev_device_new_from_syspath(udev, id); | ||
308 | else | ||
309 | return NULL; | ||
310 | } | ||
311 | |||
312 | static int uinfo(struct udev *udev, int argc, char *argv[]) | ||
313 | { | ||
314 | struct udev_device *device = NULL; | ||
315 | bool root = 0; | ||
316 | bool export = 0; | ||
317 | const char *export_prefix = NULL; | ||
318 | char name[UTIL_PATH_SIZE]; | ||
319 | struct udev_list_entry *list_entry; | ||
320 | int rc = 0; | ||
321 | |||
322 | static const struct option options[] = { | ||
323 | { "name", required_argument, NULL, 'n' }, | ||
324 | { "path", required_argument, NULL, 'p' }, | ||
325 | { "query", required_argument, NULL, 'q' }, | ||
326 | { "attribute-walk", no_argument, NULL, 'a' }, | ||
327 | { "cleanup-db", no_argument, NULL, 'c' }, | ||
328 | { "export-db", no_argument, NULL, 'e' }, | ||
329 | { "root", no_argument, NULL, 'r' }, | ||
330 | { "run", no_argument, NULL, 'R' }, | ||
331 | { "device-id-of-file", required_argument, NULL, 'd' }, | ||
332 | { "export", no_argument, NULL, 'x' }, | ||
333 | { "export-prefix", required_argument, NULL, 'P' }, | ||
334 | { "version", no_argument, NULL, 'V' }, | ||
335 | { "help", no_argument, NULL, 'h' }, | ||
336 | {} | ||
337 | }; | ||
338 | |||
339 | static const char usage[] = | ||
340 | "Usage: udevadm info OPTIONS\n" | ||
341 | " --query=<type> query device information:\n" | ||
342 | " name name of device node\n" | ||
343 | " symlink pointing to node\n" | ||
344 | " path sys device path\n" | ||
345 | " property the device properties\n" | ||
346 | " all all values\n" | ||
347 | " --path=<syspath> sys device path used for query or attribute walk\n" | ||
348 | " --name=<name> node or symlink name used for query or attribute walk\n" | ||
349 | " --root prepend dev directory to path names\n" | ||
350 | " --attribute-walk print all key matches while walking along the chain\n" | ||
351 | " of parent devices\n" | ||
352 | " --device-id-of-file=<file> print major:minor of device containing this file\n" | ||
353 | " --export export key/value pairs\n" | ||
354 | " --export-prefix export the key name with a prefix\n" | ||
355 | " --export-db export the content of the udev database\n" | ||
356 | " --cleanup-db cleanup the udev database\n" | ||
357 | " --help\n"; | ||
358 | |||
359 | |||
360 | enum action_type { | ||
361 | ACTION_NONE, | ||
362 | ACTION_QUERY, | ||
363 | ACTION_ATTRIBUTE_WALK, | ||
364 | ACTION_ROOT, | ||
365 | ACTION_DEVICE_ID_FILE, | ||
366 | } action = ACTION_QUERY; | ||
367 | |||
368 | enum query_type { | ||
369 | QUERY_NONE, | ||
370 | QUERY_NAME, | ||
371 | QUERY_PATH, | ||
372 | QUERY_SYMLINK, | ||
373 | QUERY_PROPERTY, | ||
374 | QUERY_ALL, | ||
375 | } query = QUERY_ALL; | ||
376 | |||
377 | for (;;) { | ||
378 | int option; | ||
379 | |||
380 | option = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL); | ||
381 | if (option == -1) | ||
382 | break; | ||
383 | |||
384 | dbg(udev, "option '%c'\n", option); | ||
385 | switch (option) { | ||
386 | case 'n': | ||
387 | if (device != NULL) { | ||
388 | fprintf(stderr, "device already specified\n"); | ||
389 | rc = 2; | ||
390 | goto exit; | ||
391 | } | ||
392 | device = find_device(udev, optarg, udev_get_dev_path(udev)); | ||
393 | if(device == NULL) { | ||
394 | fprintf(stderr, "device node not found\n"); | ||
395 | rc = 2; | ||
396 | goto exit; | ||
397 | } | ||
398 | break; | ||
399 | case 'p': | ||
400 | if (device != NULL) { | ||
401 | fprintf(stderr, "device already specified\n"); | ||
402 | rc = 2; | ||
403 | goto exit; | ||
404 | } | ||
405 | |||
406 | device = find_device(udev, optarg, udev_get_sys_path(udev)); | ||
407 | if (device == NULL) { | ||
408 | fprintf(stderr, "device path not found\n"); | ||
409 | rc = 2; | ||
410 | goto exit; | ||
411 | } | ||
412 | break; | ||
413 | case 'q': | ||
414 | action = ACTION_QUERY; | ||
415 | if (strcmp(optarg, "property") == 0 || strcmp(optarg, "env") == 0) { | ||
416 | query = QUERY_PROPERTY; | ||
417 | } else if (strcmp(optarg, "name") == 0) { | ||
418 | query = QUERY_NAME; | ||
419 | } else if (strcmp(optarg, "symlink") == 0) { | ||
420 | query = QUERY_SYMLINK; | ||
421 | } else if (strcmp(optarg, "path") == 0) { | ||
422 | query = QUERY_PATH; | ||
423 | } else if (strcmp(optarg, "all") == 0) { | ||
424 | query = QUERY_ALL; | ||
425 | } else { | ||
426 | fprintf(stderr, "unknown query type\n"); | ||
427 | rc = 3; | ||
428 | goto exit; | ||
429 | } | ||
430 | break; | ||
431 | case 'r': | ||
432 | if (action == ACTION_NONE) | ||
433 | action = ACTION_ROOT; | ||
434 | root = true; | ||
435 | break; | ||
436 | case 'R': | ||
437 | printf("%s\n", udev_get_run_path(udev)); | ||
438 | goto exit; | ||
439 | case 'd': | ||
440 | action = ACTION_DEVICE_ID_FILE; | ||
441 | util_strscpy(name, sizeof(name), optarg); | ||
442 | break; | ||
443 | case 'a': | ||
444 | action = ACTION_ATTRIBUTE_WALK; | ||
445 | break; | ||
446 | case 'e': | ||
447 | export_devices(udev); | ||
448 | goto exit; | ||
449 | case 'c': | ||
450 | cleanup_db(udev); | ||
451 | goto exit; | ||
452 | case 'x': | ||
453 | export = true; | ||
454 | break; | ||
455 | case 'P': | ||
456 | export_prefix = optarg; | ||
457 | break; | ||
458 | case 'V': | ||
459 | printf("%s\n", VERSION); | ||
460 | goto exit; | ||
461 | case 'h': | ||
462 | printf("%s\n", usage); | ||
463 | goto exit; | ||
464 | default: | ||
465 | rc = 1; | ||
466 | goto exit; | ||
467 | } | ||
468 | } | ||
469 | |||
470 | switch (action) { | ||
471 | case ACTION_QUERY: | ||
472 | if (device == NULL) { | ||
473 | if(!argv[optind]) { | ||
474 | fprintf(stderr, "%s\n", usage); | ||
475 | rc = 2; | ||
476 | goto exit; | ||
477 | } | ||
478 | device = find_device(udev, argv[optind], NULL); | ||
479 | if(device == NULL) { | ||
480 | fprintf(stderr, | ||
481 | "query needs a valid device specified" | ||
482 | "by --path=, --name=, or absolute path" | ||
483 | "in %s or %s\n", udev_get_dev_path(udev), udev_get_sys_path(udev)); | ||
484 | rc = 4; | ||
485 | goto exit; | ||
486 | } | ||
487 | } | ||
488 | |||
489 | switch(query) { | ||
490 | case QUERY_NAME: { | ||
491 | const char *node = udev_device_get_devnode(device); | ||
492 | |||
493 | if (node == NULL) { | ||
494 | fprintf(stderr, "no device node found\n"); | ||
495 | rc = 5; | ||
496 | goto exit; | ||
497 | } | ||
498 | |||
499 | if (root) { | ||
500 | printf("%s\n", udev_device_get_devnode(device)); | ||
501 | } else { | ||
502 | size_t len = strlen(udev_get_dev_path(udev)); | ||
503 | |||
504 | printf("%s\n", &udev_device_get_devnode(device)[len+1]); | ||
505 | } | ||
506 | break; | ||
507 | } | ||
508 | case QUERY_SYMLINK: | ||
509 | list_entry = udev_device_get_devlinks_list_entry(device); | ||
510 | while (list_entry != NULL) { | ||
511 | if (root) { | ||
512 | printf("%s", udev_list_entry_get_name(list_entry)); | ||
513 | } else { | ||
514 | size_t len; | ||
515 | |||
516 | len = strlen(udev_get_dev_path(udev_device_get_udev(device))); | ||
517 | printf("%s", &udev_list_entry_get_name(list_entry)[len+1]); | ||
518 | } | ||
519 | list_entry = udev_list_entry_get_next(list_entry); | ||
520 | if (list_entry != NULL) | ||
521 | printf(" "); | ||
522 | } | ||
523 | printf("\n"); | ||
524 | break; | ||
525 | case QUERY_PATH: | ||
526 | printf("%s\n", udev_device_get_devpath(device)); | ||
527 | goto exit; | ||
528 | case QUERY_PROPERTY: | ||
529 | list_entry = udev_device_get_properties_list_entry(device); | ||
530 | while (list_entry != NULL) { | ||
531 | if (export) { | ||
532 | const char *prefix = export_prefix; | ||
533 | |||
534 | if (prefix == NULL) | ||
535 | prefix = ""; | ||
536 | printf("%s%s='%s'\n", prefix, | ||
537 | udev_list_entry_get_name(list_entry), | ||
538 | udev_list_entry_get_value(list_entry)); | ||
539 | } else { | ||
540 | printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry)); | ||
541 | } | ||
542 | list_entry = udev_list_entry_get_next(list_entry); | ||
543 | } | ||
544 | break; | ||
545 | case QUERY_ALL: | ||
546 | print_record(device); | ||
547 | break; | ||
548 | default: | ||
549 | fprintf(stderr, "unknown query type\n"); | ||
550 | break; | ||
551 | } | ||
552 | break; | ||
553 | case ACTION_ATTRIBUTE_WALK: | ||
554 | if (device == NULL) { | ||
555 | if(argv[optind]) | ||
556 | device = find_device(udev, argv[optind], NULL); | ||
557 | if(device == NULL) { | ||
558 | fprintf(stderr, "query needs a valid device specified by --path= or --name=\n"); | ||
559 | rc = 4; | ||
560 | goto exit; | ||
561 | } | ||
562 | } | ||
563 | print_device_chain(device); | ||
564 | break; | ||
565 | case ACTION_DEVICE_ID_FILE: | ||
566 | if (stat_device(name, export, export_prefix) != 0) | ||
567 | rc = 1; | ||
568 | break; | ||
569 | case ACTION_ROOT: | ||
570 | printf("%s\n", udev_get_dev_path(udev)); | ||
571 | break; | ||
572 | default: | ||
573 | fprintf(stderr, "missing option\n"); | ||
574 | rc = 1; | ||
575 | break; | ||
576 | } | ||
577 | |||
578 | exit: | ||
579 | udev_device_unref(device); | ||
580 | return rc; | ||
581 | } | ||
582 | |||
583 | const struct udevadm_cmd udevadm_info = { | ||
584 | .name = "info", | ||
585 | .cmd = uinfo, | ||
586 | .help = "query sysfs or the udev database", | ||
587 | }; |
File src/udevadm-monitor.c added (mode: 100644) (index 0000000..5997dd8) | |||
1 | /* | ||
2 | * Copyright (C) 2004-2010 Kay Sievers <kay.sievers@vrfy.org> | ||
3 | * | ||
4 | * This program is free software: you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation, either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #include <unistd.h> | ||
19 | #include <stdio.h> | ||
20 | #include <stdlib.h> | ||
21 | #include <stddef.h> | ||
22 | #include <string.h> | ||
23 | #include <fcntl.h> | ||
24 | #include <errno.h> | ||
25 | #include <signal.h> | ||
26 | #include <getopt.h> | ||
27 | #include <time.h> | ||
28 | #include <sys/time.h> | ||
29 | #include <sys/socket.h> | ||
30 | #include <sys/un.h> | ||
31 | #include <sys/epoll.h> | ||
32 | #include <linux/types.h> | ||
33 | #include <linux/netlink.h> | ||
34 | |||
35 | #include "udev.h" | ||
36 | |||
37 | static bool udev_exit; | ||
38 | |||
39 | static void sig_handler(int signum) | ||
40 | { | ||
41 | if (signum == SIGINT || signum == SIGTERM) | ||
42 | udev_exit = true; | ||
43 | } | ||
44 | |||
45 | static void print_device(struct udev_device *device, const char *source, int prop) | ||
46 | { | ||
47 | struct timespec ts; | ||
48 | |||
49 | clock_gettime(CLOCK_MONOTONIC, &ts); | ||
50 | printf("%-6s[%llu.%06u] %-8s %s (%s)\n", | ||
51 | source, | ||
52 | (unsigned long long) ts.tv_sec, (unsigned int) ts.tv_nsec/1000, | ||
53 | udev_device_get_action(device), | ||
54 | udev_device_get_devpath(device), | ||
55 | udev_device_get_subsystem(device)); | ||
56 | if (prop) { | ||
57 | struct udev_list_entry *list_entry; | ||
58 | |||
59 | udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) | ||
60 | printf("%s=%s\n", | ||
61 | udev_list_entry_get_name(list_entry), | ||
62 | udev_list_entry_get_value(list_entry)); | ||
63 | printf("\n"); | ||
64 | } | ||
65 | } | ||
66 | |||
67 | static int adm_monitor(struct udev *udev, int argc, char *argv[]) | ||
68 | { | ||
69 | struct sigaction act; | ||
70 | sigset_t mask; | ||
71 | int option; | ||
72 | bool prop = false; | ||
73 | bool print_kernel = false; | ||
74 | bool print_udev = false; | ||
75 | struct udev_list subsystem_match_list; | ||
76 | struct udev_list tag_match_list; | ||
77 | struct udev_monitor *udev_monitor = NULL; | ||
78 | struct udev_monitor *kernel_monitor = NULL; | ||
79 | int fd_ep = -1; | ||
80 | int fd_kernel = -1, fd_udev = -1; | ||
81 | struct epoll_event ep_kernel, ep_udev; | ||
82 | int rc = 0; | ||
83 | |||
84 | static const struct option options[] = { | ||
85 | { "property", no_argument, NULL, 'p' }, | ||
86 | { "environment", no_argument, NULL, 'e' }, | ||
87 | { "kernel", no_argument, NULL, 'k' }, | ||
88 | { "udev", no_argument, NULL, 'u' }, | ||
89 | { "subsystem-match", required_argument, NULL, 's' }, | ||
90 | { "tag-match", required_argument, NULL, 't' }, | ||
91 | { "help", no_argument, NULL, 'h' }, | ||
92 | {} | ||
93 | }; | ||
94 | |||
95 | udev_list_init(udev, &subsystem_match_list, true); | ||
96 | udev_list_init(udev, &tag_match_list, true); | ||
97 | |||
98 | for (;;) { | ||
99 | option = getopt_long(argc, argv, "pekus:t:h", options, NULL); | ||
100 | if (option == -1) | ||
101 | break; | ||
102 | |||
103 | switch (option) { | ||
104 | case 'p': | ||
105 | case 'e': | ||
106 | prop = true; | ||
107 | break; | ||
108 | case 'k': | ||
109 | print_kernel = true; | ||
110 | break; | ||
111 | case 'u': | ||
112 | print_udev = true; | ||
113 | break; | ||
114 | case 's': | ||
115 | { | ||
116 | char subsys[UTIL_NAME_SIZE]; | ||
117 | char *devtype; | ||
118 | |||
119 | util_strscpy(subsys, sizeof(subsys), optarg); | ||
120 | devtype = strchr(subsys, '/'); | ||
121 | if (devtype != NULL) { | ||
122 | devtype[0] = '\0'; | ||
123 | devtype++; | ||
124 | } | ||
125 | udev_list_entry_add(&subsystem_match_list, subsys, devtype); | ||
126 | break; | ||
127 | } | ||
128 | case 't': | ||
129 | udev_list_entry_add(&tag_match_list, optarg, NULL); | ||
130 | break; | ||
131 | case 'h': | ||
132 | printf("Usage: udevadm monitor [--property] [--kernel] [--udev] [--help]\n" | ||
133 | " --property print the event properties\n" | ||
134 | " --kernel print kernel uevents\n" | ||
135 | " --udev print udev events\n" | ||
136 | " --subsystem-match=<subsystem[/devtype]> filter events by subsystem\n" | ||
137 | " --tag-match=<tag> filter events by tag\n" | ||
138 | " --help\n\n"); | ||
139 | goto out; | ||
140 | default: | ||
141 | rc = 1; | ||
142 | goto out; | ||
143 | } | ||
144 | } | ||
145 | |||
146 | if (!print_kernel && !print_udev) { | ||
147 | print_kernel = true; | ||
148 | print_udev = true; | ||
149 | } | ||
150 | |||
151 | /* set signal handlers */ | ||
152 | memset(&act, 0x00, sizeof(struct sigaction)); | ||
153 | act.sa_handler = sig_handler; | ||
154 | sigemptyset(&act.sa_mask); | ||
155 | act.sa_flags = SA_RESTART; | ||
156 | sigaction(SIGINT, &act, NULL); | ||
157 | sigaction(SIGTERM, &act, NULL); | ||
158 | sigemptyset(&mask); | ||
159 | sigaddset(&mask, SIGINT); | ||
160 | sigaddset(&mask, SIGTERM); | ||
161 | sigprocmask(SIG_UNBLOCK, &mask, NULL); | ||
162 | |||
163 | fd_ep = epoll_create1(EPOLL_CLOEXEC); | ||
164 | if (fd_ep < 0) { | ||
165 | err(udev, "error creating epoll fd: %m\n"); | ||
166 | goto out; | ||
167 | } | ||
168 | |||
169 | printf("monitor will print the received events for:\n"); | ||
170 | if (print_udev) { | ||
171 | struct udev_list_entry *entry; | ||
172 | |||
173 | udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); | ||
174 | if (udev_monitor == NULL) { | ||
175 | fprintf(stderr, "error: unable to create netlink socket\n"); | ||
176 | rc = 1; | ||
177 | goto out; | ||
178 | } | ||
179 | udev_monitor_set_receive_buffer_size(udev_monitor, 128*1024*1024); | ||
180 | fd_udev = udev_monitor_get_fd(udev_monitor); | ||
181 | |||
182 | udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) { | ||
183 | const char *subsys = udev_list_entry_get_name(entry); | ||
184 | const char *devtype = udev_list_entry_get_value(entry); | ||
185 | |||
186 | if (udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, subsys, devtype) < 0) | ||
187 | fprintf(stderr, "error: unable to apply subsystem filter '%s'\n", subsys); | ||
188 | } | ||
189 | |||
190 | udev_list_entry_foreach(entry, udev_list_get_entry(&tag_match_list)) { | ||
191 | const char *tag = udev_list_entry_get_name(entry); | ||
192 | |||
193 | if (udev_monitor_filter_add_match_tag(udev_monitor, tag) < 0) | ||
194 | fprintf(stderr, "error: unable to apply tag filter '%s'\n", tag); | ||
195 | } | ||
196 | |||
197 | if (udev_monitor_enable_receiving(udev_monitor) < 0) { | ||
198 | fprintf(stderr, "error: unable to subscribe to udev events\n"); | ||
199 | rc = 2; | ||
200 | goto out; | ||
201 | } | ||
202 | |||
203 | memset(&ep_udev, 0, sizeof(struct epoll_event)); | ||
204 | ep_udev.events = EPOLLIN; | ||
205 | ep_udev.data.fd = fd_udev; | ||
206 | if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) { | ||
207 | err(udev, "fail to add fd to epoll: %m\n"); | ||
208 | goto out; | ||
209 | } | ||
210 | |||
211 | printf("UDEV - the event which udev sends out after rule processing\n"); | ||
212 | } | ||
213 | |||
214 | if (print_kernel) { | ||
215 | struct udev_list_entry *entry; | ||
216 | |||
217 | kernel_monitor = udev_monitor_new_from_netlink(udev, "kernel"); | ||
218 | if (kernel_monitor == NULL) { | ||
219 | fprintf(stderr, "error: unable to create netlink socket\n"); | ||
220 | rc = 3; | ||
221 | goto out; | ||
222 | } | ||
223 | udev_monitor_set_receive_buffer_size(kernel_monitor, 128*1024*1024); | ||
224 | fd_kernel = udev_monitor_get_fd(kernel_monitor); | ||
225 | |||
226 | udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) { | ||
227 | const char *subsys = udev_list_entry_get_name(entry); | ||
228 | |||
229 | if (udev_monitor_filter_add_match_subsystem_devtype(kernel_monitor, subsys, NULL) < 0) | ||
230 | fprintf(stderr, "error: unable to apply subsystem filter '%s'\n", subsys); | ||
231 | } | ||
232 | |||
233 | if (udev_monitor_enable_receiving(kernel_monitor) < 0) { | ||
234 | fprintf(stderr, "error: unable to subscribe to kernel events\n"); | ||
235 | rc = 4; | ||
236 | goto out; | ||
237 | } | ||
238 | |||
239 | memset(&ep_kernel, 0, sizeof(struct epoll_event)); | ||
240 | ep_kernel.events = EPOLLIN; | ||
241 | ep_kernel.data.fd = fd_kernel; | ||
242 | if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_kernel, &ep_kernel) < 0) { | ||
243 | err(udev, "fail to add fd to epoll: %m\n"); | ||
244 | goto out; | ||
245 | } | ||
246 | |||
247 | printf("KERNEL - the kernel uevent\n"); | ||
248 | } | ||
249 | printf("\n"); | ||
250 | |||
251 | while (!udev_exit) { | ||
252 | int fdcount; | ||
253 | struct epoll_event ev[4]; | ||
254 | int i; | ||
255 | |||
256 | fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1); | ||
257 | if (fdcount < 0) { | ||
258 | if (errno != EINTR) | ||
259 | fprintf(stderr, "error receiving uevent message: %m\n"); | ||
260 | continue; | ||
261 | } | ||
262 | |||
263 | for (i = 0; i < fdcount; i++) { | ||
264 | if (ev[i].data.fd == fd_kernel && ev[i].events & EPOLLIN) { | ||
265 | struct udev_device *device; | ||
266 | |||
267 | device = udev_monitor_receive_device(kernel_monitor); | ||
268 | if (device == NULL) | ||
269 | continue; | ||
270 | print_device(device, "KERNEL", prop); | ||
271 | udev_device_unref(device); | ||
272 | } else if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) { | ||
273 | struct udev_device *device; | ||
274 | |||
275 | device = udev_monitor_receive_device(udev_monitor); | ||
276 | if (device == NULL) | ||
277 | continue; | ||
278 | print_device(device, "UDEV", prop); | ||
279 | udev_device_unref(device); | ||
280 | } | ||
281 | } | ||
282 | } | ||
283 | out: | ||
284 | if (fd_ep >= 0) | ||
285 | close(fd_ep); | ||
286 | udev_monitor_unref(udev_monitor); | ||
287 | udev_monitor_unref(kernel_monitor); | ||
288 | udev_list_cleanup(&subsystem_match_list); | ||
289 | udev_list_cleanup(&tag_match_list); | ||
290 | return rc; | ||
291 | } | ||
292 | |||
293 | const struct udevadm_cmd udevadm_monitor = { | ||
294 | .name = "monitor", | ||
295 | .cmd = adm_monitor, | ||
296 | .help = "listen to kernel and udev events", | ||
297 | }; |
File src/udevadm-settle.c added (mode: 100644) (index 0000000..b168def) | |||
1 | /* | ||
2 | * Copyright (C) 2006-2009 Kay Sievers <kay@vrfy.org> | ||
3 | * Copyright (C) 2009 Canonical Ltd. | ||
4 | * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com> | ||
5 | * | ||
6 | * This program is free software: you can redistribute it and/or modify | ||
7 | * it 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 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | |||
20 | #include <stdlib.h> | ||
21 | #include <stddef.h> | ||
22 | #include <string.h> | ||
23 | #include <stdio.h> | ||
24 | #include <unistd.h> | ||
25 | #include <errno.h> | ||
26 | #include <dirent.h> | ||
27 | #include <fcntl.h> | ||
28 | #include <syslog.h> | ||
29 | #include <getopt.h> | ||
30 | #include <signal.h> | ||
31 | #include <time.h> | ||
32 | #include <sys/inotify.h> | ||
33 | #include <sys/poll.h> | ||
34 | #include <sys/stat.h> | ||
35 | #include <sys/types.h> | ||
36 | |||
37 | #include "udev.h" | ||
38 | |||
39 | static int adm_settle(struct udev *udev, int argc, char *argv[]) | ||
40 | { | ||
41 | static const struct option options[] = { | ||
42 | { "seq-start", required_argument, NULL, 's' }, | ||
43 | { "seq-end", required_argument, NULL, 'e' }, | ||
44 | { "timeout", required_argument, NULL, 't' }, | ||
45 | { "exit-if-exists", required_argument, NULL, 'E' }, | ||
46 | { "quiet", no_argument, NULL, 'q' }, | ||
47 | { "help", no_argument, NULL, 'h' }, | ||
48 | {} | ||
49 | }; | ||
50 | unsigned long long start_usec = now_usec(); | ||
51 | unsigned long long start = 0; | ||
52 | unsigned long long end = 0; | ||
53 | int quiet = 0; | ||
54 | const char *exists = NULL; | ||
55 | unsigned int timeout = 120; | ||
56 | struct pollfd pfd[1]; | ||
57 | struct udev_queue *udev_queue = NULL; | ||
58 | int rc = EXIT_FAILURE; | ||
59 | |||
60 | dbg(udev, "version %s\n", VERSION); | ||
61 | |||
62 | for (;;) { | ||
63 | int option; | ||
64 | int seconds; | ||
65 | |||
66 | option = getopt_long(argc, argv, "s:e:t:E:qh", options, NULL); | ||
67 | if (option == -1) | ||
68 | break; | ||
69 | |||
70 | switch (option) { | ||
71 | case 's': | ||
72 | start = strtoull(optarg, NULL, 0); | ||
73 | break; | ||
74 | case 'e': | ||
75 | end = strtoull(optarg, NULL, 0); | ||
76 | break; | ||
77 | case 't': | ||
78 | seconds = atoi(optarg); | ||
79 | if (seconds >= 0) | ||
80 | timeout = seconds; | ||
81 | else | ||
82 | fprintf(stderr, "invalid timeout value\n"); | ||
83 | dbg(udev, "timeout=%i\n", timeout); | ||
84 | break; | ||
85 | case 'q': | ||
86 | quiet = 1; | ||
87 | break; | ||
88 | case 'E': | ||
89 | exists = optarg; | ||
90 | break; | ||
91 | case 'h': | ||
92 | printf("Usage: udevadm settle OPTIONS\n" | ||
93 | " --timeout=<seconds> maximum time to wait for events\n" | ||
94 | " --seq-start=<seqnum> first seqnum to wait for\n" | ||
95 | " --seq-end=<seqnum> last seqnum to wait for\n" | ||
96 | " --exit-if-exists=<file> stop waiting if file exists\n" | ||
97 | " --quiet do not print list after timeout\n" | ||
98 | " --help\n\n"); | ||
99 | exit(EXIT_SUCCESS); | ||
100 | default: | ||
101 | exit(EXIT_FAILURE); | ||
102 | } | ||
103 | } | ||
104 | |||
105 | udev_queue = udev_queue_new(udev); | ||
106 | if (udev_queue == NULL) | ||
107 | exit(2); | ||
108 | |||
109 | if (start > 0) { | ||
110 | unsigned long long kernel_seq; | ||
111 | |||
112 | kernel_seq = udev_queue_get_kernel_seqnum(udev_queue); | ||
113 | |||
114 | /* unless specified, the last event is the current kernel seqnum */ | ||
115 | if (end == 0) | ||
116 | end = udev_queue_get_kernel_seqnum(udev_queue); | ||
117 | |||
118 | if (start > end) { | ||
119 | err(udev, "seq-start larger than seq-end, ignoring\n"); | ||
120 | start = 0; | ||
121 | end = 0; | ||
122 | } | ||
123 | |||
124 | if (start > kernel_seq || end > kernel_seq) { | ||
125 | err(udev, "seq-start or seq-end larger than current kernel value, ignoring\n"); | ||
126 | start = 0; | ||
127 | end = 0; | ||
128 | } | ||
129 | info(udev, "start=%llu end=%llu current=%llu\n", start, end, kernel_seq); | ||
130 | } else { | ||
131 | if (end > 0) { | ||
132 | err(udev, "seq-end needs seq-start parameter, ignoring\n"); | ||
133 | end = 0; | ||
134 | } | ||
135 | } | ||
136 | |||
137 | /* guarantee that the udev daemon isn't pre-processing */ | ||
138 | if (getuid() == 0) { | ||
139 | struct udev_ctrl *uctrl; | ||
140 | |||
141 | uctrl = udev_ctrl_new(udev); | ||
142 | if (uctrl != NULL) { | ||
143 | if (udev_ctrl_send_ping(uctrl, timeout) < 0) { | ||
144 | info(udev, "no connection to daemon\n"); | ||
145 | udev_ctrl_unref(uctrl); | ||
146 | rc = EXIT_SUCCESS; | ||
147 | goto out; | ||
148 | } | ||
149 | udev_ctrl_unref(uctrl); | ||
150 | } | ||
151 | } | ||
152 | |||
153 | pfd[0].events = POLLIN; | ||
154 | pfd[0].fd = inotify_init1(IN_CLOEXEC); | ||
155 | if (pfd[0].fd < 0) { | ||
156 | err(udev, "inotify_init failed: %m\n"); | ||
157 | } else { | ||
158 | if (inotify_add_watch(pfd[0].fd, udev_get_run_path(udev), IN_MOVED_TO) < 0) { | ||
159 | err(udev, "watching '%s' failed\n", udev_get_run_path(udev)); | ||
160 | close(pfd[0].fd); | ||
161 | pfd[0].fd = -1; | ||
162 | } | ||
163 | } | ||
164 | |||
165 | for (;;) { | ||
166 | struct stat statbuf; | ||
167 | |||
168 | if (exists != NULL && stat(exists, &statbuf) == 0) { | ||
169 | rc = EXIT_SUCCESS; | ||
170 | break; | ||
171 | } | ||
172 | |||
173 | if (start > 0) { | ||
174 | /* if asked for, wait for a specific sequence of events */ | ||
175 | if (udev_queue_get_seqnum_sequence_is_finished(udev_queue, start, end) == 1) { | ||
176 | rc = EXIT_SUCCESS; | ||
177 | break; | ||
178 | } | ||
179 | } else { | ||
180 | /* exit if queue is empty */ | ||
181 | if (udev_queue_get_queue_is_empty(udev_queue)) { | ||
182 | rc = EXIT_SUCCESS; | ||
183 | break; | ||
184 | } | ||
185 | } | ||
186 | |||
187 | if (pfd[0].fd >= 0) { | ||
188 | int delay; | ||
189 | |||
190 | if (exists != NULL || start > 0) | ||
191 | delay = 100; | ||
192 | else | ||
193 | delay = 1000; | ||
194 | /* wake up after delay, or immediately after the queue is rebuilt */ | ||
195 | if (poll(pfd, 1, delay) > 0 && pfd[0].revents & POLLIN) { | ||
196 | char buf[sizeof(struct inotify_event) + PATH_MAX]; | ||
197 | |||
198 | read(pfd[0].fd, buf, sizeof(buf)); | ||
199 | } | ||
200 | } else { | ||
201 | sleep(1); | ||
202 | } | ||
203 | |||
204 | if (timeout > 0) { | ||
205 | unsigned long long age_usec; | ||
206 | |||
207 | age_usec = now_usec() - start_usec; | ||
208 | if (age_usec / (1000 * 1000) >= timeout) { | ||
209 | struct udev_list_entry *list_entry; | ||
210 | |||
211 | if (!quiet && udev_queue_get_queued_list_entry(udev_queue) != NULL) { | ||
212 | info(udev, "timeout waiting for udev queue\n"); | ||
213 | printf("\nudevadm settle - timeout of %i seconds reached, the event queue contains:\n", timeout); | ||
214 | udev_list_entry_foreach(list_entry, udev_queue_get_queued_list_entry(udev_queue)) | ||
215 | printf(" %s (%s)\n", | ||
216 | udev_list_entry_get_name(list_entry), | ||
217 | udev_list_entry_get_value(list_entry)); | ||
218 | } | ||
219 | |||
220 | break; | ||
221 | } | ||
222 | } | ||
223 | } | ||
224 | out: | ||
225 | if (pfd[0].fd >= 0) | ||
226 | close(pfd[0].fd); | ||
227 | udev_queue_unref(udev_queue); | ||
228 | return rc; | ||
229 | } | ||
230 | |||
231 | const struct udevadm_cmd udevadm_settle = { | ||
232 | .name = "settle", | ||
233 | .cmd = adm_settle, | ||
234 | .help = "wait for the event queue to finish", | ||
235 | }; |
File src/udevadm-test-builtin.c added (mode: 100644) (index 0000000..3a49f7c) | |||
1 | /* | ||
2 | * Copyright (C) 2011 Kay Sievers <kay.sievers@vrfy.org> | ||
3 | * | ||
4 | * This program is free software: you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation, either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #include <stdlib.h> | ||
19 | #include <stddef.h> | ||
20 | #include <string.h> | ||
21 | #include <stdio.h> | ||
22 | #include <unistd.h> | ||
23 | #include <errno.h> | ||
24 | #include <dirent.h> | ||
25 | #include <fcntl.h> | ||
26 | #include <syslog.h> | ||
27 | #include <getopt.h> | ||
28 | #include <signal.h> | ||
29 | #include <time.h> | ||
30 | #include <sys/inotify.h> | ||
31 | #include <sys/poll.h> | ||
32 | #include <sys/stat.h> | ||
33 | #include <sys/types.h> | ||
34 | |||
35 | #include "udev.h" | ||
36 | |||
37 | static void help(struct udev *udev) | ||
38 | { | ||
39 | fprintf(stderr, "\n"); | ||
40 | fprintf(stderr, "Usage: udevadm builtin [--help] <command> <syspath>\n"); | ||
41 | udev_builtin_list(udev); | ||
42 | fprintf(stderr, "\n"); | ||
43 | } | ||
44 | |||
45 | static int adm_builtin(struct udev *udev, int argc, char *argv[]) | ||
46 | { | ||
47 | static const struct option options[] = { | ||
48 | { "help", no_argument, NULL, 'h' }, | ||
49 | {} | ||
50 | }; | ||
51 | char *command = NULL; | ||
52 | char *syspath = NULL; | ||
53 | char filename[UTIL_PATH_SIZE]; | ||
54 | struct udev_device *dev = NULL; | ||
55 | enum udev_builtin_cmd cmd; | ||
56 | int rc = EXIT_SUCCESS; | ||
57 | |||
58 | dbg(udev, "version %s\n", VERSION); | ||
59 | |||
60 | for (;;) { | ||
61 | int option; | ||
62 | |||
63 | option = getopt_long(argc, argv, "h", options, NULL); | ||
64 | if (option == -1) | ||
65 | break; | ||
66 | |||
67 | switch (option) { | ||
68 | case 'h': | ||
69 | help(udev); | ||
70 | goto out; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | command = argv[optind++]; | ||
75 | if (command == NULL) { | ||
76 | fprintf(stderr, "command missing\n"); | ||
77 | help(udev); | ||
78 | rc = 2; | ||
79 | goto out; | ||
80 | } | ||
81 | |||
82 | syspath = argv[optind++]; | ||
83 | if (syspath == NULL) { | ||
84 | fprintf(stderr, "syspath missing\n\n"); | ||
85 | rc = 3; | ||
86 | goto out; | ||
87 | } | ||
88 | |||
89 | udev_builtin_init(udev); | ||
90 | |||
91 | cmd = udev_builtin_lookup(command); | ||
92 | if (cmd >= UDEV_BUILTIN_MAX) { | ||
93 | fprintf(stderr, "unknown command '%s'\n", command); | ||
94 | help(udev); | ||
95 | rc = 5; | ||
96 | goto out; | ||
97 | } | ||
98 | |||
99 | /* add /sys if needed */ | ||
100 | if (strncmp(syspath, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0) | ||
101 | util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), syspath, NULL); | ||
102 | else | ||
103 | util_strscpy(filename, sizeof(filename), syspath); | ||
104 | util_remove_trailing_chars(filename, '/'); | ||
105 | |||
106 | dev = udev_device_new_from_syspath(udev, filename); | ||
107 | if (dev == NULL) { | ||
108 | fprintf(stderr, "unable to open device '%s'\n\n", filename); | ||
109 | rc = 4; | ||
110 | goto out; | ||
111 | } | ||
112 | |||
113 | if (udev_builtin_run(dev, cmd, command, true) < 0) { | ||
114 | fprintf(stderr, "error executing '%s'\n\n", command); | ||
115 | rc = 6; | ||
116 | } | ||
117 | out: | ||
118 | udev_device_unref(dev); | ||
119 | udev_builtin_exit(udev); | ||
120 | return rc; | ||
121 | } | ||
122 | |||
123 | const struct udevadm_cmd udevadm_test_builtin = { | ||
124 | .name = "test-builtin", | ||
125 | .cmd = adm_builtin, | ||
126 | .help = "test a built-in command", | ||
127 | .debug = true, | ||
128 | }; |
File src/udevadm-test.c added (mode: 100644) (index 0000000..6275cff) | |||
1 | /* | ||
2 | * Copyright (C) 2003-2004 Greg Kroah-Hartman <greg@kroah.com> | ||
3 | * Copyright (C) 2004-2008 Kay Sievers <kay.sievers@vrfy.org> | ||
4 | * | ||
5 | * This program is free software: you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation, either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | |||
19 | #include <stdlib.h> | ||
20 | #include <string.h> | ||
21 | #include <stdio.h> | ||
22 | #include <stddef.h> | ||
23 | #include <unistd.h> | ||
24 | #include <errno.h> | ||
25 | #include <ctype.h> | ||
26 | #include <fcntl.h> | ||
27 | #include <signal.h> | ||
28 | #include <syslog.h> | ||
29 | #include <getopt.h> | ||
30 | #include <sys/signalfd.h> | ||
31 | |||
32 | #include "udev.h" | ||
33 | |||
34 | static int adm_test(struct udev *udev, int argc, char *argv[]) | ||
35 | { | ||
36 | int resolve_names = 1; | ||
37 | char filename[UTIL_PATH_SIZE]; | ||
38 | const char *action = "add"; | ||
39 | const char *syspath = NULL; | ||
40 | struct udev_event *event = NULL; | ||
41 | struct udev_device *dev = NULL; | ||
42 | struct udev_rules *rules = NULL; | ||
43 | struct udev_list_entry *entry; | ||
44 | sigset_t mask, sigmask_orig; | ||
45 | int err; | ||
46 | int rc = 0; | ||
47 | |||
48 | static const struct option options[] = { | ||
49 | { "action", required_argument, NULL, 'a' }, | ||
50 | { "resolve-names", required_argument, NULL, 'N' }, | ||
51 | { "help", no_argument, NULL, 'h' }, | ||
52 | {} | ||
53 | }; | ||
54 | |||
55 | info(udev, "version %s\n", VERSION); | ||
56 | |||
57 | for (;;) { | ||
58 | int option; | ||
59 | |||
60 | option = getopt_long(argc, argv, "a:s:N:fh", options, NULL); | ||
61 | if (option == -1) | ||
62 | break; | ||
63 | |||
64 | dbg(udev, "option '%c'\n", option); | ||
65 | switch (option) { | ||
66 | case 'a': | ||
67 | action = optarg; | ||
68 | break; | ||
69 | case 'N': | ||
70 | if (strcmp (optarg, "early") == 0) { | ||
71 | resolve_names = 1; | ||
72 | } else if (strcmp (optarg, "late") == 0) { | ||
73 | resolve_names = 0; | ||
74 | } else if (strcmp (optarg, "never") == 0) { | ||
75 | resolve_names = -1; | ||
76 | } else { | ||
77 | fprintf(stderr, "resolve-names must be early, late or never\n"); | ||
78 | err(udev, "resolve-names must be early, late or never\n"); | ||
79 | exit(EXIT_FAILURE); | ||
80 | } | ||
81 | break; | ||
82 | case 'h': | ||
83 | printf("Usage: udevadm test OPTIONS <syspath>\n" | ||
84 | " --action=<string> set action string\n" | ||
85 | " --help\n\n"); | ||
86 | exit(EXIT_SUCCESS); | ||
87 | default: | ||
88 | exit(EXIT_FAILURE); | ||
89 | } | ||
90 | } | ||
91 | syspath = argv[optind]; | ||
92 | |||
93 | if (syspath == NULL) { | ||
94 | fprintf(stderr, "syspath parameter missing\n"); | ||
95 | rc = 2; | ||
96 | goto out; | ||
97 | } | ||
98 | |||
99 | printf("This program is for debugging only, it does not run any program,\n" | ||
100 | "specified by a RUN key. It may show incorrect results, because\n" | ||
101 | "some values may be different, or not available at a simulation run.\n" | ||
102 | "\n"); | ||
103 | |||
104 | sigprocmask(SIG_SETMASK, NULL, &sigmask_orig); | ||
105 | |||
106 | udev_builtin_init(udev); | ||
107 | |||
108 | rules = udev_rules_new(udev, resolve_names); | ||
109 | if (rules == NULL) { | ||
110 | fprintf(stderr, "error reading rules\n"); | ||
111 | rc = 3; | ||
112 | goto out; | ||
113 | } | ||
114 | |||
115 | /* add /sys if needed */ | ||
116 | if (strncmp(syspath, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0) | ||
117 | util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), syspath, NULL); | ||
118 | else | ||
119 | util_strscpy(filename, sizeof(filename), syspath); | ||
120 | util_remove_trailing_chars(filename, '/'); | ||
121 | |||
122 | dev = udev_device_new_from_syspath(udev, filename); | ||
123 | if (dev == NULL) { | ||
124 | fprintf(stderr, "unable to open device '%s'\n", filename); | ||
125 | rc = 4; | ||
126 | goto out; | ||
127 | } | ||
128 | |||
129 | /* skip reading of db, but read kernel parameters */ | ||
130 | udev_device_set_info_loaded(dev); | ||
131 | udev_device_read_uevent_file(dev); | ||
132 | |||
133 | udev_device_set_action(dev, action); | ||
134 | event = udev_event_new(dev); | ||
135 | |||
136 | sigfillset(&mask); | ||
137 | sigprocmask(SIG_SETMASK, &mask, &sigmask_orig); | ||
138 | event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); | ||
139 | if (event->fd_signal < 0) { | ||
140 | fprintf(stderr, "error creating signalfd\n"); | ||
141 | rc = 5; | ||
142 | goto out; | ||
143 | } | ||
144 | |||
145 | err = udev_event_execute_rules(event, rules, &sigmask_orig); | ||
146 | |||
147 | udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev)) | ||
148 | printf("%s=%s\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry)); | ||
149 | |||
150 | if (err == 0) { | ||
151 | udev_list_entry_foreach(entry, udev_list_get_entry(&event->run_list)) { | ||
152 | char program[UTIL_PATH_SIZE]; | ||
153 | |||
154 | udev_event_apply_format(event, udev_list_entry_get_name(entry), program, sizeof(program)); | ||
155 | printf("run: '%s'\n", program); | ||
156 | } | ||
157 | } | ||
158 | out: | ||
159 | if (event != NULL && event->fd_signal >= 0) | ||
160 | close(event->fd_signal); | ||
161 | udev_event_unref(event); | ||
162 | udev_device_unref(dev); | ||
163 | udev_rules_unref(rules); | ||
164 | udev_builtin_exit(udev); | ||
165 | return rc; | ||
166 | } | ||
167 | |||
168 | const struct udevadm_cmd udevadm_test = { | ||
169 | .name = "test", | ||
170 | .cmd = adm_test, | ||
171 | .help = "test an event run", | ||
172 | .debug = true, | ||
173 | }; |
File src/udevadm-trigger.c added (mode: 100644) (index 0000000..3cce23d) | |||
1 | /* | ||
2 | * Copyright (C) 2008-2009 Kay Sievers <kay.sievers@vrfy.org> | ||
3 | * | ||
4 | * This program is free software: you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation, either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #include <stdlib.h> | ||
19 | #include <stddef.h> | ||
20 | #include <string.h> | ||
21 | #include <stdio.h> | ||
22 | #include <unistd.h> | ||
23 | #include <getopt.h> | ||
24 | #include <errno.h> | ||
25 | #include <dirent.h> | ||
26 | #include <fcntl.h> | ||
27 | #include <syslog.h> | ||
28 | #include <fnmatch.h> | ||
29 | #include <sys/stat.h> | ||
30 | #include <sys/types.h> | ||
31 | #include <sys/socket.h> | ||
32 | #include <sys/un.h> | ||
33 | |||
34 | #include "udev.h" | ||
35 | |||
36 | static int verbose; | ||
37 | static int dry_run; | ||
38 | |||
39 | static void exec_list(struct udev_enumerate *udev_enumerate, const char *action) | ||
40 | { | ||
41 | struct udev *udev = udev_enumerate_get_udev(udev_enumerate); | ||
42 | struct udev_list_entry *entry; | ||
43 | |||
44 | udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(udev_enumerate)) { | ||
45 | char filename[UTIL_PATH_SIZE]; | ||
46 | int fd; | ||
47 | |||
48 | if (verbose) | ||
49 | printf("%s\n", udev_list_entry_get_name(entry)); | ||
50 | if (dry_run) | ||
51 | continue; | ||
52 | util_strscpyl(filename, sizeof(filename), udev_list_entry_get_name(entry), "/uevent", NULL); | ||
53 | fd = open(filename, O_WRONLY); | ||
54 | if (fd < 0) { | ||
55 | dbg(udev, "error on opening %s: %m\n", filename); | ||
56 | continue; | ||
57 | } | ||
58 | if (write(fd, action, strlen(action)) < 0) | ||
59 | info(udev, "error writing '%s' to '%s': %m\n", action, filename); | ||
60 | close(fd); | ||
61 | } | ||
62 | } | ||
63 | |||
64 | static const char *keyval(const char *str, const char **val, char *buf, size_t size) | ||
65 | { | ||
66 | char *pos; | ||
67 | |||
68 | util_strscpy(buf, size,str); | ||
69 | pos = strchr(buf, '='); | ||
70 | if (pos != NULL) { | ||
71 | pos[0] = 0; | ||
72 | pos++; | ||
73 | } | ||
74 | *val = pos; | ||
75 | return buf; | ||
76 | } | ||
77 | |||
78 | static int adm_trigger(struct udev *udev, int argc, char *argv[]) | ||
79 | { | ||
80 | static const struct option options[] = { | ||
81 | { "verbose", no_argument, NULL, 'v' }, | ||
82 | { "dry-run", no_argument, NULL, 'n' }, | ||
83 | { "type", required_argument, NULL, 't' }, | ||
84 | { "action", required_argument, NULL, 'c' }, | ||
85 | { "subsystem-match", required_argument, NULL, 's' }, | ||
86 | { "subsystem-nomatch", required_argument, NULL, 'S' }, | ||
87 | { "attr-match", required_argument, NULL, 'a' }, | ||
88 | { "attr-nomatch", required_argument, NULL, 'A' }, | ||
89 | { "property-match", required_argument, NULL, 'p' }, | ||
90 | { "tag-match", required_argument, NULL, 'g' }, | ||
91 | { "sysname-match", required_argument, NULL, 'y' }, | ||
92 | { "parent-match", required_argument, NULL, 'b' }, | ||
93 | { "help", no_argument, NULL, 'h' }, | ||
94 | {} | ||
95 | }; | ||
96 | enum { | ||
97 | TYPE_DEVICES, | ||
98 | TYPE_SUBSYSTEMS, | ||
99 | } device_type = TYPE_DEVICES; | ||
100 | const char *action = "change"; | ||
101 | struct udev_enumerate *udev_enumerate; | ||
102 | int rc = 0; | ||
103 | |||
104 | dbg(udev, "version %s\n", VERSION); | ||
105 | udev_enumerate = udev_enumerate_new(udev); | ||
106 | if (udev_enumerate == NULL) { | ||
107 | rc = 1; | ||
108 | goto exit; | ||
109 | } | ||
110 | |||
111 | for (;;) { | ||
112 | int option; | ||
113 | const char *key; | ||
114 | const char *val; | ||
115 | char buf[UTIL_PATH_SIZE]; | ||
116 | |||
117 | option = getopt_long(argc, argv, "vng:o:t:hc:p:s:S:a:A:y:b:", options, NULL); | ||
118 | if (option == -1) | ||
119 | break; | ||
120 | |||
121 | switch (option) { | ||
122 | case 'v': | ||
123 | verbose = 1; | ||
124 | break; | ||
125 | case 'n': | ||
126 | dry_run = 1; | ||
127 | break; | ||
128 | case 't': | ||
129 | if (strcmp(optarg, "devices") == 0) { | ||
130 | device_type = TYPE_DEVICES; | ||
131 | } else if (strcmp(optarg, "subsystems") == 0) { | ||
132 | device_type = TYPE_SUBSYSTEMS; | ||
133 | } else { | ||
134 | err(udev, "unknown type --type=%s\n", optarg); | ||
135 | rc = 2; | ||
136 | goto exit; | ||
137 | } | ||
138 | break; | ||
139 | case 'c': | ||
140 | action = optarg; | ||
141 | break; | ||
142 | case 's': | ||
143 | udev_enumerate_add_match_subsystem(udev_enumerate, optarg); | ||
144 | break; | ||
145 | case 'S': | ||
146 | udev_enumerate_add_nomatch_subsystem(udev_enumerate, optarg); | ||
147 | break; | ||
148 | case 'a': | ||
149 | key = keyval(optarg, &val, buf, sizeof(buf)); | ||
150 | udev_enumerate_add_match_sysattr(udev_enumerate, key, val); | ||
151 | break; | ||
152 | case 'A': | ||
153 | key = keyval(optarg, &val, buf, sizeof(buf)); | ||
154 | udev_enumerate_add_nomatch_sysattr(udev_enumerate, key, val); | ||
155 | break; | ||
156 | case 'p': | ||
157 | key = keyval(optarg, &val, buf, sizeof(buf)); | ||
158 | udev_enumerate_add_match_property(udev_enumerate, key, val); | ||
159 | break; | ||
160 | case 'g': | ||
161 | udev_enumerate_add_match_tag(udev_enumerate, optarg); | ||
162 | break; | ||
163 | case 'y': | ||
164 | udev_enumerate_add_match_sysname(udev_enumerate, optarg); | ||
165 | break; | ||
166 | case 'b': { | ||
167 | char path[UTIL_PATH_SIZE]; | ||
168 | struct udev_device *dev; | ||
169 | |||
170 | /* add sys dir if needed */ | ||
171 | if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0) | ||
172 | util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), optarg, NULL); | ||
173 | else | ||
174 | util_strscpy(path, sizeof(path), optarg); | ||
175 | util_remove_trailing_chars(path, '/'); | ||
176 | dev = udev_device_new_from_syspath(udev, path); | ||
177 | if (dev == NULL) { | ||
178 | err(udev, "unable to open the device '%s'\n", optarg); | ||
179 | rc = 2; | ||
180 | goto exit; | ||
181 | } | ||
182 | udev_enumerate_add_match_parent(udev_enumerate, dev); | ||
183 | /* drop reference immediately, enumerate pins the device as long as needed */ | ||
184 | udev_device_unref(dev); | ||
185 | break; | ||
186 | } | ||
187 | case 'h': | ||
188 | printf("Usage: udevadm trigger OPTIONS\n" | ||
189 | " --verbose print the list of devices while running\n" | ||
190 | " --dry-run do not actually trigger the events\n" | ||
191 | " --type= type of events to trigger\n" | ||
192 | " devices sys devices (default)\n" | ||
193 | " subsystems sys subsystems and drivers\n" | ||
194 | " --action=<action> event action value, default is \"change\"\n" | ||
195 | " --subsystem-match=<subsystem> trigger devices from a matching subsystem\n" | ||
196 | " --subsystem-nomatch=<subsystem> exclude devices from a matching subsystem\n" | ||
197 | " --attr-match=<file[=<value>]> trigger devices with a matching attribute\n" | ||
198 | " --attr-nomatch=<file[=<value>]> exclude devices with a matching attribute\n" | ||
199 | " --property-match=<key>=<value> trigger devices with a matching property\n" | ||
200 | " --tag-match=<key>=<value> trigger devices with a matching property\n" | ||
201 | " --sysname-match=<name> trigger devices with a matching name\n" | ||
202 | " --parent-match=<name> trigger devices with that parent device\n" | ||
203 | " --help\n\n"); | ||
204 | goto exit; | ||
205 | default: | ||
206 | rc = 1; | ||
207 | goto exit; | ||
208 | } | ||
209 | } | ||
210 | |||
211 | switch (device_type) { | ||
212 | case TYPE_SUBSYSTEMS: | ||
213 | udev_enumerate_scan_subsystems(udev_enumerate); | ||
214 | exec_list(udev_enumerate, action); | ||
215 | goto exit; | ||
216 | case TYPE_DEVICES: | ||
217 | udev_enumerate_scan_devices(udev_enumerate); | ||
218 | exec_list(udev_enumerate, action); | ||
219 | goto exit; | ||
220 | default: | ||
221 | goto exit; | ||
222 | } | ||
223 | exit: | ||
224 | udev_enumerate_unref(udev_enumerate); | ||
225 | return rc; | ||
226 | } | ||
227 | |||
228 | const struct udevadm_cmd udevadm_trigger = { | ||
229 | .name = "trigger", | ||
230 | .cmd = adm_trigger, | ||
231 | .help = "request events from the kernel", | ||
232 | }; |
File src/udevadm.c added (mode: 100644) (index 0000000..224ece0) | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 Kay Sievers <kay.sievers@vrfy.org> | ||
3 | * | ||
4 | * This program is free software: you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation, either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #include <unistd.h> | ||
19 | #include <stdio.h> | ||
20 | #include <stdlib.h> | ||
21 | #include <stddef.h> | ||
22 | #include <string.h> | ||
23 | #include <errno.h> | ||
24 | #include <getopt.h> | ||
25 | |||
26 | #include "udev.h" | ||
27 | |||
28 | static bool debug; | ||
29 | |||
30 | void udev_main_log(struct udev *udev, int priority, | ||
31 | const char *file, int line, const char *fn, | ||
32 | const char *format, va_list args) | ||
33 | { | ||
34 | if (debug) { | ||
35 | fprintf(stderr, "%s: ", fn); | ||
36 | vfprintf(stderr, format, args); | ||
37 | } else { | ||
38 | va_list args2; | ||
39 | |||
40 | va_copy(args2, args); | ||
41 | vfprintf(stderr, format, args2); | ||
42 | va_end(args2); | ||
43 | vsyslog(priority, format, args); | ||
44 | } | ||
45 | } | ||
46 | |||
47 | static int adm_version(struct udev *udev, int argc, char *argv[]) | ||
48 | { | ||
49 | printf("%s\n", VERSION); | ||
50 | return 0; | ||
51 | } | ||
52 | static const struct udevadm_cmd udevadm_version = { | ||
53 | .name = "version", | ||
54 | .cmd = adm_version, | ||
55 | }; | ||
56 | |||
57 | static int adm_help(struct udev *udev, int argc, char *argv[]); | ||
58 | static const struct udevadm_cmd udevadm_help = { | ||
59 | .name = "help", | ||
60 | .cmd = adm_help, | ||
61 | }; | ||
62 | |||
63 | static const struct udevadm_cmd *udevadm_cmds[] = { | ||
64 | &udevadm_info, | ||
65 | &udevadm_trigger, | ||
66 | &udevadm_settle, | ||
67 | &udevadm_control, | ||
68 | &udevadm_monitor, | ||
69 | &udevadm_test, | ||
70 | &udevadm_test_builtin, | ||
71 | &udevadm_version, | ||
72 | &udevadm_help, | ||
73 | }; | ||
74 | |||
75 | static int adm_help(struct udev *udev, int argc, char *argv[]) | ||
76 | { | ||
77 | unsigned int i; | ||
78 | |||
79 | fprintf(stderr, "Usage: udevadm [--help] [--version] [--debug] COMMAND [COMMAND OPTIONS]\n"); | ||
80 | for (i = 0; i < ARRAY_SIZE(udevadm_cmds); i++) | ||
81 | if (udevadm_cmds[i]->help != NULL) | ||
82 | printf(" %-12s %s\n", udevadm_cmds[i]->name, udevadm_cmds[i]->help); | ||
83 | fprintf(stderr, "\n"); | ||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static int run_command(struct udev *udev, const struct udevadm_cmd *cmd, int argc, char *argv[]) | ||
88 | { | ||
89 | if (cmd->debug) { | ||
90 | debug = true; | ||
91 | if (udev_get_log_priority(udev) < LOG_INFO) | ||
92 | udev_set_log_priority(udev, LOG_INFO); | ||
93 | } | ||
94 | info(udev, "calling: %s\n", cmd->name); | ||
95 | return cmd->cmd(udev, argc, argv); | ||
96 | } | ||
97 | |||
98 | int main(int argc, char *argv[]) | ||
99 | { | ||
100 | struct udev *udev; | ||
101 | static const struct option options[] = { | ||
102 | { "debug", no_argument, NULL, 'd' }, | ||
103 | { "help", no_argument, NULL, 'h' }, | ||
104 | { "version", no_argument, NULL, 'V' }, | ||
105 | {} | ||
106 | }; | ||
107 | const char *command; | ||
108 | unsigned int i; | ||
109 | int rc = 1; | ||
110 | |||
111 | udev = udev_new(); | ||
112 | if (udev == NULL) | ||
113 | goto out; | ||
114 | |||
115 | udev_log_init("udevadm"); | ||
116 | udev_set_log_fn(udev, udev_main_log); | ||
117 | udev_selinux_init(udev); | ||
118 | |||
119 | for (;;) { | ||
120 | int option; | ||
121 | |||
122 | option = getopt_long(argc, argv, "+dhV", options, NULL); | ||
123 | if (option == -1) | ||
124 | break; | ||
125 | |||
126 | switch (option) { | ||
127 | case 'd': | ||
128 | debug = true; | ||
129 | if (udev_get_log_priority(udev) < LOG_INFO) | ||
130 | udev_set_log_priority(udev, LOG_INFO); | ||
131 | break; | ||
132 | case 'h': | ||
133 | rc = adm_help(udev, argc, argv); | ||
134 | goto out; | ||
135 | case 'V': | ||
136 | rc = adm_version(udev, argc, argv); | ||
137 | goto out; | ||
138 | default: | ||
139 | goto out; | ||
140 | } | ||
141 | } | ||
142 | command = argv[optind]; | ||
143 | |||
144 | info(udev, "runtime dir '%s'\n", udev_get_run_path(udev)); | ||
145 | |||
146 | if (command != NULL) | ||
147 | for (i = 0; i < ARRAY_SIZE(udevadm_cmds); i++) { | ||
148 | if (strcmp(udevadm_cmds[i]->name, command) == 0) { | ||
149 | argc -= optind; | ||
150 | argv += optind; | ||
151 | optind = 0; | ||
152 | rc = run_command(udev, udevadm_cmds[i], argc, argv); | ||
153 | goto out; | ||
154 | } | ||
155 | } | ||
156 | |||
157 | fprintf(stderr, "missing or unknown command\n\n"); | ||
158 | adm_help(udev, argc, argv); | ||
159 | rc = 2; | ||
160 | out: | ||
161 | udev_selinux_exit(udev); | ||
162 | udev_unref(udev); | ||
163 | udev_log_close(); | ||
164 | return rc; | ||
165 | } |
File src/udevadm.xml added (mode: 100644) (index 0000000..455ce80) | |||
1 | <?xml version='1.0'?> | ||
2 | <?xml-stylesheet type="text/xsl" href="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl"?> | ||
3 | <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" | ||
4 | "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> | ||
5 | |||
6 | <refentry id="udevadm"> | ||
7 | <refentryinfo> | ||
8 | <title>udevadm</title> | ||
9 | <productname>udev</productname> | ||
10 | </refentryinfo> | ||
11 | |||
12 | <refmeta> | ||
13 | <refentrytitle>udevadm</refentrytitle> | ||
14 | <manvolnum>8</manvolnum> | ||
15 | <refmiscinfo class="version"></refmiscinfo> | ||
16 | </refmeta> | ||
17 | |||
18 | <refnamediv> | ||
19 | <refname>udevadm</refname><refpurpose>udev management tool</refpurpose> | ||
20 | </refnamediv> | ||
21 | |||
22 | <refsynopsisdiv> | ||
23 | <cmdsynopsis> | ||
24 | <command>udevadm</command> | ||
25 | <arg><option>--debug</option></arg> | ||
26 | <arg><option>--version</option></arg> | ||
27 | <arg><option>--help</option></arg> | ||
28 | </cmdsynopsis> | ||
29 | <cmdsynopsis> | ||
30 | <command>udevadm info <replaceable>options</replaceable></command> | ||
31 | </cmdsynopsis> | ||
32 | <cmdsynopsis> | ||
33 | <command>udevadm trigger <optional>options</optional></command> | ||
34 | </cmdsynopsis> | ||
35 | <cmdsynopsis> | ||
36 | <command>udevadm settle <optional>options</optional></command> | ||
37 | </cmdsynopsis> | ||
38 | <cmdsynopsis> | ||
39 | <command>udevadm control <replaceable>command</replaceable></command> | ||
40 | </cmdsynopsis> | ||
41 | <cmdsynopsis> | ||
42 | <command>udevadm monitor <optional>options</optional></command> | ||
43 | </cmdsynopsis> | ||
44 | <cmdsynopsis> | ||
45 | <command>udevadm test <optional>options</optional> <replaceable>devpath</replaceable></command> | ||
46 | </cmdsynopsis> | ||
47 | <cmdsynopsis> | ||
48 | <command>udevadm test-builtin <optional>options</optional> <replaceable>command</replaceable> <replaceable>devpath</replaceable></command> | ||
49 | </cmdsynopsis> | ||
50 | </refsynopsisdiv> | ||
51 | |||
52 | <refsect1><title>Description</title> | ||
53 | <para>udevadm expects a command and command specific options. It | ||
54 | controls the runtime behavior of udev, requests kernel events, | ||
55 | manages the event queue, and provides simple debugging mechanisms.</para> | ||
56 | </refsect1> | ||
57 | |||
58 | <refsect1><title>OPTIONS</title> | ||
59 | <variablelist> | ||
60 | <varlistentry> | ||
61 | <term><option>--debug</option></term> | ||
62 | <listitem> | ||
63 | <para>Print debug messages to stderr.</para> | ||
64 | </listitem> | ||
65 | </varlistentry> | ||
66 | <varlistentry> | ||
67 | <term><option>--version</option></term> | ||
68 | <listitem> | ||
69 | <para>Print version number.</para> | ||
70 | </listitem> | ||
71 | </varlistentry> | ||
72 | <varlistentry> | ||
73 | <term><option>--help</option></term> | ||
74 | <listitem> | ||
75 | <para>Print help text.</para> | ||
76 | </listitem> | ||
77 | </varlistentry> | ||
78 | </variablelist> | ||
79 | |||
80 | <refsect2><title>udevadm info <replaceable>options</replaceable></title> | ||
81 | <para>Queries the udev database for device information | ||
82 | stored in the udev database. It can also query the properties | ||
83 | of a device from its sysfs representation to help creating udev | ||
84 | rules that match this device.</para> | ||
85 | <variablelist> | ||
86 | <varlistentry> | ||
87 | <term><option>--query=<replaceable>type</replaceable></option></term> | ||
88 | <listitem> | ||
89 | <para>Query the database for specified type of device data. It needs the | ||
90 | <option>--path</option> or <option>--name</option> to identify the specified | ||
91 | device. Valid queries are: | ||
92 | <command>name</command>, <command>symlink</command>, <command>path</command>, | ||
93 | <command>property</command>, <command>all</command>.</para> | ||
94 | </listitem> | ||
95 | </varlistentry> | ||
96 | <varlistentry> | ||
97 | <term><option>--path=<replaceable>devpath</replaceable></option></term> | ||
98 | <listitem> | ||
99 | <para>The devpath of the device to query.</para> | ||
100 | </listitem> | ||
101 | </varlistentry> | ||
102 | <varlistentry> | ||
103 | <term><option>--name=<replaceable>file</replaceable></option></term> | ||
104 | <listitem> | ||
105 | <para>The name of the device node or a symlink to query</para> | ||
106 | </listitem> | ||
107 | </varlistentry> | ||
108 | <varlistentry> | ||
109 | <term><option>--root</option></term> | ||
110 | <listitem> | ||
111 | <para>The udev root directory: <filename>/dev</filename>. If used in conjunction | ||
112 | with a <command>name</command> or <command>symlink</command> query, the | ||
113 | query returns the absolute path including the root directory.</para> | ||
114 | </listitem> | ||
115 | </varlistentry> | ||
116 | <varlistentry> | ||
117 | <term><option>--run</option></term> | ||
118 | <listitem> | ||
119 | <para>The udev runtime directory: <filename>/run/udev</filename>.</para> | ||
120 | </listitem> | ||
121 | </varlistentry> | ||
122 | <varlistentry> | ||
123 | <term><option>--attribute-walk</option></term> | ||
124 | <listitem> | ||
125 | <para>Print all sysfs properties of the specified device that can be used | ||
126 | in udev rules to match the specified device. It prints all devices | ||
127 | along the chain, up to the root of sysfs that can be used in udev rules.</para> | ||
128 | </listitem> | ||
129 | </varlistentry> | ||
130 | <varlistentry> | ||
131 | <term><option>--export</option></term> | ||
132 | <listitem> | ||
133 | <para>Print output as key/value pairs. Values are enclosed in single quotes.</para> | ||
134 | </listitem> | ||
135 | </varlistentry> | ||
136 | <varlistentry> | ||
137 | <term><option>--export-prefix=<replaceable>name</replaceable></option></term> | ||
138 | <listitem> | ||
139 | <para>Add a prefix to the key name of exported values.</para> | ||
140 | </listitem> | ||
141 | </varlistentry> | ||
142 | <varlistentry> | ||
143 | <term><option>--device-id-of-file=<replaceable>file</replaceable></option></term> | ||
144 | <listitem> | ||
145 | <para>Print major/minor numbers of the underlying device, where the file | ||
146 | lives on.</para> | ||
147 | </listitem> | ||
148 | </varlistentry> | ||
149 | <varlistentry> | ||
150 | <term><option>--export-db</option></term> | ||
151 | <listitem> | ||
152 | <para>Export the content of the udev database.</para> | ||
153 | </listitem> | ||
154 | </varlistentry> | ||
155 | <varlistentry> | ||
156 | <term><option>--cleanup-db</option></term> | ||
157 | <listitem> | ||
158 | <para>Cleanup the udev database.</para> | ||
159 | </listitem> | ||
160 | </varlistentry> | ||
161 | <varlistentry> | ||
162 | <term><option>--version</option></term> | ||
163 | <listitem> | ||
164 | <para>Print version.</para> | ||
165 | </listitem> | ||
166 | </varlistentry> | ||
167 | <varlistentry> | ||
168 | <term><option>--help</option></term> | ||
169 | <listitem> | ||
170 | <para>Print help text.</para> | ||
171 | </listitem> | ||
172 | </varlistentry> | ||
173 | </variablelist> | ||
174 | </refsect2> | ||
175 | |||
176 | <refsect2><title>udevadm trigger <optional>options</optional></title> | ||
177 | <para>Request device events from the kernel. Primarily used to replay events at system coldplug time.</para> | ||
178 | <variablelist> | ||
179 | <varlistentry> | ||
180 | <term><option>--verbose</option></term> | ||
181 | <listitem> | ||
182 | <para>Print the list of devices which will be triggered.</para> | ||
183 | </listitem> | ||
184 | </varlistentry> | ||
185 | <varlistentry> | ||
186 | <term><option>--dry-run</option></term> | ||
187 | <listitem> | ||
188 | <para>Do not actually trigger the event.</para> | ||
189 | </listitem> | ||
190 | </varlistentry> | ||
191 | <varlistentry> | ||
192 | <term><option>--type=<replaceable>type</replaceable></option></term> | ||
193 | <listitem> | ||
194 | <para>Trigger a specific type of devices. Valid types are: | ||
195 | <command>devices</command>, <command>subsystems</command>. | ||
196 | The default value is <command>devices</command>.</para> | ||
197 | </listitem> | ||
198 | </varlistentry> | ||
199 | <varlistentry> | ||
200 | <term><option>--action=<replaceable>action</replaceable></option></term> | ||
201 | <listitem> | ||
202 | <para>Type of event to be triggered. The default value is <command>change</command>.</para> | ||
203 | </listitem> | ||
204 | </varlistentry> | ||
205 | <varlistentry> | ||
206 | <term><option>--subsystem-match=<replaceable>subsystem</replaceable></option></term> | ||
207 | <listitem> | ||
208 | <para>Trigger events for devices which belong to a matching subsystem. This option | ||
209 | can be specified multiple times and supports shell style pattern matching.</para> | ||
210 | </listitem> | ||
211 | </varlistentry> | ||
212 | <varlistentry> | ||
213 | <term><option>--subsystem-nomatch=<replaceable>subsystem</replaceable></option></term> | ||
214 | <listitem> | ||
215 | <para>Do not trigger events for devices which belong to a matching subsystem. This option | ||
216 | can be specified multiple times and supports shell style pattern matching.</para> | ||
217 | </listitem> | ||
218 | </varlistentry> | ||
219 | <varlistentry> | ||
220 | <term><option>--attr-match=<replaceable>attribute</replaceable>=<replaceable>value</replaceable></option></term> | ||
221 | <listitem> | ||
222 | <para>Trigger events for devices with a matching sysfs attribute. If a value is specified | ||
223 | along with the attribute name, the content of the attribute is matched against the given | ||
224 | value using shell style pattern matching. If no value is specified, the existence of the | ||
225 | sysfs attribute is checked. This option can be specified multiple times.</para> | ||
226 | </listitem> | ||
227 | </varlistentry> | ||
228 | <varlistentry> | ||
229 | <term><option>--attr-nomatch=<replaceable>attribute</replaceable>=<replaceable>value</replaceable></option></term> | ||
230 | <listitem> | ||
231 | <para>Do not trigger events for devices with a matching sysfs attribute. If a value is | ||
232 | specified along with the attribute name, the content of the attribute is matched against | ||
233 | the given value using shell style pattern matching. If no value is specified, the existence | ||
234 | of the sysfs attribute is checked. This option can be specified multiple times.</para> | ||
235 | </listitem> | ||
236 | </varlistentry> | ||
237 | <varlistentry> | ||
238 | <term><option>--property-match=<replaceable>property</replaceable>=<replaceable>value</replaceable></option></term> | ||
239 | <listitem> | ||
240 | <para>Trigger events for devices with a matching property value. This option can be | ||
241 | specified multiple times and supports shell style pattern matching.</para> | ||
242 | </listitem> | ||
243 | </varlistentry> | ||
244 | <varlistentry> | ||
245 | <term><option>--tag-match=<replaceable>property</replaceable></option></term> | ||
246 | <listitem> | ||
247 | <para>Trigger events for devices with a matching tag. This option can be | ||
248 | specified multiple times.</para> | ||
249 | </listitem> | ||
250 | </varlistentry> | ||
251 | <varlistentry> | ||
252 | <term><option>--sysname-match=<replaceable>name</replaceable></option></term> | ||
253 | <listitem> | ||
254 | <para>Trigger events for devices with a matching sys device name. This option can be | ||
255 | specified multiple times and supports shell style pattern matching.</para> | ||
256 | </listitem> | ||
257 | </varlistentry> | ||
258 | <varlistentry> | ||
259 | <term><option>--parent-match=<replaceable>syspath</replaceable></option></term> | ||
260 | <listitem> | ||
261 | <para>Trigger events for all children of a given device.</para> | ||
262 | </listitem> | ||
263 | </varlistentry> | ||
264 | </variablelist> | ||
265 | </refsect2> | ||
266 | |||
267 | <refsect2><title>udevadm settle <optional>options</optional></title> | ||
268 | <para>Watches the udev event queue, and exits if all current events are handled.</para> | ||
269 | <variablelist> | ||
270 | <varlistentry> | ||
271 | <term><option>--timeout=<replaceable>seconds</replaceable></option></term> | ||
272 | <listitem> | ||
273 | <para>Maximum number of seconds to wait for the event queue to become empty. | ||
274 | The default value is 120 seconds. A value of 0 will check if the queue is empty | ||
275 | and always return immediately.</para> | ||
276 | </listitem> | ||
277 | </varlistentry> | ||
278 | <varlistentry> | ||
279 | <term><option>--seq-start=<replaceable>seqnum</replaceable></option></term> | ||
280 | <listitem> | ||
281 | <para>Wait only for events after the given sequence number.</para> | ||
282 | </listitem> | ||
283 | </varlistentry> | ||
284 | <varlistentry> | ||
285 | <term><option>--seq-end=<replaceable>seqnum</replaceable></option></term> | ||
286 | <listitem> | ||
287 | <para>Wait only for events before the given sequence number.</para> | ||
288 | </listitem> | ||
289 | </varlistentry> | ||
290 | <varlistentry> | ||
291 | <term><option>--exit-if-exists=<replaceable>file</replaceable></option></term> | ||
292 | <listitem> | ||
293 | <para>Stop waiting if file exists.</para> | ||
294 | </listitem> | ||
295 | </varlistentry> | ||
296 | <varlistentry> | ||
297 | <term><option>--quiet</option></term> | ||
298 | <listitem> | ||
299 | <para>Do not print any output, like the remaining queue entries when reaching the timeout.</para> | ||
300 | </listitem> | ||
301 | </varlistentry> | ||
302 | <varlistentry> | ||
303 | <term><option>--help</option></term> | ||
304 | <listitem> | ||
305 | <para>Print help text.</para> | ||
306 | </listitem> | ||
307 | </varlistentry> | ||
308 | </variablelist> | ||
309 | </refsect2> | ||
310 | |||
311 | <refsect2><title>udevadm control <replaceable>command</replaceable></title> | ||
312 | <para>Modify the internal state of the running udev daemon.</para> | ||
313 | <variablelist> | ||
314 | <varlistentry> | ||
315 | <term><option>--exit</option></term> | ||
316 | <listitem> | ||
317 | <para>Signal and wait for udevd to exit.</para> | ||
318 | </listitem> | ||
319 | </varlistentry> | ||
320 | <varlistentry> | ||
321 | <term><option>--log-priority=<replaceable>value</replaceable></option></term> | ||
322 | <listitem> | ||
323 | <para>Set the internal log level of udevd. Valid values are the numerical | ||
324 | syslog priorities or their textual representations: <option>err</option>, | ||
325 | <option>info</option> and <option>debug</option>.</para> | ||
326 | </listitem> | ||
327 | </varlistentry> | ||
328 | <varlistentry> | ||
329 | <term><option>--stop-exec-queue</option></term> | ||
330 | <listitem> | ||
331 | <para>Signal udevd to stop executing new events. Incoming events | ||
332 | will be queued.</para> | ||
333 | </listitem> | ||
334 | </varlistentry> | ||
335 | <varlistentry> | ||
336 | <term><option>--start-exec-queue</option></term> | ||
337 | <listitem> | ||
338 | <para>Signal udevd to enable the execution of events.</para> | ||
339 | </listitem> | ||
340 | </varlistentry> | ||
341 | <varlistentry> | ||
342 | <term><option>--reload</option></term> | ||
343 | <listitem> | ||
344 | <para>Signal udevd to reload the rules files and other databases like the kernel | ||
345 | module index. Reloading rules and databases does not apply any changes to already | ||
346 | existing devices; the new configuration will only be applied to new events.</para> | ||
347 | </listitem> | ||
348 | </varlistentry> | ||
349 | <varlistentry> | ||
350 | <term><option>--property=<replaceable>KEY</replaceable>=<replaceable>value</replaceable></option></term> | ||
351 | <listitem> | ||
352 | <para>Set a global property for all events.</para> | ||
353 | </listitem> | ||
354 | </varlistentry> | ||
355 | <varlistentry> | ||
356 | <term><option>--children-max=</option><replaceable>value</replaceable></term> | ||
357 | <listitem> | ||
358 | <para>Set the maximum number of events, udevd will handle at the | ||
359 | same time.</para> | ||
360 | </listitem> | ||
361 | </varlistentry> | ||
362 | <varlistentry> | ||
363 | <term><option>--timeout=</option><replaceable>seconds</replaceable></term> | ||
364 | <listitem> | ||
365 | <para>The maximum number seconds to wait for a reply from udevd.</para> | ||
366 | </listitem> | ||
367 | </varlistentry> | ||
368 | <varlistentry> | ||
369 | <term><option>--help</option></term> | ||
370 | <listitem> | ||
371 | <para>Print help text.</para> | ||
372 | </listitem> | ||
373 | </varlistentry> | ||
374 | </variablelist> | ||
375 | </refsect2> | ||
376 | |||
377 | <refsect2><title>udevadm monitor <optional>options</optional></title> | ||
378 | <para>Listens to the kernel uevents and events sent out by a udev rule | ||
379 | and prints the devpath of the event to the console. It can be used to analyze the | ||
380 | event timing, by comparing the timestamps of the kernel uevent and the udev event. | ||
381 | </para> | ||
382 | <variablelist> | ||
383 | <varlistentry> | ||
384 | <term><option>--kernel</option></term> | ||
385 | <listitem> | ||
386 | <para>Print the kernel uevents.</para> | ||
387 | </listitem> | ||
388 | </varlistentry> | ||
389 | <varlistentry> | ||
390 | <term><option>--udev</option></term> | ||
391 | <listitem> | ||
392 | <para>Print the udev event after the rule processing.</para> | ||
393 | </listitem> | ||
394 | </varlistentry> | ||
395 | <varlistentry> | ||
396 | <term><option>--property</option></term> | ||
397 | <listitem> | ||
398 | <para>Also print the properties of the event.</para> | ||
399 | </listitem> | ||
400 | </varlistentry> | ||
401 | <varlistentry> | ||
402 | <term><option>--subsystem-match=<replaceable>string[/string]</replaceable></option></term> | ||
403 | <listitem> | ||
404 | <para>Filter events by subsystem[/devtype]. Only udev events with a matching subsystem value will pass.</para> | ||
405 | </listitem> | ||
406 | </varlistentry> | ||
407 | <varlistentry> | ||
408 | <term><option>--tag-match=<replaceable>string</replaceable></option></term> | ||
409 | <listitem> | ||
410 | <para>Filter events by property. Only udev events with a given tag attached will pass.</para> | ||
411 | </listitem> | ||
412 | </varlistentry> | ||
413 | <varlistentry> | ||
414 | <term><option>--help</option></term> | ||
415 | <listitem> | ||
416 | <para>Print help text.</para> | ||
417 | </listitem> | ||
418 | </varlistentry> | ||
419 | </variablelist> | ||
420 | </refsect2> | ||
421 | |||
422 | <refsect2><title>udevadm test <optional>options</optional> <replaceable>devpath</replaceable></title> | ||
423 | <para>Simulate a udev event run for the given device, and print debug output.</para> | ||
424 | <variablelist> | ||
425 | <varlistentry> | ||
426 | <term><option>--action=<replaceable>string</replaceable></option></term> | ||
427 | <listitem> | ||
428 | <para>The action string.</para> | ||
429 | </listitem> | ||
430 | </varlistentry> | ||
431 | <varlistentry> | ||
432 | <term><option>--subsystem=<replaceable>string</replaceable></option></term> | ||
433 | <listitem> | ||
434 | <para>The subsystem string.</para> | ||
435 | </listitem> | ||
436 | </varlistentry> | ||
437 | <varlistentry> | ||
438 | <term><option>--help</option></term> | ||
439 | <listitem> | ||
440 | <para>Print help text.</para> | ||
441 | </listitem> | ||
442 | </varlistentry> | ||
443 | </variablelist> | ||
444 | </refsect2> | ||
445 | |||
446 | <refsect2><title>udevadm test-builtin <optional>options</optional> <replaceable>command</replaceable> <replaceable>devpath</replaceable></title> | ||
447 | <para>Run a built-in command for the given device, and print debug output.</para> | ||
448 | <variablelist> | ||
449 | <varlistentry> | ||
450 | <term><option>--help</option></term> | ||
451 | <listitem> | ||
452 | <para>Print help text.</para> | ||
453 | </listitem> | ||
454 | </varlistentry> | ||
455 | </variablelist> | ||
456 | </refsect2> | ||
457 | </refsect1> | ||
458 | |||
459 | <refsect1><title>Author</title> | ||
460 | <para>Written by Kay Sievers <email>kay.sievers@vrfy.org</email>.</para> | ||
461 | </refsect1> | ||
462 | |||
463 | <refsect1> | ||
464 | <title>See Also</title> | ||
465 | <para><citerefentry> | ||
466 | <refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum> | ||
467 | </citerefentry> | ||
468 | <citerefentry> | ||
469 | <refentrytitle>udevd</refentrytitle><manvolnum>8</manvolnum> | ||
470 | </citerefentry></para> | ||
471 | </refsect1> | ||
472 | </refentry> |
File src/udevd.c added (mode: 100644) (index 0000000..fde4519) | |||
1 | /* | ||
2 | * Copyright (C) 2004-2011 Kay Sievers <kay.sievers@vrfy.org> | ||
3 | * Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca> | ||
4 | * Copyright (C) 2009 Canonical Ltd. | ||
5 | * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com> | ||
6 | * | ||
7 | * This program is free software: you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation, either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | */ | ||
20 | |||
21 | #include <stddef.h> | ||
22 | #include <signal.h> | ||
23 | #include <unistd.h> | ||
24 | #include <errno.h> | ||
25 | #include <stdio.h> | ||
26 | #include <stdlib.h> | ||
27 | #include <stdbool.h> | ||
28 | #include <string.h> | ||
29 | #include <ctype.h> | ||
30 | #include <fcntl.h> | ||
31 | #include <time.h> | ||
32 | #include <getopt.h> | ||
33 | #include <dirent.h> | ||
34 | #include <sys/time.h> | ||
35 | #include <sys/prctl.h> | ||
36 | #include <sys/socket.h> | ||
37 | #include <sys/un.h> | ||
38 | #include <sys/signalfd.h> | ||
39 | #include <sys/epoll.h> | ||
40 | #include <sys/poll.h> | ||
41 | #include <sys/wait.h> | ||
42 | #include <sys/stat.h> | ||
43 | #include <sys/ioctl.h> | ||
44 | #include <sys/inotify.h> | ||
45 | #include <sys/utsname.h> | ||
46 | |||
47 | #include "udev.h" | ||
48 | |||
49 | static bool debug; | ||
50 | |||
51 | void udev_main_log(struct udev *udev, int priority, | ||
52 | const char *file, int line, const char *fn, | ||
53 | const char *format, va_list args) | ||
54 | { | ||
55 | if (debug) { | ||
56 | char buf[1024]; | ||
57 | struct timespec ts; | ||
58 | |||
59 | vsnprintf(buf, sizeof(buf), format, args); | ||
60 | clock_gettime(CLOCK_MONOTONIC, &ts); | ||
61 | fprintf(stderr, "[%llu.%06u] [%u] %s: %s", | ||
62 | (unsigned long long) ts.tv_sec, (unsigned int) ts.tv_nsec/1000, | ||
63 | (int) getpid(), fn, buf); | ||
64 | } else { | ||
65 | vsyslog(priority, format, args); | ||
66 | } | ||
67 | } | ||
68 | |||
69 | static struct udev_rules *rules; | ||
70 | static struct udev_queue_export *udev_queue_export; | ||
71 | static struct udev_ctrl *udev_ctrl; | ||
72 | static struct udev_monitor *monitor; | ||
73 | static int worker_watch[2] = { -1, -1 }; | ||
74 | static int fd_signal = -1; | ||
75 | static int fd_ep = -1; | ||
76 | static int fd_inotify = -1; | ||
77 | static bool stop_exec_queue; | ||
78 | static bool reload; | ||
79 | static int children; | ||
80 | static int children_max; | ||
81 | static int exec_delay; | ||
82 | static sigset_t sigmask_orig; | ||
83 | static UDEV_LIST(event_list); | ||
84 | static UDEV_LIST(worker_list); | ||
85 | static bool udev_exit; | ||
86 | |||
87 | enum event_state { | ||
88 | EVENT_UNDEF, | ||
89 | EVENT_QUEUED, | ||
90 | EVENT_RUNNING, | ||
91 | }; | ||
92 | |||
93 | struct event { | ||
94 | struct udev_list_node node; | ||
95 | struct udev *udev; | ||
96 | struct udev_device *dev; | ||
97 | enum event_state state; | ||
98 | int exitcode; | ||
99 | unsigned long long int delaying_seqnum; | ||
100 | unsigned long long int seqnum; | ||
101 | const char *devpath; | ||
102 | size_t devpath_len; | ||
103 | const char *devpath_old; | ||
104 | dev_t devnum; | ||
105 | bool is_block; | ||
106 | int ifindex; | ||
107 | }; | ||
108 | |||
109 | static struct event *node_to_event(struct udev_list_node *node) | ||
110 | { | ||
111 | char *event; | ||
112 | |||
113 | event = (char *)node; | ||
114 | event -= offsetof(struct event, node); | ||
115 | return (struct event *)event; | ||
116 | } | ||
117 | |||
118 | static void event_queue_cleanup(struct udev *udev, enum event_state type); | ||
119 | |||
120 | enum worker_state { | ||
121 | WORKER_UNDEF, | ||
122 | WORKER_RUNNING, | ||
123 | WORKER_IDLE, | ||
124 | WORKER_KILLED, | ||
125 | }; | ||
126 | |||
127 | struct worker { | ||
128 | struct udev_list_node node; | ||
129 | struct udev *udev; | ||
130 | int refcount; | ||
131 | pid_t pid; | ||
132 | struct udev_monitor *monitor; | ||
133 | enum worker_state state; | ||
134 | struct event *event; | ||
135 | unsigned long long event_start_usec; | ||
136 | }; | ||
137 | |||
138 | /* passed from worker to main process */ | ||
139 | struct worker_message { | ||
140 | pid_t pid; | ||
141 | int exitcode; | ||
142 | }; | ||
143 | |||
144 | static struct worker *node_to_worker(struct udev_list_node *node) | ||
145 | { | ||
146 | char *worker; | ||
147 | |||
148 | worker = (char *)node; | ||
149 | worker -= offsetof(struct worker, node); | ||
150 | return (struct worker *)worker; | ||
151 | } | ||
152 | |||
153 | static void event_queue_delete(struct event *event, bool export) | ||
154 | { | ||
155 | udev_list_node_remove(&event->node); | ||
156 | |||
157 | if (export) { | ||
158 | udev_queue_export_device_finished(udev_queue_export, event->dev); | ||
159 | info(event->udev, "seq %llu done with %i\n", udev_device_get_seqnum(event->dev), event->exitcode); | ||
160 | } | ||
161 | udev_device_unref(event->dev); | ||
162 | free(event); | ||
163 | } | ||
164 | |||
165 | static struct worker *worker_ref(struct worker *worker) | ||
166 | { | ||
167 | worker->refcount++; | ||
168 | return worker; | ||
169 | } | ||
170 | |||
171 | static void worker_cleanup(struct worker *worker) | ||
172 | { | ||
173 | udev_list_node_remove(&worker->node); | ||
174 | udev_monitor_unref(worker->monitor); | ||
175 | children--; | ||
176 | free(worker); | ||
177 | } | ||
178 | |||
179 | static void worker_unref(struct worker *worker) | ||
180 | { | ||
181 | worker->refcount--; | ||
182 | if (worker->refcount > 0) | ||
183 | return; | ||
184 | info(worker->udev, "worker [%u] cleaned up\n", worker->pid); | ||
185 | worker_cleanup(worker); | ||
186 | } | ||
187 | |||
188 | static void worker_list_cleanup(struct udev *udev) | ||
189 | { | ||
190 | struct udev_list_node *loop, *tmp; | ||
191 | |||
192 | udev_list_node_foreach_safe(loop, tmp, &worker_list) { | ||
193 | struct worker *worker = node_to_worker(loop); | ||
194 | |||
195 | worker_cleanup(worker); | ||
196 | } | ||
197 | } | ||
198 | |||
199 | static void worker_new(struct event *event) | ||
200 | { | ||
201 | struct udev *udev = event->udev; | ||
202 | struct worker *worker; | ||
203 | struct udev_monitor *worker_monitor; | ||
204 | pid_t pid; | ||
205 | |||
206 | /* listen for new events */ | ||
207 | worker_monitor = udev_monitor_new_from_netlink(udev, NULL); | ||
208 | if (worker_monitor == NULL) | ||
209 | return; | ||
210 | /* allow the main daemon netlink address to send devices to the worker */ | ||
211 | udev_monitor_allow_unicast_sender(worker_monitor, monitor); | ||
212 | udev_monitor_enable_receiving(worker_monitor); | ||
213 | |||
214 | worker = calloc(1, sizeof(struct worker)); | ||
215 | if (worker == NULL) { | ||
216 | udev_monitor_unref(worker_monitor); | ||
217 | return; | ||
218 | } | ||
219 | /* worker + event reference */ | ||
220 | worker->refcount = 2; | ||
221 | worker->udev = udev; | ||
222 | |||
223 | pid = fork(); | ||
224 | switch (pid) { | ||
225 | case 0: { | ||
226 | struct udev_device *dev = NULL; | ||
227 | int fd_monitor; | ||
228 | struct epoll_event ep_signal, ep_monitor; | ||
229 | sigset_t mask; | ||
230 | int rc = EXIT_SUCCESS; | ||
231 | |||
232 | /* take initial device from queue */ | ||
233 | dev = event->dev; | ||
234 | event->dev = NULL; | ||
235 | |||
236 | free(worker); | ||
237 | worker_list_cleanup(udev); | ||
238 | event_queue_cleanup(udev, EVENT_UNDEF); | ||
239 | udev_queue_export_unref(udev_queue_export); | ||
240 | udev_monitor_unref(monitor); | ||
241 | udev_ctrl_unref(udev_ctrl); | ||
242 | close(fd_signal); | ||
243 | close(fd_ep); | ||
244 | close(worker_watch[READ_END]); | ||
245 | |||
246 | sigfillset(&mask); | ||
247 | fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); | ||
248 | if (fd_signal < 0) { | ||
249 | err(udev, "error creating signalfd %m\n"); | ||
250 | rc = 2; | ||
251 | goto out; | ||
252 | } | ||
253 | |||
254 | fd_ep = epoll_create1(EPOLL_CLOEXEC); | ||
255 | if (fd_ep < 0) { | ||
256 | err(udev, "error creating epoll fd: %m\n"); | ||
257 | rc = 3; | ||
258 | goto out; | ||
259 | } | ||
260 | |||
261 | memset(&ep_signal, 0, sizeof(struct epoll_event)); | ||
262 | ep_signal.events = EPOLLIN; | ||
263 | ep_signal.data.fd = fd_signal; | ||
264 | |||
265 | fd_monitor = udev_monitor_get_fd(worker_monitor); | ||
266 | memset(&ep_monitor, 0, sizeof(struct epoll_event)); | ||
267 | ep_monitor.events = EPOLLIN; | ||
268 | ep_monitor.data.fd = fd_monitor; | ||
269 | |||
270 | if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 || | ||
271 | epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) { | ||
272 | err(udev, "fail to add fds to epoll: %m\n"); | ||
273 | rc = 4; | ||
274 | goto out; | ||
275 | } | ||
276 | |||
277 | /* request TERM signal if parent exits */ | ||
278 | prctl(PR_SET_PDEATHSIG, SIGTERM); | ||
279 | |||
280 | for (;;) { | ||
281 | struct udev_event *udev_event; | ||
282 | struct worker_message msg; | ||
283 | int err; | ||
284 | |||
285 | info(udev, "seq %llu running\n", udev_device_get_seqnum(dev)); | ||
286 | udev_event = udev_event_new(dev); | ||
287 | if (udev_event == NULL) { | ||
288 | rc = 5; | ||
289 | goto out; | ||
290 | } | ||
291 | |||
292 | /* needed for SIGCHLD/SIGTERM in spawn() */ | ||
293 | udev_event->fd_signal = fd_signal; | ||
294 | |||
295 | if (exec_delay > 0) | ||
296 | udev_event->exec_delay = exec_delay; | ||
297 | |||
298 | /* apply rules, create node, symlinks */ | ||
299 | err = udev_event_execute_rules(udev_event, rules, &sigmask_orig); | ||
300 | |||
301 | if (err == 0) | ||
302 | udev_event_execute_run(udev_event, &sigmask_orig); | ||
303 | |||
304 | /* apply/restore inotify watch */ | ||
305 | if (err == 0 && udev_event->inotify_watch) { | ||
306 | udev_watch_begin(udev, dev); | ||
307 | udev_device_update_db(dev); | ||
308 | } | ||
309 | |||
310 | /* send processed event back to libudev listeners */ | ||
311 | udev_monitor_send_device(worker_monitor, NULL, dev); | ||
312 | |||
313 | /* send udevd the result of the event execution */ | ||
314 | memset(&msg, 0, sizeof(struct worker_message)); | ||
315 | if (err != 0) | ||
316 | msg.exitcode = err; | ||
317 | msg.pid = getpid(); | ||
318 | send(worker_watch[WRITE_END], &msg, sizeof(struct worker_message), 0); | ||
319 | |||
320 | info(udev, "seq %llu processed with %i\n", udev_device_get_seqnum(dev), err); | ||
321 | |||
322 | udev_device_unref(dev); | ||
323 | dev = NULL; | ||
324 | |||
325 | if (udev_event->sigterm) { | ||
326 | udev_event_unref(udev_event); | ||
327 | goto out; | ||
328 | } | ||
329 | |||
330 | udev_event_unref(udev_event); | ||
331 | |||
332 | /* wait for more device messages from main udevd, or term signal */ | ||
333 | while (dev == NULL) { | ||
334 | struct epoll_event ev[4]; | ||
335 | int fdcount; | ||
336 | int i; | ||
337 | |||
338 | fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1); | ||
339 | if (fdcount < 0) { | ||
340 | if (errno == EINTR) | ||
341 | continue; | ||
342 | err = -errno; | ||
343 | err(udev, "failed to poll: %m\n"); | ||
344 | goto out; | ||
345 | } | ||
346 | |||
347 | for (i = 0; i < fdcount; i++) { | ||
348 | if (ev[i].data.fd == fd_monitor && ev[i].events & EPOLLIN) { | ||
349 | dev = udev_monitor_receive_device(worker_monitor); | ||
350 | break; | ||
351 | } else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN) { | ||
352 | struct signalfd_siginfo fdsi; | ||
353 | ssize_t size; | ||
354 | |||
355 | size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo)); | ||
356 | if (size != sizeof(struct signalfd_siginfo)) | ||
357 | continue; | ||
358 | switch (fdsi.ssi_signo) { | ||
359 | case SIGTERM: | ||
360 | goto out; | ||
361 | } | ||
362 | } | ||
363 | } | ||
364 | } | ||
365 | } | ||
366 | out: | ||
367 | udev_device_unref(dev); | ||
368 | if (fd_signal >= 0) | ||
369 | close(fd_signal); | ||
370 | if (fd_ep >= 0) | ||
371 | close(fd_ep); | ||
372 | close(fd_inotify); | ||
373 | close(worker_watch[WRITE_END]); | ||
374 | udev_rules_unref(rules); | ||
375 | udev_builtin_exit(udev); | ||
376 | udev_monitor_unref(worker_monitor); | ||
377 | udev_unref(udev); | ||
378 | udev_log_close(); | ||
379 | exit(rc); | ||
380 | } | ||
381 | case -1: | ||
382 | udev_monitor_unref(worker_monitor); | ||
383 | event->state = EVENT_QUEUED; | ||
384 | free(worker); | ||
385 | err(udev, "fork of child failed: %m\n"); | ||
386 | break; | ||
387 | default: | ||
388 | /* close monitor, but keep address around */ | ||
389 | udev_monitor_disconnect(worker_monitor); | ||
390 | worker->monitor = worker_monitor; | ||
391 | worker->pid = pid; | ||
392 | worker->state = WORKER_RUNNING; | ||
393 | worker->event_start_usec = now_usec(); | ||
394 | worker->event = event; | ||
395 | event->state = EVENT_RUNNING; | ||
396 | udev_list_node_append(&worker->node, &worker_list); | ||
397 | children++; | ||
398 | info(udev, "seq %llu forked new worker [%u]\n", udev_device_get_seqnum(event->dev), pid); | ||
399 | break; | ||
400 | } | ||
401 | } | ||
402 | |||
403 | static void event_run(struct event *event) | ||
404 | { | ||
405 | struct udev_list_node *loop; | ||
406 | |||
407 | udev_list_node_foreach(loop, &worker_list) { | ||
408 | struct worker *worker = node_to_worker(loop); | ||
409 | ssize_t count; | ||
410 | |||
411 | if (worker->state != WORKER_IDLE) | ||
412 | continue; | ||
413 | |||
414 | count = udev_monitor_send_device(monitor, worker->monitor, event->dev); | ||
415 | if (count < 0) { | ||
416 | err(event->udev, "worker [%u] did not accept message %zi (%m), kill it\n", worker->pid, count); | ||
417 | kill(worker->pid, SIGKILL); | ||
418 | worker->state = WORKER_KILLED; | ||
419 | continue; | ||
420 | } | ||
421 | worker_ref(worker); | ||
422 | worker->event = event; | ||
423 | worker->state = WORKER_RUNNING; | ||
424 | worker->event_start_usec = now_usec(); | ||
425 | event->state = EVENT_RUNNING; | ||
426 | return; | ||
427 | } | ||
428 | |||
429 | if (children >= children_max) { | ||
430 | if (children_max > 1) | ||
431 | info(event->udev, "maximum number (%i) of children reached\n", children); | ||
432 | return; | ||
433 | } | ||
434 | |||
435 | /* start new worker and pass initial device */ | ||
436 | worker_new(event); | ||
437 | } | ||
438 | |||
439 | static int event_queue_insert(struct udev_device *dev) | ||
440 | { | ||
441 | struct event *event; | ||
442 | |||
443 | event = calloc(1, sizeof(struct event)); | ||
444 | if (event == NULL) | ||
445 | return -1; | ||
446 | |||
447 | event->udev = udev_device_get_udev(dev); | ||
448 | event->dev = dev; | ||
449 | event->seqnum = udev_device_get_seqnum(dev); | ||
450 | event->devpath = udev_device_get_devpath(dev); | ||
451 | event->devpath_len = strlen(event->devpath); | ||
452 | event->devpath_old = udev_device_get_devpath_old(dev); | ||
453 | event->devnum = udev_device_get_devnum(dev); | ||
454 | event->is_block = (strcmp("block", udev_device_get_subsystem(dev)) == 0); | ||
455 | event->ifindex = udev_device_get_ifindex(dev); | ||
456 | |||
457 | udev_queue_export_device_queued(udev_queue_export, dev); | ||
458 | info(event->udev, "seq %llu queued, '%s' '%s'\n", udev_device_get_seqnum(dev), | ||
459 | udev_device_get_action(dev), udev_device_get_subsystem(dev)); | ||
460 | |||
461 | event->state = EVENT_QUEUED; | ||
462 | udev_list_node_append(&event->node, &event_list); | ||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | static void worker_kill(struct udev *udev, int retain) | ||
467 | { | ||
468 | struct udev_list_node *loop; | ||
469 | int max; | ||
470 | |||
471 | if (children <= retain) | ||
472 | return; | ||
473 | |||
474 | max = children - retain; | ||
475 | |||
476 | udev_list_node_foreach(loop, &worker_list) { | ||
477 | struct worker *worker = node_to_worker(loop); | ||
478 | |||
479 | if (max-- <= 0) | ||
480 | break; | ||
481 | |||
482 | if (worker->state == WORKER_KILLED) | ||
483 | continue; | ||
484 | |||
485 | worker->state = WORKER_KILLED; | ||
486 | kill(worker->pid, SIGTERM); | ||
487 | } | ||
488 | } | ||
489 | |||
490 | /* lookup event for identical, parent, child device */ | ||
491 | static bool is_devpath_busy(struct event *event) | ||
492 | { | ||
493 | struct udev_list_node *loop; | ||
494 | size_t common; | ||
495 | |||
496 | /* check if queue contains events we depend on */ | ||
497 | udev_list_node_foreach(loop, &event_list) { | ||
498 | struct event *loop_event = node_to_event(loop); | ||
499 | |||
500 | /* we already found a later event, earlier can not block us, no need to check again */ | ||
501 | if (loop_event->seqnum < event->delaying_seqnum) | ||
502 | continue; | ||
503 | |||
504 | /* event we checked earlier still exists, no need to check again */ | ||
505 | if (loop_event->seqnum == event->delaying_seqnum) | ||
506 | return true; | ||
507 | |||
508 | /* found ourself, no later event can block us */ | ||
509 | if (loop_event->seqnum >= event->seqnum) | ||
510 | break; | ||
511 | |||
512 | /* check major/minor */ | ||
513 | if (major(event->devnum) != 0 && event->devnum == loop_event->devnum && event->is_block == loop_event->is_block) | ||
514 | return true; | ||
515 | |||
516 | /* check network device ifindex */ | ||
517 | if (event->ifindex != 0 && event->ifindex == loop_event->ifindex) | ||
518 | return true; | ||
519 | |||
520 | /* check our old name */ | ||
521 | if (event->devpath_old != NULL && strcmp(loop_event->devpath, event->devpath_old) == 0) { | ||
522 | event->delaying_seqnum = loop_event->seqnum; | ||
523 | return true; | ||
524 | } | ||
525 | |||
526 | /* compare devpath */ | ||
527 | common = MIN(loop_event->devpath_len, event->devpath_len); | ||
528 | |||
529 | /* one devpath is contained in the other? */ | ||
530 | if (memcmp(loop_event->devpath, event->devpath, common) != 0) | ||
531 | continue; | ||
532 | |||
533 | /* identical device event found */ | ||
534 | if (loop_event->devpath_len == event->devpath_len) { | ||
535 | /* devices names might have changed/swapped in the meantime */ | ||
536 | if (major(event->devnum) != 0 && (event->devnum != loop_event->devnum || event->is_block != loop_event->is_block)) | ||
537 | continue; | ||
538 | if (event->ifindex != 0 && event->ifindex != loop_event->ifindex) | ||
539 | continue; | ||
540 | event->delaying_seqnum = loop_event->seqnum; | ||
541 | return true; | ||
542 | } | ||
543 | |||
544 | /* parent device event found */ | ||
545 | if (event->devpath[common] == '/') { | ||
546 | event->delaying_seqnum = loop_event->seqnum; | ||
547 | return true; | ||
548 | } | ||
549 | |||
550 | /* child device event found */ | ||
551 | if (loop_event->devpath[common] == '/') { | ||
552 | event->delaying_seqnum = loop_event->seqnum; | ||
553 | return true; | ||
554 | } | ||
555 | |||
556 | /* no matching device */ | ||
557 | continue; | ||
558 | } | ||
559 | |||
560 | return false; | ||
561 | } | ||
562 | |||
563 | static void event_queue_start(struct udev *udev) | ||
564 | { | ||
565 | struct udev_list_node *loop; | ||
566 | |||
567 | udev_list_node_foreach(loop, &event_list) { | ||
568 | struct event *event = node_to_event(loop); | ||
569 | |||
570 | if (event->state != EVENT_QUEUED) | ||
571 | continue; | ||
572 | |||
573 | /* do not start event if parent or child event is still running */ | ||
574 | if (is_devpath_busy(event)) { | ||
575 | dbg(udev, "delay seq %llu (%s)\n", event->seqnum, event->devpath); | ||
576 | continue; | ||
577 | } | ||
578 | |||
579 | event_run(event); | ||
580 | } | ||
581 | } | ||
582 | |||
583 | static void event_queue_cleanup(struct udev *udev, enum event_state match_type) | ||
584 | { | ||
585 | struct udev_list_node *loop, *tmp; | ||
586 | |||
587 | udev_list_node_foreach_safe(loop, tmp, &event_list) { | ||
588 | struct event *event = node_to_event(loop); | ||
589 | |||
590 | if (match_type != EVENT_UNDEF && match_type != event->state) | ||
591 | continue; | ||
592 | |||
593 | event_queue_delete(event, false); | ||
594 | } | ||
595 | } | ||
596 | |||
597 | static void worker_returned(int fd_worker) | ||
598 | { | ||
599 | for (;;) { | ||
600 | struct worker_message msg; | ||
601 | ssize_t size; | ||
602 | struct udev_list_node *loop; | ||
603 | |||
604 | size = recv(fd_worker, &msg, sizeof(struct worker_message), MSG_DONTWAIT); | ||
605 | if (size != sizeof(struct worker_message)) | ||
606 | break; | ||
607 | |||
608 | /* lookup worker who sent the signal */ | ||
609 | udev_list_node_foreach(loop, &worker_list) { | ||
610 | struct worker *worker = node_to_worker(loop); | ||
611 | |||
612 | if (worker->pid != msg.pid) | ||
613 | continue; | ||
614 | |||
615 | /* worker returned */ | ||
616 | if (worker->event) { | ||
617 | worker->event->exitcode = msg.exitcode; | ||
618 | event_queue_delete(worker->event, true); | ||
619 | worker->event = NULL; | ||
620 | } | ||
621 | if (worker->state != WORKER_KILLED) | ||
622 | worker->state = WORKER_IDLE; | ||
623 | worker_unref(worker); | ||
624 | break; | ||
625 | } | ||
626 | } | ||
627 | } | ||
628 | |||
629 | /* receive the udevd message from userspace */ | ||
630 | static struct udev_ctrl_connection *handle_ctrl_msg(struct udev_ctrl *uctrl) | ||
631 | { | ||
632 | struct udev *udev = udev_ctrl_get_udev(uctrl); | ||
633 | struct udev_ctrl_connection *ctrl_conn; | ||
634 | struct udev_ctrl_msg *ctrl_msg = NULL; | ||
635 | const char *str; | ||
636 | int i; | ||
637 | |||
638 | ctrl_conn = udev_ctrl_get_connection(uctrl); | ||
639 | if (ctrl_conn == NULL) | ||
640 | goto out; | ||
641 | |||
642 | ctrl_msg = udev_ctrl_receive_msg(ctrl_conn); | ||
643 | if (ctrl_msg == NULL) | ||
644 | goto out; | ||
645 | |||
646 | i = udev_ctrl_get_set_log_level(ctrl_msg); | ||
647 | if (i >= 0) { | ||
648 | info(udev, "udevd message (SET_LOG_PRIORITY) received, log_priority=%i\n", i); | ||
649 | udev_set_log_priority(udev, i); | ||
650 | worker_kill(udev, 0); | ||
651 | } | ||
652 | |||
653 | if (udev_ctrl_get_stop_exec_queue(ctrl_msg) > 0) { | ||
654 | info(udev, "udevd message (STOP_EXEC_QUEUE) received\n"); | ||
655 | stop_exec_queue = true; | ||
656 | } | ||
657 | |||
658 | if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) { | ||
659 | info(udev, "udevd message (START_EXEC_QUEUE) received\n"); | ||
660 | stop_exec_queue = false; | ||
661 | } | ||
662 | |||
663 | if (udev_ctrl_get_reload(ctrl_msg) > 0) { | ||
664 | info(udev, "udevd message (RELOAD) received\n"); | ||
665 | reload = true; | ||
666 | } | ||
667 | |||
668 | str = udev_ctrl_get_set_env(ctrl_msg); | ||
669 | if (str != NULL) { | ||
670 | char *key; | ||
671 | |||
672 | key = strdup(str); | ||
673 | if (key != NULL) { | ||
674 | char *val; | ||
675 | |||
676 | val = strchr(key, '='); | ||
677 | if (val != NULL) { | ||
678 | val[0] = '\0'; | ||
679 | val = &val[1]; | ||
680 | if (val[0] == '\0') { | ||
681 | info(udev, "udevd message (ENV) received, unset '%s'\n", key); | ||
682 | udev_add_property(udev, key, NULL); | ||
683 | } else { | ||
684 | info(udev, "udevd message (ENV) received, set '%s=%s'\n", key, val); | ||
685 | udev_add_property(udev, key, val); | ||
686 | } | ||
687 | } else { | ||
688 | err(udev, "wrong key format '%s'\n", key); | ||
689 | } | ||
690 | free(key); | ||
691 | } | ||
692 | worker_kill(udev, 0); | ||
693 | } | ||
694 | |||
695 | i = udev_ctrl_get_set_children_max(ctrl_msg); | ||
696 | if (i >= 0) { | ||
697 | info(udev, "udevd message (SET_MAX_CHILDREN) received, children_max=%i\n", i); | ||
698 | children_max = i; | ||
699 | } | ||
700 | |||
701 | if (udev_ctrl_get_ping(ctrl_msg) > 0) | ||
702 | info(udev, "udevd message (SYNC) received\n"); | ||
703 | |||
704 | if (udev_ctrl_get_exit(ctrl_msg) > 0) { | ||
705 | info(udev, "udevd message (EXIT) received\n"); | ||
706 | udev_exit = true; | ||
707 | /* keep reference to block the client until we exit */ | ||
708 | udev_ctrl_connection_ref(ctrl_conn); | ||
709 | } | ||
710 | out: | ||
711 | udev_ctrl_msg_unref(ctrl_msg); | ||
712 | return udev_ctrl_connection_unref(ctrl_conn); | ||
713 | } | ||
714 | |||
715 | /* read inotify messages */ | ||
716 | static int handle_inotify(struct udev *udev) | ||
717 | { | ||
718 | int nbytes, pos; | ||
719 | char *buf; | ||
720 | struct inotify_event *ev; | ||
721 | |||
722 | if ((ioctl(fd_inotify, FIONREAD, &nbytes) < 0) || (nbytes <= 0)) | ||
723 | return 0; | ||
724 | |||
725 | buf = malloc(nbytes); | ||
726 | if (buf == NULL) { | ||
727 | err(udev, "error getting buffer for inotify\n"); | ||
728 | return -1; | ||
729 | } | ||
730 | |||
731 | nbytes = read(fd_inotify, buf, nbytes); | ||
732 | |||
733 | for (pos = 0; pos < nbytes; pos += sizeof(struct inotify_event) + ev->len) { | ||
734 | struct udev_device *dev; | ||
735 | |||
736 | ev = (struct inotify_event *)(buf + pos); | ||
737 | dev = udev_watch_lookup(udev, ev->wd); | ||
738 | if (dev != NULL) { | ||
739 | info(udev, "inotify event: %x for %s\n", ev->mask, udev_device_get_devnode(dev)); | ||
740 | if (ev->mask & IN_CLOSE_WRITE) { | ||
741 | char filename[UTIL_PATH_SIZE]; | ||
742 | int fd; | ||
743 | |||
744 | info(udev, "device %s closed, synthesising 'change'\n", udev_device_get_devnode(dev)); | ||
745 | util_strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL); | ||
746 | fd = open(filename, O_WRONLY); | ||
747 | if (fd >= 0) { | ||
748 | if (write(fd, "change", 6) < 0) | ||
749 | info(udev, "error writing uevent: %m\n"); | ||
750 | close(fd); | ||
751 | } | ||
752 | } | ||
753 | if (ev->mask & IN_IGNORED) | ||
754 | udev_watch_end(udev, dev); | ||
755 | |||
756 | udev_device_unref(dev); | ||
757 | } | ||
758 | |||
759 | } | ||
760 | |||
761 | free(buf); | ||
762 | return 0; | ||
763 | } | ||
764 | |||
765 | static void handle_signal(struct udev *udev, int signo) | ||
766 | { | ||
767 | switch (signo) { | ||
768 | case SIGINT: | ||
769 | case SIGTERM: | ||
770 | udev_exit = true; | ||
771 | break; | ||
772 | case SIGCHLD: | ||
773 | for (;;) { | ||
774 | pid_t pid; | ||
775 | int status; | ||
776 | struct udev_list_node *loop, *tmp; | ||
777 | |||
778 | pid = waitpid(-1, &status, WNOHANG); | ||
779 | if (pid <= 0) | ||
780 | break; | ||
781 | |||
782 | udev_list_node_foreach_safe(loop, tmp, &worker_list) { | ||
783 | struct worker *worker = node_to_worker(loop); | ||
784 | |||
785 | if (worker->pid != pid) | ||
786 | continue; | ||
787 | info(udev, "worker [%u] exit\n", pid); | ||
788 | |||
789 | if (WIFEXITED(status)) { | ||
790 | if (WEXITSTATUS(status) != 0) | ||
791 | err(udev, "worker [%u] exit with return code %i\n", pid, WEXITSTATUS(status)); | ||
792 | } else if (WIFSIGNALED(status)) { | ||
793 | err(udev, "worker [%u] terminated by signal %i (%s)\n", | ||
794 | pid, WTERMSIG(status), strsignal(WTERMSIG(status))); | ||
795 | } else if (WIFSTOPPED(status)) { | ||
796 | err(udev, "worker [%u] stopped\n", pid); | ||
797 | } else if (WIFCONTINUED(status)) { | ||
798 | err(udev, "worker [%u] continued\n", pid); | ||
799 | } else { | ||
800 | err(udev, "worker [%u] exit with status 0x%04x\n", pid, status); | ||
801 | } | ||
802 | |||
803 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { | ||
804 | if (worker->event) { | ||
805 | err(udev, "worker [%u] failed while handling '%s'\n", | ||
806 | pid, worker->event->devpath); | ||
807 | worker->event->exitcode = -32; | ||
808 | event_queue_delete(worker->event, true); | ||
809 | /* drop reference taken for state 'running' */ | ||
810 | worker_unref(worker); | ||
811 | } | ||
812 | } | ||
813 | worker_unref(worker); | ||
814 | break; | ||
815 | } | ||
816 | } | ||
817 | break; | ||
818 | case SIGHUP: | ||
819 | reload = true; | ||
820 | break; | ||
821 | } | ||
822 | } | ||
823 | |||
824 | static void static_dev_create_from_modules(struct udev *udev) | ||
825 | { | ||
826 | struct utsname kernel; | ||
827 | char modules[UTIL_PATH_SIZE]; | ||
828 | char buf[4096]; | ||
829 | FILE *f; | ||
830 | |||
831 | uname(&kernel); | ||
832 | util_strscpyl(modules, sizeof(modules), "/lib/modules/", kernel.release, "/modules.devname", NULL); | ||
833 | f = fopen(modules, "r"); | ||
834 | if (f == NULL) | ||
835 | return; | ||
836 | |||
837 | while (fgets(buf, sizeof(buf), f) != NULL) { | ||
838 | char *s; | ||
839 | const char *modname; | ||
840 | const char *devname; | ||
841 | const char *devno; | ||
842 | int maj, min; | ||
843 | char type; | ||
844 | mode_t mode; | ||
845 | char filename[UTIL_PATH_SIZE]; | ||
846 | |||
847 | if (buf[0] == '#') | ||
848 | continue; | ||
849 | |||
850 | modname = buf; | ||
851 | s = strchr(modname, ' '); | ||
852 | if (s == NULL) | ||
853 | continue; | ||
854 | s[0] = '\0'; | ||
855 | |||
856 | devname = &s[1]; | ||
857 | s = strchr(devname, ' '); | ||
858 | if (s == NULL) | ||
859 | continue; | ||
860 | s[0] = '\0'; | ||
861 | |||
862 | devno = &s[1]; | ||
863 | s = strchr(devno, ' '); | ||
864 | if (s == NULL) | ||
865 | s = strchr(devno, '\n'); | ||
866 | if (s != NULL) | ||
867 | s[0] = '\0'; | ||
868 | if (sscanf(devno, "%c%u:%u", &type, &maj, &min) != 3) | ||
869 | continue; | ||
870 | |||
871 | mode = 0600; | ||
872 | if (type == 'c') | ||
873 | mode |= S_IFCHR; | ||
874 | else if (type == 'b') | ||
875 | mode |= S_IFBLK; | ||
876 | else | ||
877 | continue; | ||
878 | |||
879 | util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/", devname, NULL); | ||
880 | util_create_path_selinux(udev, filename); | ||
881 | udev_selinux_setfscreatecon(udev, filename, mode); | ||
882 | info(udev, "mknod '%s' %c%u:%u\n", filename, type, maj, min); | ||
883 | if (mknod(filename, mode, makedev(maj, min)) < 0 && errno == EEXIST) | ||
884 | utimensat(AT_FDCWD, filename, NULL, 0); | ||
885 | udev_selinux_resetfscreatecon(udev); | ||
886 | } | ||
887 | |||
888 | fclose(f); | ||
889 | } | ||
890 | |||
891 | static int copy_dev_dir(struct udev *udev, DIR *dir_from, DIR *dir_to, int maxdepth) | ||
892 | { | ||
893 | struct dirent *dent; | ||
894 | |||
895 | for (dent = readdir(dir_from); dent != NULL; dent = readdir(dir_from)) { | ||
896 | struct stat stats; | ||
897 | |||
898 | if (dent->d_name[0] == '.') | ||
899 | continue; | ||
900 | if (fstatat(dirfd(dir_from), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0) | ||
901 | continue; | ||
902 | |||
903 | if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { | ||
904 | udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, stats.st_mode & 0777); | ||
905 | if (mknodat(dirfd(dir_to), dent->d_name, stats.st_mode, stats.st_rdev) == 0) { | ||
906 | fchmodat(dirfd(dir_to), dent->d_name, stats.st_mode & 0777, 0); | ||
907 | fchownat(dirfd(dir_to), dent->d_name, stats.st_uid, stats.st_gid, 0); | ||
908 | } else { | ||
909 | utimensat(dirfd(dir_to), dent->d_name, NULL, 0); | ||
910 | } | ||
911 | udev_selinux_resetfscreatecon(udev); | ||
912 | } else if (S_ISLNK(stats.st_mode)) { | ||
913 | char target[UTIL_PATH_SIZE]; | ||
914 | ssize_t len; | ||
915 | |||
916 | len = readlinkat(dirfd(dir_from), dent->d_name, target, sizeof(target)); | ||
917 | if (len <= 0 || len == (ssize_t)sizeof(target)) | ||
918 | continue; | ||
919 | target[len] = '\0'; | ||
920 | udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, S_IFLNK); | ||
921 | if (symlinkat(target, dirfd(dir_to), dent->d_name) < 0 && errno == EEXIST) | ||
922 | utimensat(dirfd(dir_to), dent->d_name, NULL, AT_SYMLINK_NOFOLLOW); | ||
923 | udev_selinux_resetfscreatecon(udev); | ||
924 | } else if (S_ISDIR(stats.st_mode)) { | ||
925 | DIR *dir2_from, *dir2_to; | ||
926 | |||
927 | if (maxdepth == 0) | ||
928 | continue; | ||
929 | |||
930 | udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, S_IFDIR|0755); | ||
931 | mkdirat(dirfd(dir_to), dent->d_name, 0755); | ||
932 | udev_selinux_resetfscreatecon(udev); | ||
933 | |||
934 | dir2_to = fdopendir(openat(dirfd(dir_to), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); | ||
935 | if (dir2_to == NULL) | ||
936 | continue; | ||
937 | |||
938 | dir2_from = fdopendir(openat(dirfd(dir_from), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); | ||
939 | if (dir2_from == NULL) { | ||
940 | closedir(dir2_to); | ||
941 | continue; | ||
942 | } | ||
943 | |||
944 | copy_dev_dir(udev, dir2_from, dir2_to, maxdepth-1); | ||
945 | |||
946 | closedir(dir2_to); | ||
947 | closedir(dir2_from); | ||
948 | } | ||
949 | } | ||
950 | |||
951 | return 0; | ||
952 | } | ||
953 | |||
954 | static void static_dev_create_links(struct udev *udev, DIR *dir) | ||
955 | { | ||
956 | struct stdlinks { | ||
957 | const char *link; | ||
958 | const char *target; | ||
959 | }; | ||
960 | static const struct stdlinks stdlinks[] = { | ||
961 | { "core", "/proc/kcore" }, | ||
962 | { "fd", "/proc/self/fd" }, | ||
963 | { "stdin", "/proc/self/fd/0" }, | ||
964 | { "stdout", "/proc/self/fd/1" }, | ||
965 | { "stderr", "/proc/self/fd/2" }, | ||
966 | }; | ||
967 | unsigned int i; | ||
968 | |||
969 | for (i = 0; i < ARRAY_SIZE(stdlinks); i++) { | ||
970 | struct stat sb; | ||
971 | |||
972 | if (stat(stdlinks[i].target, &sb) == 0) { | ||
973 | udev_selinux_setfscreateconat(udev, dirfd(dir), stdlinks[i].link, S_IFLNK); | ||
974 | if (symlinkat(stdlinks[i].target, dirfd(dir), stdlinks[i].link) < 0 && errno == EEXIST) | ||
975 | utimensat(dirfd(dir), stdlinks[i].link, NULL, AT_SYMLINK_NOFOLLOW); | ||
976 | udev_selinux_resetfscreatecon(udev); | ||
977 | } | ||
978 | } | ||
979 | } | ||
980 | |||
981 | static void static_dev_create_from_devices(struct udev *udev, DIR *dir) | ||
982 | { | ||
983 | DIR *dir_from; | ||
984 | |||
985 | dir_from = opendir(PKGLIBEXECDIR "/devices"); | ||
986 | if (dir_from == NULL) | ||
987 | return; | ||
988 | copy_dev_dir(udev, dir_from, dir, 8); | ||
989 | closedir(dir_from); | ||
990 | } | ||
991 | |||
992 | static void static_dev_create(struct udev *udev) | ||
993 | { | ||
994 | DIR *dir; | ||
995 | |||
996 | dir = opendir(udev_get_dev_path(udev)); | ||
997 | if (dir == NULL) | ||
998 | return; | ||
999 | |||
1000 | static_dev_create_links(udev, dir); | ||
1001 | static_dev_create_from_devices(udev, dir); | ||
1002 | |||
1003 | closedir(dir); | ||
1004 | } | ||
1005 | |||
1006 | static int mem_size_mb(void) | ||
1007 | { | ||
1008 | FILE *f; | ||
1009 | char buf[4096]; | ||
1010 | long int memsize = -1; | ||
1011 | |||
1012 | f = fopen("/proc/meminfo", "r"); | ||
1013 | if (f == NULL) | ||
1014 | return -1; | ||
1015 | |||
1016 | while (fgets(buf, sizeof(buf), f) != NULL) { | ||
1017 | long int value; | ||
1018 | |||
1019 | if (sscanf(buf, "MemTotal: %ld kB", &value) == 1) { | ||
1020 | memsize = value / 1024; | ||
1021 | break; | ||
1022 | } | ||
1023 | } | ||
1024 | |||
1025 | fclose(f); | ||
1026 | return memsize; | ||
1027 | } | ||
1028 | |||
1029 | static int convert_db(struct udev *udev) | ||
1030 | { | ||
1031 | char filename[UTIL_PATH_SIZE]; | ||
1032 | FILE *f; | ||
1033 | struct udev_enumerate *udev_enumerate; | ||
1034 | struct udev_list_entry *list_entry; | ||
1035 | |||
1036 | /* current database */ | ||
1037 | util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data", NULL); | ||
1038 | if (access(filename, F_OK) >= 0) | ||
1039 | return 0; | ||
1040 | |||
1041 | /* make sure we do not get here again */ | ||
1042 | util_create_path(udev, filename); | ||
1043 | mkdir(filename, 0755); | ||
1044 | |||
1045 | /* old database */ | ||
1046 | util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/db", NULL); | ||
1047 | if (access(filename, F_OK) < 0) | ||
1048 | return 0; | ||
1049 | |||
1050 | print_kmsg("converting old udev database\n"); | ||
1051 | |||
1052 | udev_enumerate = udev_enumerate_new(udev); | ||
1053 | if (udev_enumerate == NULL) | ||
1054 | return -1; | ||
1055 | udev_enumerate_scan_devices(udev_enumerate); | ||
1056 | udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) { | ||
1057 | struct udev_device *device; | ||
1058 | |||
1059 | device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry)); | ||
1060 | if (device == NULL) | ||
1061 | continue; | ||
1062 | |||
1063 | /* try to find the old database for devices without a current one */ | ||
1064 | if (udev_device_read_db(device, NULL) < 0) { | ||
1065 | bool have_db; | ||
1066 | const char *id; | ||
1067 | struct stat stats; | ||
1068 | char devpath[UTIL_PATH_SIZE]; | ||
1069 | char from[UTIL_PATH_SIZE]; | ||
1070 | |||
1071 | have_db = false; | ||
1072 | |||
1073 | /* find database in old location */ | ||
1074 | id = udev_device_get_id_filename(device); | ||
1075 | util_strscpyl(from, sizeof(from), udev_get_dev_path(udev), "/.udev/db/", id, NULL); | ||
1076 | if (lstat(from, &stats) == 0) { | ||
1077 | if (!have_db) { | ||
1078 | udev_device_read_db(device, from); | ||
1079 | have_db = true; | ||
1080 | } | ||
1081 | unlink(from); | ||
1082 | } | ||
1083 | |||
1084 | /* find old database with $subsys:$sysname name */ | ||
1085 | util_strscpyl(from, sizeof(from), udev_get_dev_path(udev), | ||
1086 | "/.udev/db/", udev_device_get_subsystem(device), ":", | ||
1087 | udev_device_get_sysname(device), NULL); | ||
1088 | if (lstat(from, &stats) == 0) { | ||
1089 | if (!have_db) { | ||
1090 | udev_device_read_db(device, from); | ||
1091 | have_db = true; | ||
1092 | } | ||
1093 | unlink(from); | ||
1094 | } | ||
1095 | |||
1096 | /* find old database with the encoded devpath name */ | ||
1097 | util_path_encode(udev_device_get_devpath(device), devpath, sizeof(devpath)); | ||
1098 | util_strscpyl(from, sizeof(from), udev_get_dev_path(udev), "/.udev/db/", devpath, NULL); | ||
1099 | if (lstat(from, &stats) == 0) { | ||
1100 | if (!have_db) { | ||
1101 | udev_device_read_db(device, from); | ||
1102 | have_db = true; | ||
1103 | } | ||
1104 | unlink(from); | ||
1105 | } | ||
1106 | |||
1107 | /* write out new database */ | ||
1108 | if (have_db) | ||
1109 | udev_device_update_db(device); | ||
1110 | } | ||
1111 | udev_device_unref(device); | ||
1112 | } | ||
1113 | udev_enumerate_unref(udev_enumerate); | ||
1114 | return 0; | ||
1115 | } | ||
1116 | |||
1117 | static bool check_rules_timestamp(struct udev *udev) | ||
1118 | { | ||
1119 | char **p; | ||
1120 | unsigned long long *stamp_usec; | ||
1121 | int i, n; | ||
1122 | bool changed = false; | ||
1123 | |||
1124 | n = udev_get_rules_path(udev, &p, &stamp_usec); | ||
1125 | for (i = 0; i < n; i++) { | ||
1126 | struct stat stats; | ||
1127 | |||
1128 | if (stat(p[i], &stats) < 0) | ||
1129 | continue; | ||
1130 | |||
1131 | if (stamp_usec[i] == ts_usec(&stats.st_mtim)) | ||
1132 | continue; | ||
1133 | |||
1134 | /* first check */ | ||
1135 | if (stamp_usec[i] != 0) { | ||
1136 | info(udev, "reload - timestamp of '%s' changed\n", p[i]); | ||
1137 | changed = true; | ||
1138 | } | ||
1139 | |||
1140 | /* update timestamp */ | ||
1141 | stamp_usec[i] = ts_usec(&stats.st_mtim); | ||
1142 | } | ||
1143 | |||
1144 | return changed; | ||
1145 | } | ||
1146 | |||
1147 | /* | ||
1148 | * read the kernel commandline, in case we need to get into debug mode | ||
1149 | * udev.log-priority=<level> syslog priority | ||
1150 | * udev.children-max=<number of workers> events are fully serialized if set to 1 | ||
1151 | * udev.exec-delay=<number of seconds> delay execution of every executed program | ||
1152 | */ | ||
1153 | |||
1154 | static void kernel_cmdline_opts(struct udev *udev) | ||
1155 | { | ||
1156 | FILE *f; | ||
1157 | |||
1158 | f = fopen("/proc/cmdline", "r"); | ||
1159 | if (f != NULL) { | ||
1160 | char cmdline[4096]; | ||
1161 | |||
1162 | if (fgets(cmdline, sizeof(cmdline), f) != NULL) { | ||
1163 | char *pos; | ||
1164 | |||
1165 | pos = strstr(cmdline, "udev.log-priority="); | ||
1166 | if (pos != NULL) { | ||
1167 | pos += strlen("udev.log-priority="); | ||
1168 | udev_set_log_priority(udev, util_log_priority(pos)); | ||
1169 | } | ||
1170 | |||
1171 | pos = strstr(cmdline, "udev.children-max="); | ||
1172 | if (pos != NULL) { | ||
1173 | pos += strlen("udev.children-max="); | ||
1174 | children_max = strtoul(pos, NULL, 0); | ||
1175 | } | ||
1176 | |||
1177 | pos = strstr(cmdline, "udev.exec-delay="); | ||
1178 | if (pos != NULL) { | ||
1179 | pos += strlen("udev.exec-delay="); | ||
1180 | exec_delay = strtoul(pos, NULL, 0); | ||
1181 | } | ||
1182 | } | ||
1183 | fclose(f); | ||
1184 | } | ||
1185 | } | ||
1186 | |||
1187 | int main(int argc, char *argv[]) | ||
1188 | { | ||
1189 | struct udev *udev; | ||
1190 | FILE *f; | ||
1191 | sigset_t mask; | ||
1192 | int daemonize = false; | ||
1193 | int resolve_names = 1; | ||
1194 | static const struct option options[] = { | ||
1195 | { "daemon", no_argument, NULL, 'd' }, | ||
1196 | { "debug", no_argument, NULL, 'D' }, | ||
1197 | { "children-max", required_argument, NULL, 'c' }, | ||
1198 | { "exec-delay", required_argument, NULL, 'e' }, | ||
1199 | { "resolve-names", required_argument, NULL, 'N' }, | ||
1200 | { "help", no_argument, NULL, 'h' }, | ||
1201 | { "version", no_argument, NULL, 'V' }, | ||
1202 | {} | ||
1203 | }; | ||
1204 | int fd_ctrl = -1; | ||
1205 | int fd_netlink = -1; | ||
1206 | int fd_worker = -1; | ||
1207 | struct epoll_event ep_ctrl, ep_inotify, ep_signal, ep_netlink, ep_worker; | ||
1208 | struct udev_ctrl_connection *ctrl_conn = NULL; | ||
1209 | char **s; | ||
1210 | int rc = 1; | ||
1211 | |||
1212 | udev = udev_new(); | ||
1213 | if (udev == NULL) | ||
1214 | goto exit; | ||
1215 | |||
1216 | udev_log_init("udevd"); | ||
1217 | udev_set_log_fn(udev, udev_main_log); | ||
1218 | info(udev, "version %s\n", VERSION); | ||
1219 | udev_selinux_init(udev); | ||
1220 | |||
1221 | for (;;) { | ||
1222 | int option; | ||
1223 | |||
1224 | option = getopt_long(argc, argv, "c:deDtN:hV", options, NULL); | ||
1225 | if (option == -1) | ||
1226 | break; | ||
1227 | |||
1228 | switch (option) { | ||
1229 | case 'd': | ||
1230 | daemonize = true; | ||
1231 | break; | ||
1232 | case 'c': | ||
1233 | children_max = strtoul(optarg, NULL, 0); | ||
1234 | break; | ||
1235 | case 'e': | ||
1236 | exec_delay = strtoul(optarg, NULL, 0); | ||
1237 | break; | ||
1238 | case 'D': | ||
1239 | debug = true; | ||
1240 | if (udev_get_log_priority(udev) < LOG_INFO) | ||
1241 | udev_set_log_priority(udev, LOG_INFO); | ||
1242 | break; | ||
1243 | case 'N': | ||
1244 | if (strcmp (optarg, "early") == 0) { | ||
1245 | resolve_names = 1; | ||
1246 | } else if (strcmp (optarg, "late") == 0) { | ||
1247 | resolve_names = 0; | ||
1248 | } else if (strcmp (optarg, "never") == 0) { | ||
1249 | resolve_names = -1; | ||
1250 | } else { | ||
1251 | fprintf(stderr, "resolve-names must be early, late or never\n"); | ||
1252 | err(udev, "resolve-names must be early, late or never\n"); | ||
1253 | goto exit; | ||
1254 | } | ||
1255 | break; | ||
1256 | case 'h': | ||
1257 | printf("Usage: udevd OPTIONS\n" | ||
1258 | " --daemon\n" | ||
1259 | " --debug\n" | ||
1260 | " --children-max=<maximum number of workers>\n" | ||
1261 | " --exec-delay=<seconds to wait before executing RUN=>\n" | ||
1262 | " --resolve-names=early|late|never\n" | ||
1263 | " --version\n" | ||
1264 | " --help\n" | ||
1265 | "\n"); | ||
1266 | goto exit; | ||
1267 | case 'V': | ||
1268 | printf("%s\n", VERSION); | ||
1269 | goto exit; | ||
1270 | default: | ||
1271 | goto exit; | ||
1272 | } | ||
1273 | } | ||
1274 | |||
1275 | kernel_cmdline_opts(udev); | ||
1276 | |||
1277 | if (getuid() != 0) { | ||
1278 | fprintf(stderr, "root privileges required\n"); | ||
1279 | err(udev, "root privileges required\n"); | ||
1280 | goto exit; | ||
1281 | } | ||
1282 | |||
1283 | /* set umask before creating any file/directory */ | ||
1284 | chdir("/"); | ||
1285 | umask(022); | ||
1286 | |||
1287 | /* /run/udev */ | ||
1288 | mkdir(udev_get_run_path(udev), 0755); | ||
1289 | |||
1290 | /* create standard links, copy static nodes, create nodes from modules */ | ||
1291 | static_dev_create(udev); | ||
1292 | static_dev_create_from_modules(udev); | ||
1293 | |||
1294 | /* before opening new files, make sure std{in,out,err} fds are in a sane state */ | ||
1295 | if (daemonize) { | ||
1296 | int fd; | ||
1297 | |||
1298 | fd = open("/dev/null", O_RDWR); | ||
1299 | if (fd >= 0) { | ||
1300 | if (write(STDOUT_FILENO, 0, 0) < 0) | ||
1301 | dup2(fd, STDOUT_FILENO); | ||
1302 | if (write(STDERR_FILENO, 0, 0) < 0) | ||
1303 | dup2(fd, STDERR_FILENO); | ||
1304 | if (fd > STDERR_FILENO) | ||
1305 | close(fd); | ||
1306 | } else { | ||
1307 | fprintf(stderr, "cannot open /dev/null\n"); | ||
1308 | err(udev, "cannot open /dev/null\n"); | ||
1309 | } | ||
1310 | } | ||
1311 | |||
1312 | udev_ctrl = udev_ctrl_new(udev); | ||
1313 | if (udev_ctrl == NULL) { | ||
1314 | fprintf(stderr, "error initializing udev control socket"); | ||
1315 | err(udev, "error initializing udev control socket"); | ||
1316 | rc = 1; | ||
1317 | goto exit; | ||
1318 | } | ||
1319 | fd_ctrl = udev_ctrl_get_fd(udev_ctrl); | ||
1320 | |||
1321 | monitor = udev_monitor_new_from_netlink(udev, "kernel"); | ||
1322 | if (monitor == NULL) { | ||
1323 | fprintf(stderr, "error initializing netlink socket\n"); | ||
1324 | err(udev, "error initializing netlink socket\n"); | ||
1325 | rc = 3; | ||
1326 | goto exit; | ||
1327 | } | ||
1328 | fd_netlink = udev_monitor_get_fd(monitor); | ||
1329 | |||
1330 | if (udev_monitor_enable_receiving(monitor) < 0) { | ||
1331 | fprintf(stderr, "error binding netlink socket\n"); | ||
1332 | err(udev, "error binding netlink socket\n"); | ||
1333 | rc = 3; | ||
1334 | goto exit; | ||
1335 | } | ||
1336 | |||
1337 | if (udev_ctrl_enable_receiving(udev_ctrl) < 0) { | ||
1338 | fprintf(stderr, "error binding udev control socket\n"); | ||
1339 | err(udev, "error binding udev control socket\n"); | ||
1340 | rc = 1; | ||
1341 | goto exit; | ||
1342 | } | ||
1343 | |||
1344 | udev_monitor_set_receive_buffer_size(monitor, 128*1024*1024); | ||
1345 | |||
1346 | /* create queue file before signalling 'ready', to make sure we block 'settle' */ | ||
1347 | udev_queue_export = udev_queue_export_new(udev); | ||
1348 | if (udev_queue_export == NULL) { | ||
1349 | err(udev, "error creating queue file\n"); | ||
1350 | goto exit; | ||
1351 | } | ||
1352 | |||
1353 | if (daemonize) { | ||
1354 | pid_t pid; | ||
1355 | int fd; | ||
1356 | |||
1357 | pid = fork(); | ||
1358 | switch (pid) { | ||
1359 | case 0: | ||
1360 | break; | ||
1361 | case -1: | ||
1362 | err(udev, "fork of daemon failed: %m\n"); | ||
1363 | rc = 4; | ||
1364 | goto exit; | ||
1365 | default: | ||
1366 | rc = EXIT_SUCCESS; | ||
1367 | goto exit_daemonize; | ||
1368 | } | ||
1369 | |||
1370 | setsid(); | ||
1371 | |||
1372 | fd = open("/proc/self/oom_score_adj", O_RDWR|O_CLOEXEC); | ||
1373 | if (fd >= 0) { | ||
1374 | write(fd, "-1000", 5); | ||
1375 | close(fd); | ||
1376 | } | ||
1377 | } | ||
1378 | |||
1379 | print_kmsg("starting version " VERSION "\n"); | ||
1380 | |||
1381 | if (!debug) { | ||
1382 | int fd; | ||
1383 | |||
1384 | fd = open("/dev/null", O_RDWR); | ||
1385 | if (fd >= 0) { | ||
1386 | dup2(fd, STDIN_FILENO); | ||
1387 | dup2(fd, STDOUT_FILENO); | ||
1388 | dup2(fd, STDERR_FILENO); | ||
1389 | close(fd); | ||
1390 | } | ||
1391 | } | ||
1392 | |||
1393 | fd_inotify = udev_watch_init(udev); | ||
1394 | if (fd_inotify < 0) { | ||
1395 | fprintf(stderr, "error initializing inotify\n"); | ||
1396 | err(udev, "error initializing inotify\n"); | ||
1397 | rc = 4; | ||
1398 | goto exit; | ||
1399 | } | ||
1400 | udev_watch_restore(udev); | ||
1401 | |||
1402 | /* block and listen to all signals on signalfd */ | ||
1403 | sigfillset(&mask); | ||
1404 | sigprocmask(SIG_SETMASK, &mask, &sigmask_orig); | ||
1405 | fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); | ||
1406 | if (fd_signal < 0) { | ||
1407 | fprintf(stderr, "error creating signalfd\n"); | ||
1408 | err(udev, "error creating signalfd\n"); | ||
1409 | rc = 5; | ||
1410 | goto exit; | ||
1411 | } | ||
1412 | |||
1413 | /* unnamed socket from workers to the main daemon */ | ||
1414 | if (socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, worker_watch) < 0) { | ||
1415 | fprintf(stderr, "error creating socketpair\n"); | ||
1416 | err(udev, "error creating socketpair\n"); | ||
1417 | rc = 6; | ||
1418 | goto exit; | ||
1419 | } | ||
1420 | fd_worker = worker_watch[READ_END]; | ||
1421 | |||
1422 | udev_builtin_init(udev); | ||
1423 | |||
1424 | rules = udev_rules_new(udev, resolve_names); | ||
1425 | if (rules == NULL) { | ||
1426 | err(udev, "error reading rules\n"); | ||
1427 | goto exit; | ||
1428 | } | ||
1429 | |||
1430 | memset(&ep_ctrl, 0, sizeof(struct epoll_event)); | ||
1431 | ep_ctrl.events = EPOLLIN; | ||
1432 | ep_ctrl.data.fd = fd_ctrl; | ||
1433 | |||
1434 | memset(&ep_inotify, 0, sizeof(struct epoll_event)); | ||
1435 | ep_inotify.events = EPOLLIN; | ||
1436 | ep_inotify.data.fd = fd_inotify; | ||
1437 | |||
1438 | memset(&ep_signal, 0, sizeof(struct epoll_event)); | ||
1439 | ep_signal.events = EPOLLIN; | ||
1440 | ep_signal.data.fd = fd_signal; | ||
1441 | |||
1442 | memset(&ep_netlink, 0, sizeof(struct epoll_event)); | ||
1443 | ep_netlink.events = EPOLLIN; | ||
1444 | ep_netlink.data.fd = fd_netlink; | ||
1445 | |||
1446 | memset(&ep_worker, 0, sizeof(struct epoll_event)); | ||
1447 | ep_worker.events = EPOLLIN; | ||
1448 | ep_worker.data.fd = fd_worker; | ||
1449 | |||
1450 | fd_ep = epoll_create1(EPOLL_CLOEXEC); | ||
1451 | if (fd_ep < 0) { | ||
1452 | err(udev, "error creating epoll fd: %m\n"); | ||
1453 | goto exit; | ||
1454 | } | ||
1455 | if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_ctrl, &ep_ctrl) < 0 || | ||
1456 | epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_inotify, &ep_inotify) < 0 || | ||
1457 | epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 || | ||
1458 | epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_netlink, &ep_netlink) < 0 || | ||
1459 | epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_worker, &ep_worker) < 0) { | ||
1460 | err(udev, "fail to add fds to epoll: %m\n"); | ||
1461 | goto exit; | ||
1462 | } | ||
1463 | |||
1464 | /* if needed, convert old database from earlier udev version */ | ||
1465 | convert_db(udev); | ||
1466 | |||
1467 | if (children_max <= 0) { | ||
1468 | int memsize = mem_size_mb(); | ||
1469 | |||
1470 | /* set value depending on the amount of RAM */ | ||
1471 | if (memsize > 0) | ||
1472 | children_max = 16 + (memsize / 8); | ||
1473 | else | ||
1474 | children_max = 16; | ||
1475 | } | ||
1476 | info(udev, "set children_max to %u\n", children_max); | ||
1477 | |||
1478 | udev_rules_apply_static_dev_perms(rules); | ||
1479 | |||
1480 | udev_list_node_init(&event_list); | ||
1481 | udev_list_node_init(&worker_list); | ||
1482 | |||
1483 | for (;;) { | ||
1484 | static unsigned long long last_usec; | ||
1485 | struct epoll_event ev[8]; | ||
1486 | int fdcount; | ||
1487 | int timeout; | ||
1488 | bool is_worker, is_signal, is_inotify, is_netlink, is_ctrl; | ||
1489 | int i; | ||
1490 | |||
1491 | if (udev_exit) { | ||
1492 | /* close sources of new events and discard buffered events */ | ||
1493 | if (fd_ctrl >= 0) { | ||
1494 | epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_ctrl, NULL); | ||
1495 | fd_ctrl = -1; | ||
1496 | } | ||
1497 | if (monitor != NULL) { | ||
1498 | epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_netlink, NULL); | ||
1499 | udev_monitor_unref(monitor); | ||
1500 | monitor = NULL; | ||
1501 | } | ||
1502 | if (fd_inotify >= 0) { | ||
1503 | epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_inotify, NULL); | ||
1504 | close(fd_inotify); | ||
1505 | fd_inotify = -1; | ||
1506 | } | ||
1507 | |||
1508 | /* discard queued events and kill workers */ | ||
1509 | event_queue_cleanup(udev, EVENT_QUEUED); | ||
1510 | worker_kill(udev, 0); | ||
1511 | |||
1512 | /* exit after all has cleaned up */ | ||
1513 | if (udev_list_node_is_empty(&event_list) && udev_list_node_is_empty(&worker_list)) | ||
1514 | break; | ||
1515 | |||
1516 | /* timeout at exit for workers to finish */ | ||
1517 | timeout = 30 * 1000; | ||
1518 | } else if (udev_list_node_is_empty(&event_list) && children <= 2) { | ||
1519 | /* we are idle */ | ||
1520 | timeout = -1; | ||
1521 | } else { | ||
1522 | /* kill idle or hanging workers */ | ||
1523 | timeout = 3 * 1000; | ||
1524 | } | ||
1525 | fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), timeout); | ||
1526 | if (fdcount < 0) | ||
1527 | continue; | ||
1528 | |||
1529 | if (fdcount == 0) { | ||
1530 | struct udev_list_node *loop; | ||
1531 | |||
1532 | /* timeout */ | ||
1533 | if (udev_exit) { | ||
1534 | err(udev, "timeout, giving up waiting for workers to finish\n"); | ||
1535 | break; | ||
1536 | } | ||
1537 | |||
1538 | /* kill idle workers */ | ||
1539 | if (udev_list_node_is_empty(&event_list)) { | ||
1540 | info(udev, "cleanup idle workers\n"); | ||
1541 | worker_kill(udev, 2); | ||
1542 | } | ||
1543 | |||
1544 | /* check for hanging events */ | ||
1545 | udev_list_node_foreach(loop, &worker_list) { | ||
1546 | struct worker *worker = node_to_worker(loop); | ||
1547 | |||
1548 | if (worker->state != WORKER_RUNNING) | ||
1549 | continue; | ||
1550 | |||
1551 | if ((now_usec() - worker->event_start_usec) > 30 * 1000 * 1000) { | ||
1552 | err(udev, "worker [%u] timeout, kill it\n", worker->pid, | ||
1553 | worker->event ? worker->event->devpath : "<idle>"); | ||
1554 | kill(worker->pid, SIGKILL); | ||
1555 | worker->state = WORKER_KILLED; | ||
1556 | /* drop reference taken for state 'running' */ | ||
1557 | worker_unref(worker); | ||
1558 | if (worker->event) { | ||
1559 | err(udev, "seq %llu '%s' killed\n", | ||
1560 | udev_device_get_seqnum(worker->event->dev), worker->event->devpath); | ||
1561 | worker->event->exitcode = -64; | ||
1562 | event_queue_delete(worker->event, true); | ||
1563 | worker->event = NULL; | ||
1564 | } | ||
1565 | } | ||
1566 | } | ||
1567 | |||
1568 | } | ||
1569 | |||
1570 | is_worker = is_signal = is_inotify = is_netlink = is_ctrl = false; | ||
1571 | for (i = 0; i < fdcount; i++) { | ||
1572 | if (ev[i].data.fd == fd_worker && ev[i].events & EPOLLIN) | ||
1573 | is_worker = true; | ||
1574 | else if (ev[i].data.fd == fd_netlink && ev[i].events & EPOLLIN) | ||
1575 | is_netlink = true; | ||
1576 | else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN) | ||
1577 | is_signal = true; | ||
1578 | else if (ev[i].data.fd == fd_inotify && ev[i].events & EPOLLIN) | ||
1579 | is_inotify = true; | ||
1580 | else if (ev[i].data.fd == fd_ctrl && ev[i].events & EPOLLIN) | ||
1581 | is_ctrl = true; | ||
1582 | } | ||
1583 | |||
1584 | /* check for changed config, every 3 seconds at most */ | ||
1585 | if ((now_usec() - last_usec) > 3 * 1000 * 1000) { | ||
1586 | if (check_rules_timestamp(udev)) | ||
1587 | reload = true; | ||
1588 | if (udev_builtin_validate(udev)) | ||
1589 | reload = true; | ||
1590 | |||
1591 | last_usec = now_usec(); | ||
1592 | } | ||
1593 | |||
1594 | /* reload requested, HUP signal received, rules changed, builtin changed */ | ||
1595 | if (reload) { | ||
1596 | worker_kill(udev, 0); | ||
1597 | rules = udev_rules_unref(rules); | ||
1598 | udev_builtin_exit(udev); | ||
1599 | reload = 0; | ||
1600 | } | ||
1601 | |||
1602 | /* event has finished */ | ||
1603 | if (is_worker) | ||
1604 | worker_returned(fd_worker); | ||
1605 | |||
1606 | if (is_netlink) { | ||
1607 | struct udev_device *dev; | ||
1608 | |||
1609 | dev = udev_monitor_receive_device(monitor); | ||
1610 | if (dev != NULL) { | ||
1611 | udev_device_set_usec_initialized(dev, now_usec()); | ||
1612 | if (event_queue_insert(dev) < 0) | ||
1613 | udev_device_unref(dev); | ||
1614 | } | ||
1615 | } | ||
1616 | |||
1617 | /* start new events */ | ||
1618 | if (!udev_list_node_is_empty(&event_list) && !udev_exit && !stop_exec_queue) { | ||
1619 | if (rules == NULL) | ||
1620 | rules = udev_rules_new(udev, resolve_names); | ||
1621 | if (rules != NULL) | ||
1622 | event_queue_start(udev); | ||
1623 | } | ||
1624 | |||
1625 | if (is_signal) { | ||
1626 | struct signalfd_siginfo fdsi; | ||
1627 | ssize_t size; | ||
1628 | |||
1629 | size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo)); | ||
1630 | if (size == sizeof(struct signalfd_siginfo)) | ||
1631 | handle_signal(udev, fdsi.ssi_signo); | ||
1632 | } | ||
1633 | |||
1634 | /* we are shutting down, the events below are not handled anymore */ | ||
1635 | if (udev_exit) | ||
1636 | continue; | ||
1637 | |||
1638 | /* device node watch */ | ||
1639 | if (is_inotify) | ||
1640 | handle_inotify(udev); | ||
1641 | |||
1642 | /* | ||
1643 | * This needs to be after the inotify handling, to make sure, | ||
1644 | * that the ping is send back after the possibly generated | ||
1645 | * "change" events by the inotify device node watch. | ||
1646 | * | ||
1647 | * A single time we may receive a client connection which we need to | ||
1648 | * keep open to block the client. It will be closed right before we | ||
1649 | * exit. | ||
1650 | */ | ||
1651 | if (is_ctrl) | ||
1652 | ctrl_conn = handle_ctrl_msg(udev_ctrl); | ||
1653 | } | ||
1654 | |||
1655 | rc = EXIT_SUCCESS; | ||
1656 | exit: | ||
1657 | udev_queue_export_cleanup(udev_queue_export); | ||
1658 | udev_ctrl_cleanup(udev_ctrl); | ||
1659 | exit_daemonize: | ||
1660 | if (fd_ep >= 0) | ||
1661 | close(fd_ep); | ||
1662 | worker_list_cleanup(udev); | ||
1663 | event_queue_cleanup(udev, EVENT_UNDEF); | ||
1664 | udev_rules_unref(rules); | ||
1665 | udev_builtin_exit(udev); | ||
1666 | if (fd_signal >= 0) | ||
1667 | close(fd_signal); | ||
1668 | if (worker_watch[READ_END] >= 0) | ||
1669 | close(worker_watch[READ_END]); | ||
1670 | if (worker_watch[WRITE_END] >= 0) | ||
1671 | close(worker_watch[WRITE_END]); | ||
1672 | udev_monitor_unref(monitor); | ||
1673 | udev_queue_export_unref(udev_queue_export); | ||
1674 | udev_ctrl_connection_unref(ctrl_conn); | ||
1675 | udev_ctrl_unref(udev_ctrl); | ||
1676 | udev_selinux_exit(udev); | ||
1677 | udev_unref(udev); | ||
1678 | udev_log_close(); | ||
1679 | return rc; | ||
1680 | } |
File src/udevd.xml added (mode: 100644) (index 0000000..c516eb9) | |||
1 | <?xml version='1.0'?> | ||
2 | <?xml-stylesheet type="text/xsl" href="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl"?> | ||
3 | <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" | ||
4 | "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> | ||
5 | |||
6 | <refentry id="udevd"> | ||
7 | <refentryinfo> | ||
8 | <title>udevd</title> | ||
9 | <productname>udev</productname> | ||
10 | </refentryinfo> | ||
11 | |||
12 | <refmeta> | ||
13 | <refentrytitle>udevd</refentrytitle> | ||
14 | <manvolnum>8</manvolnum> | ||
15 | <refmiscinfo class="version"></refmiscinfo> | ||
16 | </refmeta> | ||
17 | |||
18 | <refnamediv> | ||
19 | <refname>udevd</refname><refpurpose>event managing daemon</refpurpose> | ||
20 | </refnamediv> | ||
21 | |||
22 | <refsynopsisdiv> | ||
23 | <cmdsynopsis> | ||
24 | <command>udevd</command> | ||
25 | <arg><option>--daemon</option></arg> | ||
26 | <arg><option>--debug</option></arg> | ||
27 | <arg><option>--children-max=</option></arg> | ||
28 | <arg><option>--exec-delay=</option></arg> | ||
29 | <arg><option>--resolve-names=early|late|never</option></arg> | ||
30 | <arg><option>--version</option></arg> | ||
31 | <arg><option>--help</option></arg> | ||
32 | </cmdsynopsis> | ||
33 | </refsynopsisdiv> | ||
34 | |||
35 | <refsect1><title>Description</title> | ||
36 | <para>udevd listens to kernel uevents. For every event, udevd executes matching | ||
37 | instructions specified in udev rules. See <citerefentry> | ||
38 | <refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum> | ||
39 | </citerefentry>.</para> | ||
40 | <para>On startup the content of the directory <filename>/usr/lib/udev/devices</filename> | ||
41 | is copied to <filename>/dev</filename>. If kernel modules specify static device | ||
42 | nodes, these nodes are created even without a corresponding kernel device, to | ||
43 | allow on-demand loading of kernel modules. Matching permissions specified in udev | ||
44 | rules are applied to these static device nodes.</para> | ||
45 | <para>The behavior of the running daemon can be changed with | ||
46 | <command>udevadm control</command>.</para> | ||
47 | </refsect1> | ||
48 | |||
49 | <refsect1><title>Options</title> | ||
50 | <variablelist> | ||
51 | <varlistentry> | ||
52 | <term><option>--daemon</option></term> | ||
53 | <listitem> | ||
54 | <para>Detach and run in the background.</para> | ||
55 | </listitem> | ||
56 | </varlistentry> | ||
57 | <varlistentry> | ||
58 | <term><option>--debug</option></term> | ||
59 | <listitem> | ||
60 | <para>Print debug messages to stderr.</para> | ||
61 | </listitem> | ||
62 | </varlistentry> | ||
63 | <varlistentry> | ||
64 | <term><option>--children-max=</option></term> | ||
65 | <listitem> | ||
66 | <para>Limit the number of parallel executed events.</para> | ||
67 | </listitem> | ||
68 | </varlistentry> | ||
69 | <varlistentry> | ||
70 | <term><option>--exec-delay=</option></term> | ||
71 | <listitem> | ||
72 | <para>Number of seconds to delay the execution of RUN instructions. | ||
73 | This might be useful when debugging system crashes during coldplug | ||
74 | cause by loading non-working kernel modules.</para> | ||
75 | </listitem> | ||
76 | </varlistentry> | ||
77 | <varlistentry> | ||
78 | <term><option>--resolve-names=</option></term> | ||
79 | <listitem> | ||
80 | <para>Specify when udevd should resolve names of users and groups. | ||
81 | When set to <option>early</option> (the default) names will be | ||
82 | resolved when the rules are parsed. When set to | ||
83 | <option>late</option> names will be resolved for every event. | ||
84 | When set to <option>never</option> names will never be resolved | ||
85 | and all devices will be owned by root.</para> | ||
86 | </listitem> | ||
87 | </varlistentry> | ||
88 | <varlistentry> | ||
89 | <term><option>--version</option></term> | ||
90 | <listitem> | ||
91 | <para>Print version number.</para> | ||
92 | </listitem> | ||
93 | </varlistentry> | ||
94 | <varlistentry> | ||
95 | <term><option>--help</option></term> | ||
96 | <listitem> | ||
97 | <para>Print help text.</para> | ||
98 | </listitem> | ||
99 | </varlistentry> | ||
100 | </variablelist> | ||
101 | </refsect1> | ||
102 | |||
103 | <refsect1><title>Environment</title> | ||
104 | <variablelist> | ||
105 | <varlistentry> | ||
106 | <term><varname>UDEV_LOG=</varname></term> | ||
107 | <listitem> | ||
108 | <para>Set the logging priority.</para> | ||
109 | </listitem> | ||
110 | </varlistentry> | ||
111 | </variablelist> | ||
112 | </refsect1> | ||
113 | |||
114 | <refsect1><title>Kernel command line</title> | ||
115 | <variablelist> | ||
116 | <varlistentry> | ||
117 | <term><varname>udev.log-priority=</varname></term> | ||
118 | <listitem> | ||
119 | <para>Set the logging priority.</para> | ||
120 | </listitem> | ||
121 | </varlistentry> | ||
122 | <varlistentry> | ||
123 | <term><varname>udev.children-max=</varname></term> | ||
124 | <listitem> | ||
125 | <para>Limit the number of parallel executed events.</para> | ||
126 | </listitem> | ||
127 | </varlistentry> | ||
128 | <varlistentry> | ||
129 | <term><varname>udev.exec-delay=</varname></term> | ||
130 | <listitem> | ||
131 | <para>Number of seconds to delay the execution of RUN instructions. | ||
132 | This might be useful when debugging system crashes during coldplug | ||
133 | cause by loading non-working kernel modules.</para> | ||
134 | </listitem> | ||
135 | </varlistentry> | ||
136 | </variablelist> | ||
137 | </refsect1> | ||
138 | |||
139 | <refsect1><title>Author</title> | ||
140 | <para>Written by Kay Sievers <email>kay.sievers@vrfy.org</email>.</para> | ||
141 | </refsect1> | ||
142 | |||
143 | <refsect1> | ||
144 | <title>See Also</title> | ||
145 | <para><citerefentry> | ||
146 | <refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum> | ||
147 | </citerefentry>, <citerefentry> | ||
148 | <refentrytitle>udevadm</refentrytitle><manvolnum>8</manvolnum> | ||
149 | </citerefentry></para> | ||
150 | </refsect1> | ||
151 | </refentry> |