Gentoo Archives: gentoo-commits

From: Ian Stakenvicius <axs@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] repo/gentoo:master commit in: sys-fs/eudev/, sys-fs/eudev/files/
Date: Thu, 24 Sep 2015 18:07:37
Message-Id: 1443117949.4bfb86649d6861f1e761c602af90618ab1ec133e.axs@gentoo
1 commit: 4bfb86649d6861f1e761c602af90618ab1ec133e
2 Author: Ian Stakenvicius <axs <AT> gentoo <DOT> org>
3 AuthorDate: Thu Sep 24 18:01:15 2015 +0000
4 Commit: Ian Stakenvicius <axs <AT> gentoo <DOT> org>
5 CommitDate: Thu Sep 24 18:05:49 2015 +0000
6 URL: https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=4bfb8664
7
8 sys-fs/eudev: add rule-generator suport for eudev-3
9
10 This commit restores rule-generator support with a patch developed
11 upstream but not yet part of a release. If testing succeeds,
12 the ebuild with this patch will be stabilized. A new
13 release for ~arch will follow soon with this patch integrated.
14
15 Package-Manager: portage-2.2.20.1
16
17 sys-fs/eudev/eudev-3.1.2-r1.ebuild | 290 +++++++++
18 sys-fs/eudev/files/eudev-3-rule-generator.patch | 712 +++++++++++++++++++++
19 .../files/eudev-3.1.2-pre-rule-generator.patch | 28 +
20 3 files changed, 1030 insertions(+)
21
22 diff --git a/sys-fs/eudev/eudev-3.1.2-r1.ebuild b/sys-fs/eudev/eudev-3.1.2-r1.ebuild
23 new file mode 100644
24 index 0000000..f999da1
25 --- /dev/null
26 +++ b/sys-fs/eudev/eudev-3.1.2-r1.ebuild
27 @@ -0,0 +1,290 @@
28 +# Copyright 1999-2015 Gentoo Foundation
29 +# Distributed under the terms of the GNU General Public License v2
30 +# $Id$
31 +
32 +EAPI="5"
33 +
34 +KV_min=2.6.39
35 +WANT_AUTOMAKE=1.13
36 +
37 +inherit autotools eutils linux-info multilib multilib-minimal user
38 +
39 +if [[ ${PV} = 9999* ]]; then
40 + EGIT_REPO_URI="git://github.com/gentoo/eudev.git"
41 + inherit git-2
42 +else
43 + SRC_URI="https://dev.gentoo.org/~blueness/${PN}/${P}.tar.gz"
44 + KEYWORDS="~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sparc ~x86"
45 +fi
46 +
47 +DESCRIPTION="Linux dynamic and persistent device naming support (aka userspace devfs)"
48 +HOMEPAGE="https://github.com/gentoo/eudev"
49 +
50 +LICENSE="LGPL-2.1 MIT GPL-2"
51 +SLOT="0"
52 +IUSE="doc gudev +hwdb +kmod introspection rule-generator selinux static-libs test"
53 +
54 +COMMON_DEPEND=">=sys-apps/util-linux-2.20
55 + gudev? ( >=dev-libs/glib-2.34.3:2[${MULTILIB_USEDEP}] )
56 + introspection? ( >=dev-libs/gobject-introspection-1.38 )
57 + kmod? ( >=sys-apps/kmod-16 )
58 + selinux? ( >=sys-libs/libselinux-2.1.9 )
59 + !<sys-libs/glibc-2.11
60 + !sys-apps/gentoo-systemd-integration
61 + !sys-apps/systemd
62 + abi_x86_32? (
63 + !<=app-emulation/emul-linux-x86-baselibs-20130224-r7
64 + !app-emulation/emul-linux-x86-baselibs[-abi_x86_32(-)]
65 + )"
66 +DEPEND="${COMMON_DEPEND}
67 + dev-util/gperf
68 + virtual/os-headers
69 + virtual/pkgconfig
70 + >=sys-devel/make-3.82-r4
71 + >=sys-kernel/linux-headers-${KV_min}
72 + doc? ( >=dev-util/gtk-doc-1.18
73 + app-text/docbook-xml-dtd:4.2
74 + app-text/docbook-xml-dtd:4.5
75 + app-text/docbook-xsl-stylesheets
76 + dev-libs/libxslt
77 + )
78 + >=dev-util/intltool-0.50
79 + test? ( app-text/tree dev-lang/perl )"
80 +
81 +RDEPEND="${COMMON_DEPEND}
82 + !<sys-fs/lvm2-2.02.103
83 + !<sec-policy/selinux-base-2.20120725-r10
84 + !sys-fs/udev
85 + !sys-apps/systemd
86 + gudev? ( !dev-libs/libgudev )"
87 +
88 +PDEPEND=">=sys-fs/udev-init-scripts-26
89 + hwdb? ( >=sys-apps/hwids-20140304[udev] )"
90 +
91 +# The multilib-build.eclass doesn't handle situation where the installed headers
92 +# are different in ABIs. In this case, we install libgudev headers in native
93 +# ABI but not for non-native ABI.
94 +multilib_check_headers() { :; }
95 +
96 +pkg_pretend() {
97 + ewarn
98 + ewarn "As of 2013-01-29, ${P} provides the new interface renaming functionality,"
99 + ewarn "as described in the URL below:"
100 + ewarn "http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames"
101 + ewarn
102 + ewarn "This functionality is enabled BY DEFAULT because eudev has no means of synchronizing"
103 + ewarn "between the default or user-modified choice of sys-fs/udev. If you wish to disable"
104 + ewarn "this new iface naming, please be sure that /etc/udev/rules.d/80-net-name-slot.rules"
105 + ewarn "exists: touch /etc/udev/rules.d/80-net-name-slot.rules"
106 + ewarn
107 +}
108 +
109 +pkg_setup() {
110 + CONFIG_CHECK="~BLK_DEV_BSG ~DEVTMPFS ~!IDE ~INOTIFY_USER ~!SYSFS_DEPRECATED ~!SYSFS_DEPRECATED_V2 ~SIGNALFD ~EPOLL ~FHANDLE ~NET"
111 + linux-info_pkg_setup
112 + get_running_version
113 +
114 + # These are required kernel options, but we don't error out on them
115 + # because you can build under one kernel and run under another.
116 + if kernel_is lt ${KV_min//./ }; then
117 + ewarn
118 + ewarn "Your current running kernel version ${KV_FULL} is too old to run ${P}."
119 + ewarn "Make sure to run udev under kernel version ${KV_min} or above."
120 + ewarn
121 + fi
122 +}
123 +
124 +src_prepare() {
125 + epatch "${FILESDIR}"/${P}-pre-rule-generator.patch
126 + epatch "${FILESDIR}"/${PN}-3-rule-generator.patch
127 +
128 + # change rules back to group uucp instead of dialout for now
129 + sed -e 's/GROUP="dialout"/GROUP="uucp"/' -i rules/*.rules \
130 + || die "failed to change group dialout to uucp"
131 +
132 + epatch_user
133 +
134 + if use doc; then
135 + gtkdocize --docdir docs || die "gtkdocize failed"
136 + else
137 + echo 'EXTRA_DIST =' > docs/gtk-doc.make
138 + fi
139 + eautoreconf
140 +}
141 +
142 +multilib_src_configure() {
143 + tc-export CC #463846
144 + export cc_cv_CFLAGS__flto=no #502950
145 +
146 + # Keep sorted by ./configure --help and only pass --disable flags
147 + # when *required* to avoid external deps or unnecessary compile
148 + local econf_args
149 + econf_args=(
150 + ac_cv_search_cap_init=
151 + ac_cv_header_sys_capability_h=yes
152 + DBUS_CFLAGS=' '
153 + DBUS_LIBS=' '
154 + --with-rootprefix=
155 + --with-rootrundir=/run
156 + --docdir=/usr/share/doc/${PF}
157 + --libdir=/usr/$(get_libdir)
158 + --with-rootlibexecdir=/lib/udev
159 + --with-html-dir="/usr/share/doc/${PF}/html"
160 + --enable-split-usr
161 + --enable-manpages
162 + --disable-hwdb
163 + --exec-prefix=/
164 +
165 + $(use_enable gudev)
166 + )
167 +
168 + # Only build libudev for non-native_abi, and only install it to libdir,
169 + # that means all options only apply to native_abi
170 + if multilib_is_native_abi; then
171 + econf_args+=(
172 + --with-rootlibdir=/$(get_libdir)
173 + $(use_enable doc gtk-doc)
174 + $(use_enable introspection)
175 + $(use_enable kmod)
176 + $(use_enable static-libs static)
177 + $(use_enable selinux)
178 + $(use_enable rule-generator)
179 + )
180 + else
181 + econf_args+=(
182 + --disable-static
183 + --disable-gtk-doc
184 + --disable-introspection
185 + --disable-kmod
186 + --disable-selinux
187 + --disable-rule-generator
188 + )
189 + fi
190 + ECONF_SOURCE="${S}" econf "${econf_args[@]}"
191 +}
192 +
193 +multilib_src_compile() {
194 + if multilib_is_native_abi; then
195 + emake
196 + else
197 + emake -C src/shared
198 + emake -C src/libudev
199 + use gudev && emake -C src/gudev
200 + fi
201 +}
202 +
203 +multilib_src_install() {
204 + if multilib_is_native_abi; then
205 + emake DESTDIR="${D}" install
206 + else
207 + emake -C src/libudev DESTDIR="${D}" install
208 + use gudev && emake -C src/gudev DESTDIR="${D}" install
209 + fi
210 +}
211 +
212 +multilib_src_test() {
213 + # make sandbox get out of the way
214 + # these are safe because there is a fake root filesystem put in place,
215 + # but sandbox seems to evaluate the paths of the test i/o instead of the
216 + # paths of the actual i/o that results.
217 + # also only test for native abi
218 + if multilib_is_native_abi; then
219 + addread /sys
220 + addwrite /dev
221 + addwrite /run
222 + default_src_test
223 + fi
224 +}
225 +
226 +multilib_src_install_all() {
227 + prune_libtool_files --all
228 + rm -rf "${ED}"/usr/share/doc/${PF}/LICENSE.*
229 +
230 + insinto /lib/udev/rules.d
231 + doins "${FILESDIR}"/40-gentoo.rules
232 +
233 + use rule-generator && doinitd "${FILESDIR}"/udev-postmount
234 +
235 + if ! [[ ${PV} = 9999* ]]; then
236 + insinto /usr/share/doc/${PF}/html/gudev
237 + doins "${S}"/docs/gudev/html/*
238 +
239 + insinto /usr/share/doc/${PF}/html/libudev
240 + doins "${S}"/docs/libudev/html/*
241 + fi
242 +}
243 +
244 +pkg_preinst() {
245 + local htmldir
246 + for htmldir in gudev libudev; do
247 + if [[ -d ${EROOT}usr/share/gtk-doc/html/${htmldir} ]]; then
248 + rm -rf "${EROOT}"usr/share/gtk-doc/html/${htmldir}
249 + fi
250 + if [[ -d ${ED}/usr/share/doc/${PF}/html/${htmldir} ]]; then
251 + dosym ../../doc/${PF}/html/${htmldir} \
252 + /usr/share/gtk-doc/html/${htmldir}
253 + fi
254 + done
255 +}
256 +
257 +pkg_postinst() {
258 + mkdir -p "${EROOT}"run
259 +
260 + # "losetup -f" is confused if there is an empty /dev/loop/, Bug #338766
261 + # So try to remove it here (will only work if empty).
262 + rmdir "${EROOT}"dev/loop 2>/dev/null
263 + if [[ -d ${EROOT}dev/loop ]]; then
264 + ewarn "Please make sure your remove /dev/loop,"
265 + ewarn "else losetup may be confused when looking for unused devices."
266 + fi
267 +
268 + if use hwdb && has_version 'sys-apps/hwids[udev]'; then
269 + udevadm hwdb --update --root="${ROOT%/}"
270 +
271 + # http://cgit.freedesktop.org/systemd/systemd/commit/?id=1fab57c209035f7e66198343074e9cee06718bda
272 + # reload database after it has be rebuilt, but only if we are not upgrading
273 + # also pass if we are -9999 since who knows what hwdb related changes there might be
274 + if [[ ${REPLACING_VERSIONS%-r*} == ${PV} || -z ${REPLACING_VERSIONS} ]] && \
275 + [[ ${ROOT%/} == "" ]] && [[ ${PV} != "9999" ]]; then
276 + udevadm control --reload
277 + fi
278 + fi
279 +
280 + ewarn
281 + ewarn "You need to restart eudev as soon as possible to make the"
282 + ewarn "upgrade go into effect:"
283 + ewarn "\t/etc/init.d/udev --nodeps restart"
284 +
285 + if use rule-generator && \
286 + [[ -x $(type -P rc-update) ]] && rc-update show | grep udev-postmount | grep -qsv 'boot\|default\|sysinit'; then
287 + ewarn
288 + ewarn "Please add the udev-postmount init script to your default runlevel"
289 + ewarn "to ensure the legacy rule-generator functionality works as reliably"
290 + ewarn "as possible."
291 + ewarn "\trc-update add udev-postmount default"
292 + fi
293 +
294 + elog
295 + elog "For more information on eudev on Gentoo, writing udev rules, and"
296 + elog "fixing known issues visit:"
297 + elog " https://www.gentoo.org/doc/en/udev-guide.xml"
298 + elog
299 +
300 + # http://cgit.freedesktop.org/systemd/systemd/commit/rules/50-udev-default.rules?id=3dff3e00e044e2d53c76fa842b9a4759d4a50e69
301 + # https://bugs.gentoo.org/246847
302 + # https://bugs.gentoo.org/514174
303 + enewgroup input
304 +
305 + # Update hwdb database in case the format is changed by udev version.
306 + if has_version 'sys-apps/hwids[udev]'; then
307 + udevadm hwdb --update --root="${ROOT%/}"
308 + # Only reload when we are not upgrading to avoid potential race w/ incompatible hwdb.bin and the running udevd
309 + if [[ -z ${REPLACING_VERSIONS} ]]; then
310 + # http://cgit.freedesktop.org/systemd/systemd/commit/?id=1fab57c209035f7e66198343074e9cee06718bda
311 + if [[ ${ROOT} != "" ]] && [[ ${ROOT} != "/" ]]; then
312 + return 0
313 + fi
314 + udevadm control --reload
315 + fi
316 + fi
317 +}
318
319 diff --git a/sys-fs/eudev/files/eudev-3-rule-generator.patch b/sys-fs/eudev/files/eudev-3-rule-generator.patch
320 new file mode 100644
321 index 0000000..b913066
322 --- /dev/null
323 +++ b/sys-fs/eudev/files/eudev-3-rule-generator.patch
324 @@ -0,0 +1,712 @@
325 +commit c3e2fc597a6ba92ab9fcfd4cd7db10bc6fb5b34b
326 +Author: Ian Stakenvicius <axs@g.o>
327 +Date: Tue Sep 22 13:53:41 2015 -0400
328 +
329 + Forward-ported network rule-generator code from eudev-1.10
330 +
331 +diff --git a/Makefile.am b/Makefile.am
332 +index 2932690..0c6429d 100644
333 +--- a/Makefile.am
334 ++++ b/Makefile.am
335 +@@ -15,3 +15,7 @@ SUBDIRS += \
336 + hwdb
337 + endif
338 +
339 ++if ENABLE_RULE_GENERATOR
340 ++SUBDIRS += \
341 ++ rule_generator
342 ++endif
343 +diff --git a/configure.ac b/configure.ac
344 +index ccb1012..56d37fa 100644
345 +--- a/configure.ac
346 ++++ b/configure.ac
347 +@@ -269,10 +269,25 @@ AC_ARG_ENABLE([hwdb], AS_HELP_STRING([--enable-hwdb],[install hwdb.d files]),[],
348 + AM_CONDITIONAL(ENABLE_HWDB, [test "x$enable_hwdb" = "xyes"])
349 +
350 + # ------------------------------------------------------------------------------
351 ++# rule-generator - persistent network and optical device rule generator
352 ++# ------------------------------------------------------------------------------
353 ++AC_ARG_ENABLE([rule-generator],
354 ++ AS_HELP_STRING([--enable-rule-generator], [enable legacy persistent network, cdrom support]),
355 ++ [], [enable_rule_generator=no])
356 ++
357 ++if test "x${enable_rule_generator}" != xno; then
358 ++ AC_DEFINE([ENABLE_RULE_GENERATOR], [1], [Define if we are enabling rule generator])
359 ++fi
360 ++
361 ++AM_CONDITIONAL([ENABLE_RULE_GENERATOR], [test "x$enable_rule_generator" = xyes])
362 ++
363 ++# ------------------------------------------------------------------------------
364 +
365 + AC_CONFIG_FILES([Makefile
366 + hwdb/Makefile
367 + man/Makefile
368 ++ rule_generator/Makefile
369 ++ rule_generator/write_net_rules
370 + rules/Makefile
371 + src/Makefile
372 + src/ata_id/Makefile
373 +diff --git a/rule_generator/75-persistent-net-generator.rules b/rule_generator/75-persistent-net-generator.rules
374 +new file mode 100644
375 +index 0000000..ce59e5d
376 +--- /dev/null
377 ++++ b/rule_generator/75-persistent-net-generator.rules
378 +@@ -0,0 +1,100 @@
379 ++# do not edit this file, it will be overwritten on update
380 ++
381 ++# these rules generate rules for persistent network device naming
382 ++#
383 ++# variables used to communicate:
384 ++# MATCHADDR MAC address used for the match
385 ++# MATCHID bus_id used for the match
386 ++# MATCHDRV driver name used for the match
387 ++# MATCHIFTYPE interface type match
388 ++# COMMENT comment to add to the generated rule
389 ++# INTERFACE_NAME requested name supplied by external tool
390 ++# INTERFACE_NEW new interface name returned by rule writer
391 ++
392 ++ACTION!="add", GOTO="persistent_net_generator_end"
393 ++SUBSYSTEM!="net", GOTO="persistent_net_generator_end"
394 ++
395 ++# ignore the interface if a name has already been set
396 ++NAME=="?*", GOTO="persistent_net_generator_end"
397 ++
398 ++# device name whitelist
399 ++KERNEL!="eth*|ath*|wlan*[0-9]|msh*|ra*|sta*|ctc*|lcs*|hsi*", GOTO="persistent_net_generator_end"
400 ++
401 ++# ignore Xen virtual interfaces
402 ++SUBSYSTEMS=="xen", GOTO="persistent_net_generator_end"
403 ++
404 ++# read MAC address
405 ++ENV{MATCHADDR}="$attr{address}"
406 ++
407 ++# match interface type
408 ++ENV{MATCHIFTYPE}="$attr{type}"
409 ++
410 ++# ignore KVM virtual interfaces
411 ++ENV{MATCHADDR}=="52:54:00:*", GOTO="persistent_net_generator_end"
412 ++# ignore VMWare virtual interfaces
413 ++ENV{MATCHADDR}=="00:0c:29:*|00:50:56:*", GOTO="persistent_net_generator_end"
414 ++# ignore Hyper-V virtual interfaces
415 ++ENV{MATCHADDR}=="00:15:5d:*", GOTO="persistent_net_generator_end"
416 ++
417 ++# These vendors are known to violate the local MAC address assignment scheme
418 ++# Interlan, DEC (UNIBUS or QBUS), Apollo, Cisco, Racal-Datacom
419 ++ENV{MATCHADDR}=="02:07:01:*", GOTO="globally_administered_whitelist"
420 ++# 3Com
421 ++ENV{MATCHADDR}=="02:60:60:*", GOTO="globally_administered_whitelist"
422 ++# 3Com IBM PC; Imagen; Valid; Cisco; Apple
423 ++ENV{MATCHADDR}=="02:60:8c:*", GOTO="globally_administered_whitelist"
424 ++# Intel
425 ++ENV{MATCHADDR}=="02:a0:c9:*", GOTO="globally_administered_whitelist"
426 ++# Olivetti
427 ++ENV{MATCHADDR}=="02:aa:3c:*", GOTO="globally_administered_whitelist"
428 ++# CMC Masscomp; Silicon Graphics; Prime EXL
429 ++ENV{MATCHADDR}=="02:cf:1f:*", GOTO="globally_administered_whitelist"
430 ++# Prominet Corporation Gigabit Ethernet Switch
431 ++ENV{MATCHADDR}=="02:e0:3b:*", GOTO="globally_administered_whitelist"
432 ++# BTI (Bus-Tech, Inc.) IBM Mainframes
433 ++ENV{MATCHADDR}=="02:e6:d3:*", GOTO="globally_administered_whitelist"
434 ++# Realtek
435 ++ENV{MATCHADDR}=="52:54:00:*", GOTO="globally_administered_whitelist"
436 ++# Novell 2000
437 ++ENV{MATCHADDR}=="52:54:4c:*", GOTO="globally_administered_whitelist"
438 ++# Realtec
439 ++ENV{MATCHADDR}=="52:54:ab:*", GOTO="globally_administered_whitelist"
440 ++# Kingston Technologies
441 ++ENV{MATCHADDR}=="e2:0c:0f:*", GOTO="globally_administered_whitelist"
442 ++
443 ++# match interface dev_id
444 ++ATTR{dev_id}=="?*", ENV{MATCHDEVID}="$attr{dev_id}"
445 ++
446 ++# do not use "locally administered" MAC address
447 ++ENV{MATCHADDR}=="?[2367abef]:*", ENV{MATCHADDR}=""
448 ++
449 ++# do not use empty address
450 ++ENV{MATCHADDR}=="00:00:00:00:00:00", ENV{MATCHADDR}=""
451 ++
452 ++LABEL="globally_administered_whitelist"
453 ++
454 ++# build comment line for generated rule:
455 ++SUBSYSTEMS=="pci", ENV{COMMENT}="PCI device $attr{vendor}:$attr{device} ($driver)"
456 ++SUBSYSTEMS=="usb", ATTRS{idVendor}=="?*", ENV{COMMENT}="USB device 0x$attr{idVendor}:0x$attr{idProduct} ($driver)"
457 ++SUBSYSTEMS=="pcmcia", ENV{COMMENT}="PCMCIA device $attr{card_id}:$attr{manf_id} ($driver)"
458 ++SUBSYSTEMS=="ieee1394", ENV{COMMENT}="Firewire device $attr{host_id})"
459 ++
460 ++# ibmveth likes to use "locally administered" MAC addresses
461 ++DRIVERS=="ibmveth", ENV{MATCHADDR}="$attr{address}", ENV{COMMENT}="ibmveth ($id)"
462 ++
463 ++# S/390 uses id matches only, do not use MAC address match
464 ++SUBSYSTEMS=="ccwgroup", ENV{COMMENT}="S/390 $driver device at $id", ENV{MATCHID}="$id", ENV{MATCHDRV}="$driver", ENV{MATCHADDR}=""
465 ++
466 ++# see if we got enough data to create a rule
467 ++ENV{MATCHADDR}=="", ENV{MATCHID}=="", ENV{INTERFACE_NAME}=="", GOTO="persistent_net_generator_end"
468 ++
469 ++# default comment
470 ++ENV{COMMENT}=="", ENV{COMMENT}="net device ($attr{driver})"
471 ++
472 ++# write rule
473 ++DRIVERS=="?*", IMPORT{program}="write_net_rules"
474 ++
475 ++# rename interface if needed
476 ++ENV{INTERFACE_NEW}=="?*", NAME="$env{INTERFACE_NEW}"
477 ++
478 ++LABEL="persistent_net_generator_end"
479 +diff --git a/rule_generator/Makefile.am b/rule_generator/Makefile.am
480 +new file mode 100644
481 +index 0000000..8ed69f7
482 +--- /dev/null
483 ++++ b/rule_generator/Makefile.am
484 +@@ -0,0 +1,13 @@
485 ++ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
486 ++
487 ++# ------------------------------------------------------------------------------
488 ++# rule_generator - persistent network rule generator
489 ++# ------------------------------------------------------------------------------
490 ++dist_udevlibexec_SCRIPTS = \
491 ++ write_net_rules
492 ++
493 ++udevhomedir = $(udevlibexecdir)
494 ++dist_udevhome_DATA = rule_generator.functions
495 ++
496 ++dist_udevrules_DATA = \
497 ++ 75-persistent-net-generator.rules
498 +diff --git a/rule_generator/rule_generator.functions b/rule_generator/rule_generator.functions
499 +new file mode 100644
500 +index 0000000..ea02acc
501 +--- /dev/null
502 ++++ b/rule_generator/rule_generator.functions
503 +@@ -0,0 +1,120 @@
504 ++# functions used by the udev rule generator
505 ++
506 ++# Copyright (C) 2006 Marco d'Itri <md@×××××.IT>
507 ++
508 ++# This program is free software: you can redistribute it and/or modify
509 ++# it under the terms of the GNU General Public License as published by
510 ++# the Free Software Foundation, either version 2 of the License, or
511 ++# (at your option) any later version.
512 ++
513 ++# This program is distributed in the hope that it will be useful,
514 ++# but WITHOUT ANY WARRANTY; without even the implied warranty of
515 ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
516 ++# GNU General Public License for more details.
517 ++
518 ++# You should have received a copy of the GNU General Public License
519 ++# along with this program. If not, see <http://www.gnu.org/licenses/>.
520 ++
521 ++PATH='/sbin:/bin'
522 ++#
523 ++
524 ++PATH='/sbin:/bin'
525 ++
526 ++# Read a single line from file $1 in the $DEVPATH directory.
527 ++# The function must not return an error even if the file does not exist.
528 ++sysread() {
529 ++ local file="$1"
530 ++ [ -e "/sys$DEVPATH/$file" ] || return 0
531 ++ local value
532 ++ read value < "/sys$DEVPATH/$file" || return 0
533 ++ echo "$value"
534 ++}
535 ++
536 ++sysreadlink() {
537 ++ local file="$1"
538 ++ [ -e "/sys$DEVPATH/$file" ] || return 0
539 ++ readlink -f /sys$DEVPATH/$file 2> /dev/null || true
540 ++}
541 ++
542 ++# Return true if a directory is writeable.
543 ++writeable() {
544 ++ if ln -s test-link $1/.is-writeable 2> /dev/null; then
545 ++ rm -f $1/.is-writeable
546 ++ return 0
547 ++ else
548 ++ return 1
549 ++ fi
550 ++}
551 ++
552 ++# Create a lock file for the current rules file.
553 ++lock_rules_file() {
554 ++ RUNDIR="/run/udev/"
555 ++
556 ++ RULES_LOCK="$RUNDIR/.lock-${RULES_FILE##*/}"
557 ++
558 ++ retry=30
559 ++ while ! mkdir $RULES_LOCK 2> /dev/null; do
560 ++ if [ $retry -eq 0 ]; then
561 ++ echo "Cannot lock $RULES_FILE!" >&2
562 ++ exit 2
563 ++ fi
564 ++ sleep 1
565 ++ retry=$(($retry - 1))
566 ++ done
567 ++}
568 ++
569 ++unlock_rules_file() {
570 ++ [ "$RULES_LOCK" ] || return 0
571 ++ rmdir $RULES_LOCK || true
572 ++}
573 ++
574 ++# Choose the real rules file if it is writeable or a temporary file if not.
575 ++# Both files should be checked later when looking for existing rules.
576 ++choose_rules_file() {
577 ++ RUNDIR="/run/udev/"
578 ++
579 ++ local tmp_rules_file="$RUNDIR/tmp-rules--${RULES_FILE##*/}"
580 ++ [ -e "$RULES_FILE" -o -e "$tmp_rules_file" ] || PRINT_HEADER=1
581 ++
582 ++ [ -d "${RULES_FILE%/*}" ] || if writeable ${RULES_FILE%/rules.d/*}; then
583 ++ mkdir -p "${RULES_FILE%/*}"
584 ++ fi
585 ++
586 ++ if writeable ${RULES_FILE%/*}; then
587 ++ RO_RULES_FILE='/dev/null'
588 ++ else
589 ++ RO_RULES_FILE=$RULES_FILE
590 ++ RULES_FILE=$tmp_rules_file
591 ++ fi
592 ++}
593 ++
594 ++# Return the name of the first free device.
595 ++raw_find_next_available() {
596 ++ local links="$1"
597 ++
598 ++ local basename=${links%%[ 0-9]*}
599 ++ local max=-1
600 ++ for name in $links; do
601 ++ local num=${name#$basename}
602 ++ [ "$num" ] || num=0
603 ++ [ $num -gt $max ] && max=$num
604 ++ done
605 ++
606 ++ local max=$(($max + 1))
607 ++ # "name0" actually is just "name"
608 ++ [ $max -eq 0 ] && return
609 ++ echo "$max"
610 ++}
611 ++
612 ++# Find all rules matching a key (with action) and a pattern.
613 ++find_all_rules() {
614 ++ local key="$1"
615 ++ local linkre="$2"
616 ++ local match="$3"
617 ++
618 ++ local search='.*[[:space:],]'"$key"'"('"$linkre"')".*'
619 ++ echo $(sed -n -r -e 's/^#.*//' -e "${match}s/${search}/\1/p" \
620 ++ $RO_RULES_FILE \
621 ++ $([ -e $RULES_FILE ] && echo $RULES_FILE) \
622 ++ 2>/dev/null)
623 ++}
624 +diff --git a/rule_generator/write_net_rules.in b/rule_generator/write_net_rules.in
625 +new file mode 100644
626 +index 0000000..324e978
627 +--- /dev/null
628 ++++ b/rule_generator/write_net_rules.in
629 +@@ -0,0 +1,141 @@
630 ++#!/bin/sh -e
631 ++
632 ++# This script is run to create persistent network device naming rules
633 ++# based on properties of the device.
634 ++# If the interface needs to be renamed, INTERFACE_NEW=<name> will be printed
635 ++# on stdout to allow udev to IMPORT it.
636 ++
637 ++# variables used to communicate:
638 ++# MATCHADDR MAC address used for the match
639 ++# MATCHID bus_id used for the match
640 ++# MATCHDEVID dev_id used for the match
641 ++# MATCHDRV driver name used for the match
642 ++# MATCHIFTYPE interface type match
643 ++# COMMENT comment to add to the generated rule
644 ++# INTERFACE_NAME requested name supplied by external tool
645 ++# INTERFACE_NEW new interface name returned by rule writer
646 ++
647 ++# Copyright (C) 2006 Marco d'Itri <md@×××××.IT>
648 ++# Copyright (C) 2007 Kay Sievers <kay.sievers@××××.org>
649 ++#
650 ++# This program is free software: you can redistribute it and/or modify
651 ++# it under the terms of the GNU General Public License as published by
652 ++# the Free Software Foundation, either version 2 of the License, or
653 ++# (at your option) any later version.
654 ++#
655 ++# This program is distributed in the hope that it will be useful,
656 ++# but WITHOUT ANY WARRANTY; without even the implied warranty of
657 ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
658 ++# GNU General Public License for more details.
659 ++#
660 ++# You should have received a copy of the GNU General Public License
661 ++# along with this program. If not, see <http://www.gnu.org/licenses/>.
662 ++
663 ++# debug, if UDEV_LOG=<debug>
664 ++if [ -n "$UDEV_LOG" ]; then
665 ++ if [ "$UDEV_LOG" -ge 7 ]; then
666 ++ set -x
667 ++ fi
668 ++fi
669 ++
670 ++RULES_FILE='@udevconfdir@/rules.d/70-persistent-net.rules'
671 ++
672 ++. @udevlibexecdir@/rule_generator.functions
673 ++
674 ++interface_name_taken() {
675 ++ local value="$(find_all_rules 'NAME=' $INTERFACE)"
676 ++ if [ "$value" ]; then
677 ++ return 0
678 ++ else
679 ++ return 1
680 ++ fi
681 ++}
682 ++
683 ++find_next_available() {
684 ++ raw_find_next_available "$(find_all_rules 'NAME=' "$1")"
685 ++}
686 ++
687 ++write_rule() {
688 ++ local match="$1"
689 ++ local name="$2"
690 ++ local comment="$3"
691 ++
692 ++ {
693 ++ if [ "$PRINT_HEADER" ]; then
694 ++ PRINT_HEADER=
695 ++ echo "# This file was automatically generated by the $0"
696 ++ echo "# program, run by the persistent-net-generator.rules rules file."
697 ++ echo "#"
698 ++ echo "# You can modify it, as long as you keep each rule on a single"
699 ++ echo "# line, and change only the value of the NAME= key."
700 ++ fi
701 ++
702 ++ echo ""
703 ++ [ "$comment" ] && echo "# $comment"
704 ++ echo "SUBSYSTEM==\"net\", ACTION==\"add\"$match, NAME=\"$name\""
705 ++ } >> $RULES_FILE
706 ++}
707 ++
708 ++if [ -z "$INTERFACE" ]; then
709 ++ echo "missing \$INTERFACE" >&2
710 ++ exit 1
711 ++fi
712 ++
713 ++# Prevent concurrent processes from modifying the file at the same time.
714 ++lock_rules_file
715 ++
716 ++# Check if the rules file is writeable.
717 ++choose_rules_file
718 ++
719 ++# the DRIVERS key is needed to not match bridges and VLAN sub-interfaces
720 ++if [ "$MATCHADDR" ]; then
721 ++ match="$match, DRIVERS==\"?*\", ATTR{address}==\"$MATCHADDR\""
722 ++fi
723 ++
724 ++if [ "$MATCHDRV" ]; then
725 ++ match="$match, DRIVERS==\"$MATCHDRV\""
726 ++fi
727 ++
728 ++if [ "$MATCHDEVID" ]; then
729 ++ match="$match, ATTR{dev_id}==\"$MATCHDEVID\""
730 ++fi
731 ++
732 ++if [ "$MATCHID" ]; then
733 ++ match="$match, KERNELS==\"$MATCHID\""
734 ++fi
735 ++
736 ++if [ "$MATCHIFTYPE" ]; then
737 ++ match="$match, ATTR{type}==\"$MATCHIFTYPE\""
738 ++fi
739 ++
740 ++if [ -z "$match" ]; then
741 ++ echo "missing valid match" >&2
742 ++ unlock_rules_file
743 ++ exit 1
744 ++fi
745 ++
746 ++basename=${INTERFACE%%[0-9]*}
747 ++match="$match, KERNEL==\"$basename*\""
748 ++
749 ++if [ "$INTERFACE_NAME" ]; then
750 ++ # external tools may request a custom name
751 ++ COMMENT="$COMMENT (custom name provided by external tool)"
752 ++ if [ "$INTERFACE_NAME" != "$INTERFACE" ]; then
753 ++ INTERFACE=$INTERFACE_NAME;
754 ++ echo "INTERFACE_NEW=$INTERFACE"
755 ++ fi
756 ++else
757 ++ # if a rule using the current name already exists, find a new name
758 ++ if interface_name_taken; then
759 ++ INTERFACE="$basename$(find_next_available "$basename[0-9]*")"
760 ++ # prevent INTERFACE from being "eth" instead of "eth0"
761 ++ [ "$INTERFACE" = "${INTERFACE%%[ \[\]0-9]*}" ] && INTERFACE=${INTERFACE}0
762 ++ echo "INTERFACE_NEW=$INTERFACE"
763 ++ fi
764 ++fi
765 ++
766 ++write_rule "$match" "$INTERFACE" "$COMMENT"
767 ++
768 ++unlock_rules_file
769 ++
770 ++exit 0
771 +diff --git a/rules/Makefile.am b/rules/Makefile.am
772 +index 2c8624f..24c099c 100644
773 +--- a/rules/Makefile.am
774 ++++ b/rules/Makefile.am
775 +@@ -15,8 +15,12 @@ dist_udevrules_DATA = \
776 + 70-mouse.rules \
777 + 75-net-description.rules \
778 + 75-probe_mtd.rules \
779 +- 78-sound-card.rules \
780 ++ 78-sound-card.rules
781 ++
782 ++if !ENABLE_RULE_GENERATOR
783 ++dist_udevrules_DATA += \
784 + 80-net-name-slot.rules
785 ++endif
786 +
787 + if HAVE_BLKID
788 + dist_udevrules_DATA += \
789 +diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
790 +index ed6f203..897f98b 100644
791 +--- a/src/udev/udev-event.c
792 ++++ b/src/udev/udev-event.c
793 +@@ -771,17 +771,17 @@ out:
794 + return err;
795 + }
796 +
797 +-static int rename_netif(struct udev_event *event) {
798 +- struct udev_device *dev = event->dev;
799 +- char name[IFNAMSIZ];
800 +- const char *oldname;
801 ++#ifdef ENABLE_RULE_GENERATOR
802 ++/* function to return the count of rules that assign NAME= to a value matching arg#2 , defined in udev-rules.c */
803 ++int udev_rules_assigning_name_to(struct udev_rules *rules,const char *match_name);
804 ++#endif
805 ++
806 ++static int rename_netif_dev_fromname_toname(struct udev_device *dev,const char *oldname,const char *name) {
807 + int r;
808 + int sk;
809 + struct ifreq ifr;
810 +
811 +- oldname = udev_device_get_sysname(dev);
812 +-
813 +- strscpy(name, IFNAMSIZ, event->name);
814 ++ log_debug("changing net interface name from '%s' to '%s'\n",oldname,name);
815 +
816 + sk = socket(PF_INET, SOCK_DGRAM, 0);
817 + if (sk < 0)
818 +@@ -791,13 +791,52 @@ static int rename_netif(struct udev_event *event) {
819 + strscpy(ifr.ifr_name, IFNAMSIZ, oldname);
820 + strscpy(ifr.ifr_newname, IFNAMSIZ, name);
821 + r = ioctl(sk, SIOCSIFNAME, &ifr);
822 +- if (r < 0)
823 +- return log_error_errno(-errno, "Error changing net interface name '%s' to '%s': %m", oldname, name);
824 +
825 +- log_debug("renamed network interface '%s' to '%s'", oldname, name);
826 ++#ifdef ENABLE_RULE_GENERATOR
827 ++ int loop;
828 ++
829 ++ if (r == 0) {
830 ++ log_info("renamed network interface %s to %s\n", ifr.ifr_name, ifr.ifr_newname);
831 ++ goto out;
832 ++ }
833 ++ /* keep trying if the destination interface name already exists */
834 ++ log_debug("collision on rename of network interface %s to %s , retrying until timeout\n",
835 ++ ifr.ifr_name, ifr.ifr_newname);
836 ++
837 ++ r = -errno;
838 ++ if (r != -EEXIST)
839 ++ goto out;
840 ++
841 ++ /* wait a maximum of 90 seconds for our target to become available */
842 ++ loop = 90 * 20;
843 ++ while (loop--) {
844 ++ const struct timespec duration = { 0, 1000 * 1000 * 1000 / 20 };
845 ++
846 ++ nanosleep(&duration, NULL);
847 ++
848 ++ r = ioctl(sk, SIOCSIFNAME, &ifr);
849 ++ if (r == 0) {
850 ++ log_info("renamed network interface %s to %s\n", ifr.ifr_name, ifr.ifr_newname);
851 ++ break;
852 ++ }
853 ++ r = -errno;
854 ++ if (r != -EEXIST)
855 ++ break;
856 ++ }
857 ++
858 ++out:
859 ++#endif
860 ++ if (r < 0)
861 ++ log_error_errno(-errno, "Error changing net interface name %s to %s: %m\n", ifr.ifr_name, ifr.ifr_newname);
862 ++ else
863 ++ log_debug("renamed network interface '%s' to '%s'", oldname, name);
864 +
865 + close(sk);
866 +- return 0;
867 ++ return r;
868 ++}
869 ++
870 ++static int rename_netif(struct udev_event *event) {
871 ++ return rename_netif_dev_fromname_toname(event->dev,udev_device_get_sysname(event->dev),event->name);
872 + }
873 +
874 + void udev_event_execute_rules(struct udev_event *event,
875 +@@ -843,6 +882,79 @@ void udev_event_execute_rules(struct udev_event *event,
876 + sigmask);
877 +
878 + /* rename a new network interface, if needed */
879 ++
880 ++ /* ENABLE_RULE_GENERATOR conditional:
881 ++ * if this is a net iface, and it is an add event,
882 ++ * and as long as all of the following are FALSE:
883 ++ * - no NAME target and the current name is not being used
884 ++ * - there is a NAME target and it is the same as the current name
885 ++ * - the rules can successfully be searched for the current name (not really part of the conditional)
886 ++ * the run the rename.
887 ++ *
888 ++ * note - udev_rules_assigning_name_to is run when event->name is NULL to ensure renames happen,
889 ++ * but also on its own to check if a temp-rename is necessary when event->name exists.
890 ++ *
891 ++ * A temp-rename is necessary when:
892 ++ * - there is no rule renaming the current iface but the current name IS used in some other rule
893 ++ * - there is a rule renaming the current iface,
894 ++ * the current name IS used AND the target name != the current name
895 ++ */
896 ++
897 ++#ifdef ENABLE_RULE_GENERATOR
898 ++ int r;
899 ++ if (udev_device_get_ifindex(dev) > 0 && streq(udev_device_get_action(dev), "add") &&
900 ++ (event->name == NULL && (r=udev_rules_assigning_name_to(rules,udev_device_get_sysname(dev))) > 0 ||
901 ++ event->name != NULL && !streq(event->name, udev_device_get_sysname(dev)))) {
902 ++ char syspath[UTIL_PATH_SIZE];
903 ++ char *pos;
904 ++ char *finalifname = event->name;
905 ++ char newifname[IFNAMSIZ];
906 ++
907 ++ /* r is the number of rules that assign a device with NAME= this sysname */
908 ++ if (r > 0 || (r=udev_rules_assigning_name_to(rules,udev_device_get_sysname(dev))) > 0) {
909 ++ /* have a conflict, rename to a temp name */
910 ++ char *newpos;
911 ++ int ifidnum;
912 ++
913 ++ /* build the temporary iface name */
914 ++ strscpy(newifname, IFNAMSIZ, udev_device_get_sysname(dev));
915 ++ newpos=pos=&newifname[strcspn(newifname,"0123456789")];
916 ++ ifidnum=(int)strtol(pos,&newpos,10);
917 ++ *pos='\0';
918 ++ if (newpos > pos && *newpos == '\0') /* append new iface num to name */
919 ++ /* use udev_device_get_ifindex(dev) as it is unique to every iface */
920 ++ snprintf(pos,IFNAMSIZ+(newifname-pos), "%d", 128 - udev_device_get_ifindex(dev));
921 ++
922 ++ /* note, r > 0, which will skip the post-rename stuff if no rename occurs */
923 ++
924 ++ /* if sysname isn't already the tmpname (ie there is no numeric component), do the rename */
925 ++ if (!streq(newifname,udev_device_get_sysname(dev))) {
926 ++ r = rename_netif_dev_fromname_toname(dev,udev_device_get_sysname(dev),newifname);
927 ++ if (r == 0) {
928 ++ finalifname = newifname;
929 ++ log_debug("renamed netif to '%s' for collision avoidance\n", newifname);
930 ++ } else {
931 ++ log_error("could not rename netif to '%s' for collision avoidance\n",newifname);
932 ++ }
933 ++ }
934 ++ /* rename it now to its final target if its not already there */
935 ++ if (event->name != NULL && !streq(event->name, newifname)) {
936 ++ r = rename_netif_dev_fromname_toname(dev,newifname,event->name);
937 ++ if (r == 0)
938 ++ finalifname = event->name;
939 ++ }
940 ++
941 ++ } else { /* no need to rename to a tempname first, do a regular direct rename to event->name */
942 ++
943 ++ r = 1; /* skip the post-rename stuff if no rename occurs */
944 ++ if (!streq(event->name, udev_device_get_sysname(dev)))
945 ++ r = rename_netif(event);
946 ++ }
947 ++
948 ++ if (r == 0) {
949 ++ log_debug("renamed netif to '%s'\n", finalifname);
950 ++ r = udev_device_rename(dev, finalifname);
951 ++#else
952 + if (udev_device_get_ifindex(dev) > 0 && streq(udev_device_get_action(dev), "add") &&
953 + event->name != NULL && !streq(event->name, udev_device_get_sysname(dev))) {
954 + int r;
955 +@@ -853,6 +965,7 @@ void udev_event_execute_rules(struct udev_event *event,
956 + udev_device_get_sysname(dev), event->name);
957 + else {
958 + r = udev_device_rename(dev, event->name);
959 ++#endif
960 + if (r < 0)
961 + log_warning_errno(r, "renamed interface '%d' from '%s' to '%s', but could not update udev_device: %m",
962 + udev_device_get_ifindex(dev), udev_device_get_sysname(dev), event->name);
963 +diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
964 +index c5e85fa..e2bb99c 100644
965 +--- a/src/udev/udev-rules.c
966 ++++ b/src/udev/udev-rules.c
967 +@@ -2755,3 +2755,69 @@ finish:
968 +
969 + return r;
970 + }
971 ++
972 ++#ifdef ENABLE_RULE_GENERATOR
973 ++/* function to return the count of rules that assign NAME= to a value matching arg#2 - returns 0,1 */
974 ++int udev_rules_assigning_name_to(struct udev_rules *rules, const char *match_name)
975 ++{
976 ++ struct token *cur;
977 ++ struct token *rule;
978 ++ enum escape_type esc = ESCAPE_UNSET;
979 ++ bool name_final = false;
980 ++
981 ++ if (rules->tokens == NULL)
982 ++ return 0;
983 ++
984 ++ /* loop through token list, match, run actions or forward to next rule */
985 ++ cur = &rules->tokens[0];
986 ++ rule = cur;
987 ++ for (;;) {
988 ++ dump_token(rules, cur);
989 ++ switch (cur->type) {
990 ++ case TK_RULE:
991 ++ /* current rule */
992 ++ rule = cur;
993 ++ if (!rule->rule.can_set_name)
994 ++ goto nomatch;
995 ++ break;
996 ++ case TK_M_SUBSYSTEM:
997 ++ if (match_key(rules, cur, "net") != 0)
998 ++ goto nomatch;
999 ++ break;
1000 ++ case TK_M_ACTION:
1001 ++ if (match_key(rules, cur, "add") != 0)
1002 ++ goto nomatch;
1003 ++ break;
1004 ++ case TK_A_NAME: {
1005 ++ const char *name = rules_str(rules, cur->key.value_off);
1006 ++ char name_str[UTIL_PATH_SIZE];
1007 ++ int count;
1008 ++
1009 ++ strscpy(name_str,UTIL_PATH_SIZE,name);
1010 ++ count = util_replace_chars(name_str, "/");
1011 ++ if (count > 0)
1012 ++ log_debug("%i character(s) replaced\n", count);
1013 ++ if (streq(name_str,match_name)) {
1014 ++ log_debug("found a match! NAME assigns %s in: %s:%u\n",
1015 ++ name,
1016 ++ rules_str(rules, rule->rule.filename_off),
1017 ++ rule->rule.filename_line);
1018 ++ return 1; /* no need to find more than one */
1019 ++ }
1020 ++
1021 ++ /* skip to next rule */
1022 ++ goto nomatch;
1023 ++ }
1024 ++ case TK_END:
1025 ++ return 0;
1026 ++ }
1027 ++
1028 ++ cur++;
1029 ++ continue;
1030 ++ nomatch:
1031 ++ /* fast-forward to next rule */
1032 ++ cur = rule + rule->rule.token_count;
1033 ++ }
1034 ++}
1035 ++#endif
1036 ++
1037
1038 diff --git a/sys-fs/eudev/files/eudev-3.1.2-pre-rule-generator.patch b/sys-fs/eudev/files/eudev-3.1.2-pre-rule-generator.patch
1039 new file mode 100644
1040 index 0000000..d66d334
1041 --- /dev/null
1042 +++ b/sys-fs/eudev/files/eudev-3.1.2-pre-rule-generator.patch
1043 @@ -0,0 +1,28 @@
1044 +Move some entries further down so that the eudev rule-generator patch applies
1045 +
1046 +--- a/configure.ac 2015-06-19 12:32:53.000000000 -0400
1047 ++++ b/configure.ac 2015-09-24 10:41:34.630153037 -0400
1048 +@@ -287,11 +287,6 @@
1049 + # ------------------------------------------------------------------------------
1050 +
1051 + AC_CONFIG_FILES([Makefile
1052 +- docs/Makefile
1053 +- docs/gudev/Makefile
1054 +- docs/gudev/version.xml
1055 +- docs/libudev/Makefile
1056 +- docs/libudev/version.xml
1057 + hwdb/Makefile
1058 + man/Makefile
1059 + rules/Makefile
1060 +@@ -310,6 +305,11 @@
1061 + src/libudev/libudev.pc
1062 + src/udev/Makefile
1063 + src/udev/udev.pc
1064 ++ docs/Makefile
1065 ++ docs/gudev/Makefile
1066 ++ docs/gudev/version.xml
1067 ++ docs/libudev/Makefile
1068 ++ docs/libudev/version.xml
1069 + test/Makefile])
1070 +
1071 + AC_OUTPUT