| Commit message (Collapse) | Author | Age | Files | Lines |
|
|
|
|
|
|
| |
Make it easier to unit test and continue reduction of main.cpp
Change-Id: Ic549e096343e7a2fb11985f1c48879ed4486e40b
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
|
|
|
|
|
|
|
| |
Testing: 100% coverage of associations.cpp
Change-Id: I311af1c868416e8e898a25e593d399cd8297ccf9
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
|
|
|
|
|
|
|
| |
Make it easier to unit test and continue reduction of main.cpp
Change-Id: Id4f4c4fc1e3928f1b600555c6dbe05f651fffce5
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
|
|
|
|
|
|
|
| |
Testing: Previous test cases get us 100% coverage here
Change-Id: I3b96872bf25ce7ddcc96422f1d453f153f9dd39c
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
|
|
|
|
|
|
|
| |
Make it easier to unit test and continue reduction of main.cpp
Change-Id: I2c0eac5c301687acab14add627586170020e4fe4
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
|
|
|
|
|
|
|
|
|
|
| |
Breaking off into a separate function enables easier unit testing of the
specific function
Testing: 97% coverage of processing.cpp
Change-Id: I08f229657a8f44230b711fabbae20fb403792637
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
|
|
|
|
|
|
|
|
| |
gtest will run test suites in parallel, need to ensure each test suite
(application) requests a distinct well-known name on Dbus.
Change-Id: Ib59211ada5ef6404e70747b7377912384cc8c60e
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
|
|
|
|
|
|
|
| |
These will be useful in other test suites so move into util directory
Change-Id: Ibea5c417e38210e6e1388cf4492af10a64db9077
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
|
|
|
|
|
|
|
|
| |
Other test suites will need this object so move to a utility directory
and inherit from.
Change-Id: Ia34c8149fc0df02c510717a6efd21f51086e97e6
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
|
|
|
|
|
|
|
| |
Testing: Verified code coverage shows 100% of new interface
Change-Id: I517acc02b06bbff971921e66a697fb297fde45c6
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
|
|
|
|
|
|
|
|
| |
Make interface more unit testable by moving to separate
file and documenting it.
Change-Id: Ia27f33d706c62a0011790ec94185dd6be3922f21
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
|
|
|
|
|
|
|
|
| |
Testing is easier when all required parameters are input (vs. global
references)
Change-Id: I0c6c45c79a6095eaa6d702150cc346ac4f8e0e0d
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
|
|
|
|
|
|
|
| |
Testing: Verified 100% code coverage of processing.cpp
Change-Id: I5ebdebe3fdcecbf250a23754f8b5c7db81bfaaa3
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
|
|
|
|
|
|
|
|
|
| |
Move a function to make more testable and add a test case for it
Testing: Verified 100% test coverage in processing.cpp
Change-Id: I0a888009cfeb57bbc8ad295681bea00b79f2a23d
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
|
|
|
|
|
|
|
|
| |
Use the defaults in the pkg check where the default error message is
sufficient to identify which package is missing.
Change-Id: I5bacea77a8a6814bcd21a904219f9e6cb1da58ce
Signed-off-by: Patrick Venture <venture@google.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
When the org.openbmc.Associations.association property changes,
the change may entail removing entries from that property,
in which case association objects may need to be removed.
The existing code did not support this at all, it only
supported adding new associations.
This is done with the help of the associationOwners map, to
know which previous associations an object path and service
had so that it can tell when ones go away.
As a reminder, if an org.openbmc.Associations.associations
property under /path/A looks like:
['forward', 'reverse', '/path/B'],
then the mapper will create 2 new objects - /path/A/forward
and /path/B/reverse, with xyz.openbmc_project.Association.endpoints
property values on them of ['/path/B'], and ['/path/A'], respectively.
If that associations property is then cleared, the mapper
needs to remove the objects it created, assuming there is no
other service that also has an association that requires them.
Resolves openbmc/phosphor-objmgr#16
Change-Id: I28defa831e5aefe8dd09b96c1b104fc1610471d6
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
It is possible that an association object can be sourced from
multiple D-Bus services and paths, and the existing code did
not take that into account when removing assocations on
interfacesRemoved signals, which caused it to overzealously
remove association objects.
For example, both objects /path/A and /path/B can have an
org.openbmc.Associations property value such that the mapper
creates a /path/C association object. Then, when /path/A
is removed from D-Bus, the /path/C association object should
still be kept until /path/B is also gone.
The code accomplishes this be creating a new map of association
metadata called associationOwners that keeps track of
associations based on the service and path of the object that
owns the org.openbmc.Associations object. Now, when the
interfacesRemoved signal comes in for that
org.openbmc.Associations object, the code knows if there are
other owners of an association which determine if the mapper
owned association object can be removed from D-Bus or not.
Change-Id: If483e2ecd1d832b6287c2cbd67c5d23143f9ce32
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The current code would not remove the "/" for the root
path causing a subtree or other call with path being
"/" to miss any object exposed on "/". Fix this
so that "/" is removed when it is the only character.
Tested-by: Phosphor-pid-control stopped throwing when
looking for object managers.
Change-Id: I8ff49617d661910f22cc95409cd2df2489862b5f
Signed-off-by: James Feist <james.feist@linux.intel.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
When any application creates a path on D-Bus, D-Bus will
automatically create all of the parent paths if they
don't already exist with 3 interfaces -
org.freedesktop.DBus.Peer, Introspectable, and Properties.
The mapper knows about those from either an introspection
or from explictly adding them during an interfacesAdded
signal, so now it also has to delete them out of the mapper
on an interfacesRemoved signal for the path being removed.
To do this, when a path is removed, it will traverse all of its
parents and check for the case of where a parent path only has
3 interfaces on it with the same owner, and there are no other
children remaining with that owner.
For example, if /A/B/C/D/E on owner X.Y is received in the
interfacesRemoved handler, then after /A/B/C/D/E is removed
from the interfaces map it will now check to see if /A/B/C/D
is such that:
* /A/B/C/D on owner X.Y has exactly 3 interfaces
* There is no remaining path on X.Y that starts with /A/B/C/D/.
If those are met, then /A/B/C/D on path X.Y will be removed from
the interfaces map, and then the next parent /A/B/C will be checked.
As soon as a parent doesn't need to be removed, the searching
will stop.
Tested: Check that these 3 interface paths that had been created
are properly deleted, such as
/xyz/openbmc_project/logging/entry when the last error
log is removed.
Change-Id: Ia9b79e6905108a76ddd6b425c22104bd77aedb24
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
On an InterfacesAdded signal, a new path may have just been
created with 2 or more new path segments. For example /A/B/C/D
was just created and only /A/B exists so far. In this case, the
current code never adds /A/B/C to the mapper, so trying to call
GetObject or GetSubTree on that path would fail.
To fix that, in the signal handler, check if all of the
parent paths are already in the interface map, and add
them if they aren't. It will take a shortcut and just add
the three org.freedesktop.DBus interfaces Introspectable, Peer,
and Properties, instead of calling the D-Bus introspect method
since that is all it would find anyway.
A big usecase of this is the REST server, where people try to
do things like GET /xyz/openbmc_project/logging/entry/enumerate,
which will fail if no logs existed when the mapper started since
nothing explicitly created that exact path.
Resolves openbmc/openbmc#3450
Tested: Created /xyz/openbmc_project/logging/entry/1,
when previously only /xyz/openbmc_project/logging existed.
Verify calling GetObject and GetSubTree now work on the
/xyz/openbmc_project/logging/entry path.
Change-Id: Id24f78beef9a34245d88de25a65006745fbda992
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
After the first result was found, the code was breaking
out instead of continuing to look for other services that
also satisified the operation. This made it so a path
would never contain results for more than 1 service.
To fix, simply remove the break statement.
Tested: Call GetAncestors on a path with an ancestor that
has multiple services and ensure they all are now
returned.
Change-Id: I6a29b7087d4e5e9763510797e0914c2f8e160495
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
As with GetSubTree, GetAncestors should only return the object
paths with their services and interfaces for just the services
that implement the interfaces passed into the function.
The previous code was returning all services/interfaces for an
object path that was an ancestor instead of just the services
that actually provided that interface.
Tested: Calling GetAncestors on /xyz/openbmc_project/ with
the ObjectManager interface filter would now only
return ancestors that actually had the ObjectManager
interface.
Change-Id: Ie80e78ac76a6fe9d474e6b34bc4555dca490ac51
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
To preserve the behavior of the original mapper, don't
fail the GetSubTree, GetSubTreePaths, and GetAncestors
calls if the results are empty, instead only fail if
the passed in object path isn't valid.
This is particularly important to preserve the REST API
that requires this different behavior between empty
results and bad paths.
Change-Id: I7e808a5613ed66cba4ea1a179a880e5c3f597f2a
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The C++ mapper will now return D-Bus object paths that only
contain the 3 default D-Bus interfaces:
* org.freedesktop.DBus.Introspectable
* org.freedesktop.DBus.Peer
* org.freedesktop.DBus.Properties
Whereas before it would filter out these paths.
As these paths aren't explicitly hosted by a service,
trying to do a GetAll method to read their properties,
which would occur during an enumerate REST operation,
would fail.
The fix is to skip trying to read properties on these
interfaces.
Change-Id: I9b306a4fbefa78c60f739c1351cd32ee2c36c234
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
There are a number of cases where the interface filter causes some
confusion. Not exposing org.freedesktop.DBus.ObjectManager interfaces
caused some confusion around collections of objects, and the ability to
search for sensors. Also, objects where the "parent" object has no
useful interfaces leads to some question about how things like
GetSubTree should behave in this condition.
This patchset removes the interface filter alltogether. The only
negatives should be a slightly increased memory usage, and extra
interface support in mapperx.
On a positive, it makes it a lot less likely to see bugs where the
blacklists are concerned.
Change-Id: I1ffd17106d0c659dd2f438fe25c2d6832a248987
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
|
|
|
|
|
|
|
|
|
| |
See comment for why the code was kept around. It looks like I
misunderstood associations, so that code wasn't required after all.
Clean up the ununsed code..
Change-Id: I5e19c6d633bd5c8e2ef105d147344352b2843753
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
|
|
|
|
|
|
|
|
|
| |
In both GetSubTree and GetSubTreePaths, don't return
the input path as a result. This matches the behavior
of the original mapper.
Change-Id: Ib4bab71a6001e9a18ab2565b0e2d708b034f8c44
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
|
|
|
|
|
|
|
|
| |
Ed wrote the c++ mapper. Matt has been hacking on the mapper a lot
lately as well. It makes sense that they have committer access.
Change-Id: I48cb7e2c32324f395136c4adbdfbcdcc41da894a
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
In cases where multiple daemons expose the same interface, there was a
bug where, if an interface filter was applied, only the first found
daemon would be returned. This resulted in sub-optimal behavior when
searching for things like ObjectManager, that might exist in a lot of
daemons.
Tested By: Before Patch: root@wolfpass:/# dbus-send --system
--print-reply --dest=xyz.openbmc_project.ObjectMapper
/xyz/openbmc_project/object_mapper
xyz.openbmc_project.ObjectMapper.GetSubTree string:"/" int32:1
array:string:org.freedesktop.DBus.ObjectManager
Returns 1 entry
After Patch:
root@wolfpass:/# dbus-send --system --print-reply
--dest=xyz.openbmc_project.ObjectMapper
/xyz/openbmc_project/object_mapper
xyz.openbmc_project.ObjectMapper.GetSubTree string:"/" int32:1
array:string:org.freedesktop.DBus.ObjectManager
returns 12 entries
Change-Id: I0b4e982649f03b0bacf35e26e4d7f310e644d6b8
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The AX_BOOST_BASE macro checks a set of directory locations for the
availability of boost which can cause an issue when one is found and
its below the specified version. (A version number is required when
using the AX_BOOST_BASE macro and was chosen to match the boost version
within the SDK.) This fix replaces the use of the AX_BOOST_BASE macro
with checking for the boost headers used.
Resolves: openbmc/openbmc#3411
Change-Id: Ia24120e55f77576a45f6f85988328f7127ea00c7
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
|
|
|
|
|
|
|
|
|
|
|
| |
Use dbus service/busname filtering to match the c++ mapper. This
facilitates an easy transition to c++ mapper.
Set service-namespace as a required parameter.
Set path-namespace as an optional parameter.
Change-Id: I6b16622ee1cb354030a82e7b9d412235933c2d39
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The c++ mapper implements a dbus service namespace whitelist instead of
a path namespace whitelist. A service namespace whitelist significantly
reduces the amount of introspection required by the mapper as compared
to path namespaces. Update the python mapper to accept the new command
line arguments.
This is entirely throw-away code to reduce corequisites as users
(OpenBMC) transition to the c++ mapper. As such, no logic is
implemented behind the new command line options.
Change-Id: Ic952e85dbd44e850059e67a3675ddbf1dd2f15b1
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
|
|
|
|
|
|
|
|
| |
605206314f added use of flat_map/set and some boost algorithm.
Change-Id: I63465542fb3b9c02e2890ff52ebdd3fd0e4c6e01
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
|
|
|
|
|
|
|
| |
Unlock new language and feature support.
Change-Id: I171b90570cd630fc7cec03b1f5fe617ebfde5de3
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
|
|
|
|
|
|
|
|
| |
If '/A/B/C' is passed in as the path argument into GetAncestors,
don't return /A/B/C, but only its actual ancestors /A and /A/B.
Change-Id: I8dde80fa2ae5fad26c93be5fda5cfda9ff1b77d8
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The 'mapper subtree-remove' command calls GetSubTreePaths
when it starts up to check if a certain interface currently
exists on a certain path. If that interface does not exist
on that path, the current ObjectMapper implementation would
just return an empty list, and the program would exit.
The new ObjectMapper implementation will fail the GetSubTreePaths
call in that case, causing an ENXIO inside of sd_event_loop().
This commit checks for the ENXIO and then just exits immediately
with a return code of 0, as this is the case where the interface
had been removed before the program was started.
Change-Id: Iaeb908ee9a0db8952ef77cc63675e0f7abe3b6c7
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
|
|
|
|
|
| |
Change-Id: I67b3b1194aaa5eb174c522220d637bc231f883f1
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
|
|
|
|
|
|
|
| |
These were good for debug, but shouldn't be necessary anymore.
Change-Id: I0a2947dbd95da2532b2689329020519c39a21f6e
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Associations are D-Bus objects that this application
creates when another application creates an instance
of an org.openbmc.Associations interface.
For example, if an application with D-Bus path 'pathA'
creates an instance of org.openbmc.Associations with
an 'associations' property of:
['forward', 'reverse', 'pathB']
the mapper will create 2 new D-Bus objects:
1) 'pathA/forward'
2) 'pathB/reverse'
1) will have an xyz.openbmc_project.Association interface
with an 'endpoints' property that contains ['pathB']
2) will have an xyz.openbmc_project.Association interface
with an 'endpoints' property that contains ['pathA']
If pathA is removed from the bus, 1) should be completely removed,
and 2) should have 'pathA' removed from the endpoints property.
If the endpoints property in 2) is now empty, that whole
object should be removed as well.
If, when adding the xyz.openbmc_project.Association interface, it
already exists, then the endpoints property should just be added
to.
This commit finishes adding support for all of this.
Change-Id: Ib1b3d39407a4b75992664552a766e49fdb27123f
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
|
|
|
|
|
|
|
| |
A perfect example for how much typing it can save.
Change-Id: I1826f5f7fe7c21aa39edd3759e748a567cf03a92
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
|
|
|
|
|
|
|
|
| |
GetObject should only return the services (with their interfaces)
that provide the interface passed in.
Change-Id: I76ca7be85c7f2da4cc66d865a671c27504cb8061
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
|
|
|
|
|
|
|
|
|
|
|
| |
GetSubTree should only return service names and their interfaces
for services that implement the interfaces passed into the function.
The previous code would return every service/interface list if any
service implemented the specified interfaces.
Change-Id: I351af0aedd553e45125bffe2dd72aa352ec8d403
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This allows service whitelists and blacklists, and
interface whitelists to be passed into the application.
The whitelists can be prefixes, like xyz.openbmc_project.
The blacklist is the full service name.
A future commit can add support for interface blacklists.
Change-Id: I91f6ef2f7be63e4d13ac03d570bba18ef8277fae
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This commit attempts to reimplement the mapper using C++/sdbusplus
in the hopes of improving performance both at startup (lower priority)
and runtime (higher priority).
After this patch, performance seems to be greatly improved. On an unloaded system, full
introspection of all daemons clocks in at 2.23 seconds for the worst
case. Prelimiary tests show this to be 7X faster than the existing
client. Expect this to come down slightly once associations are
implemented, as that will introduce some overhead.
It should not yet be considered complete although it does function.
The things that still need doing are:
1. Implement the configurable whitelist/blacklist that the existing
manage implements. Today they are compiled in.
Tested By:
time busctl call xyz.openbmc_project.ObjectMapper
/xyz/openbmc_project/object_mapper xyz.openbmc_project.ObjectMapper
GetSubTree sias "/" 0 1 xyz.openbmc_project.State.Chassis
a{sa{sas}} 1 "/xyz/openbmc_project/state/chassis0" 1
"xyz.openbmc_project.State.Chassis" 2
"org.freedesktop.DBus.ObjectManager" "xyz.openbmc_project.State.Chassis"
C++ implementation
real 0m0.092s
user 0m0.040s
sys 0m0.010s
Python implementation
real 0m0.416s
user 0m0.010s
sys 0m0.030s
Also has been tested using reboots, daemon dropouts and reconnects.
Change-Id: I46ca8c273df09261cb2a2448a3570a601ea8e9f4
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
Signed-off-by: James Feist <james.feist@linux.intel.com>
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
|
|
|
|
|
|
|
|
| |
Pick up latest clang-format from the docs repo and rerun
with clang-format-6.0
Change-Id: I2f0411bb01d78f096563d63b197ce12daf43bcbd
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
|
|
|
|
|
|
|
|
| |
This exception class was not removed when its usage disappeared in
d43087f294949583507b5520c8f0b2c359296aa1.
Change-Id: I937f78ac37018ad59e8cb5fc65819497126f02d9
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
|
|
|
|
|
|
|
|
|
|
|
| |
The dbus daemon can return ENOBUFS if a server has too many outstanding
requests that have not yet been serviced. We've noticed during boot that
the load on the mapper is great enough that we will hit this dbus limit.
Since this condition eventually improves, we want to retry just like
with EBUSY.
Change-Id: Ia21d87fba1793016e7c9dfa835fbe7bac0085f10
Signed-off-by: William A. Kennington III <wak@google.com>
|
|
|
|
|
|
|
|
| |
Equivalent function can be simulated with busctl call and mapper
get-service _and_ doesn't rely on systemd patches.
Change-Id: Ia0c3c8d7d0290de2b6e79f18df72cac97f2b14fa
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
|
|
|
|
|
|
|
|
|
| |
We don't want to keep retrying every 1 second for 5 tries. This would
allow the timeout to lapse for a very busy BMC and just cause more
congestion. Instead backoff at exponentially increasing intervals.
Change-Id: I9780d9a3dc787a6936aca2c2af30418dd2b0bf4b
Signed-off-by: William A. Kennington III <wak@google.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Don't rely on undefined behavior in failed read calls where some of the
message is read. Right now the subtree remove callback parses the message
of type "as" into a 1 element array. It uses this to determine if the
array is empty or not. This depends on the underlying implementation to
populate one of the array elements in the case where it has more than
one and produces an error.
Instead, properly enter the array container and check to see if it is empty
while doing error handling for the calls.
Change-Id: I542c488524a5dce5466d6196879159d888e47346
Signed-off-by: William A. Kennington III <wak@google.com>
|