The services layer of the QNX SDK for Apps and Media is built on the Persistent
Publish/Subscribe (PPS) service, a simple filesystem-based facility that
provides information persistence across reboots. Small and extensible, PPS allows
interfacing from almost any higher-level language that supports open, read, write, and
close operations on files.
Key concepts
- Objects
-
Objects are implemented as files under the /pps
directory. Your apps and HMI use objects to communicate with each other.
There can be many objects in the system but never more than one instance of
the same object.
-
Apps and HMI services often use a control object for sending
commands and a corresponding status object for publishing
responses.
-
Client apps can read the special .all object to get
notifications of changes to all the objects in a directory. They can
use the special .notify object to get changes for a
certain set of objects.
- Attributes
-
Objects contain attributes (or properties) that apps can modify.
Each attribute appears on a single line in the object file.
- Publishers
-
As publishers, apps can modify objects and their attributes so that other
interested apps can receive updates. Publishing is asynchronous—apps
don't have to wait for the publisher.
- To publish to an object, the publisher calls open() for that
object and then write() to
modify it. Multiple publishers can publish to the same object. When a
publisher changes an object, the PPS service informs all subscribers of
the change.
- Subscribers
-
As subscribers, apps receive updates for objects and attributes that
publishers have modified. To get updates for an object, a subscriber calls
open() for that
object and then read() to query it.
Note that reads are nonblocking by default. Multiple subscribers can
subscribe to the same object.
Note: The same app can be a publisher, a subscriber, or both.
- Full subscription mode
-
In full mode (the default), the subscriber gets a "snapshot" of the
entire object as it exists when the request is made. Note that if a
publisher changes the object many times, the subscriber may miss some of the
changes. Full mode is useful, for instance, for high-bandwidth objects that
have numerous and frequent changes.
- Delta subscription mode
-
In delta mode, the subscriber gets only the changes made to an object. On
first read, the subscriber will get all the object's attributes
(because the subscriber knows nothing yet about the object's state);
subsequent reads will return only the changes since the previous read. Delta
mode is useful, for instance, when you want to receive all the warnings or
error messages that might be published to an object.
- Persistence
-
PPS maintains objects in memory while it's running and can save them to
persistent storage (either at shutdown or on demand) on any reliable
filesystem, such as flash or hard disk. Objects can be restored immediately
on startup or on first access.
- Server objects
-
PPS supports point-to-point communication between a server and one or more
clients. An app can designate itself as the server when
creating a PPS object. When a client writes to this object, only the server
gets the message. PPS appends a unique identifier to the object name so that
the server knows which client app is sending the message.
- When the server replies, it must append the same identifier to the object
name so that the response is sent only to the client indicated by that
identifier. In this communication mode, both the server and the clients
read from and write to the object. For more details, see
"Server objects" in the
Persistent Publish/Subscribe Developer's Guide.
Command-line options for the PPS service
pps [-A file][-b][-C][-d backlog][-l argument][-m mount][-p dir]
[-P prio][-t period][-T tolerance][-U uid:gid][-v]
- -A
file
- Set the path to the ACL configuration file. For details, see "Access Control List configuration
file" in the Persistent Publish/Subscribe
Developer's Guide.
- -b
- Don't run in the background (useful for debugging).
- -C
- Convert between -U and non-U persistence formats.
- -d
backlog
- Set the default size of the delta backlog, in kilobytes
(default is 256 kilobytes).
- -l
argument
- Set the object load behavior:
- 0 — load directory names and objects on demand
(default).
- 1 — load directories at startup, but objects on demand.
- 2 — load directories and objects at startup.
- -m
mount
- Specify the mountpoint for PPS (default is /pps).
- -p
dir
- Specify the directory for persistent storage (default is
/var/pps).
- -P
prio
- Set the priority of the persistence thread.
- -t
period
- Set the time period (in milliseconds) for writing to persistent storage
(default is off).
- -T
tolerance
- Set the tolerance (in milliseconds) for writing to persistent storage
(default is off).
- -U
uid:gid
- Downgrade from root to the specified UID and GID.
- -v
- Run in verbose mode (use multiple v's to increase
verbosity).
Note: You can also use SIGUSR1 to increase
verbosity.
Pathname options
PPS lets you use various pathname options when opening objects. An option must
follow a question mark (?). Use a comma to separate multiple options.
For example, opening the playlist object like this:
/pps/media/playlist?wait,delta
will open the object with the wait and delta
options.
You can set these options:
- backlog=size
- Set the total delta size to keep before flushing this client's buffer of
deltas. The size is in kilobytes, so 4 means 4 KB. The default is 256 KB,
unless you specify the -d option, which overrides the
default delta size.
- cred
- Add client credentials to this object.
This option is effective only when server is used,
because it tells PPS to pass the client's PID, UID, and GID to the
server by including these fields in the object name.
- critical
- Designate the publisher as critical to the object. For details, see the
"Critical option" section in the
PPS Developer's Guide.
- delta
- Open the object in delta mode, which means only the changes made to the
object are returned by a read operation.
- deltadir
- Return the names of all objects listed in the .all
object in a directory.
- f=attrspec{+attrspec}...
- Filter notifications based on changes to the names and/or values of
specified attributes, where attrspec can be either an
attribute's name or an expression specifying an attribute's value. A value
expression consists of an attribute name, followed by an operator, followed by a value.
- Operators for integers (which must be in the range of a long long type)
are: <, <=,
>, >=,
=, ==, and
!=
- Operators for strings are: =, ==,
and != (you can use + if escaped
with \)
- flow=backlog_size
- Deliver purge notifications for this object (similar to a
server object).
This flag takes an optional argument for the number of kilobytes of
backlog (i.e., series of deltas) that the server is permitted. If you
don't specify this argument, the backlog size is used;
if this other option isn't defined, the default size of 256 KB is used.
- When the server falls behind in reading the object and the backlog exceeds
the size specified in flow, the object will be purged and
the server will receive purge notifications of the form
|@objname.
- A purge will occur for a client if it doesn't read the reply data at a fast
enough rate. In this case, the server will received purge notifications of
the form |@objname.clientid.
- The flow flag is effective only when delta
is used, and is mutually exclusive with backlog and
server (because it enables the server mode internally).
- hiwater=backlog_percentage
- Deliver overflow notifications for this object when the client backlog
exceeds a certain limit. This flag takes a mandatory argument in the range
of 1 to 99, to indicate the percentage of client backlog at which the server
will begin receiving overflow notifications.
We refer to this limit as the high watermark for overflow.
- As long as the backlog remains above this limit, the server will receive a
notification of the form ^@objname.clientid for every write
that it performs on the object.
- The hiwater flag is valid only with the
flow flag and must be explicitly set to enable overflow
notifications. The default hiwater value of 100 means the
service waits until the client backlog is full before purging the object
and hence, no overflow notifications are sent.
- nopersist
- Make the object nonpersistent.
- notify=id:value
- Associate the object with the notification group specified by
id:value, where:
- id is the string returned by the first read from
the .notify object
- value is any arbitrary string
- opens
- Update an _opens::rd,wr attribute when the open count
changes.
- reflect
- Reflect attribute updates made on this object back to the process that wrote
them. When this option is set, if a process writes data to the object and
then reads the object using the same file descriptor, the process will read
the data that it wrote.
By default, this option isn't set and a read() operation
won't return the data written with the same file descriptor, because this
isn't considered a change.
- server
- Designate the publisher as a server for the object
(see "Server Objects"
for details).
- verbose
- Set the verbosity level for this object.
- wait
- Clear the O_NONBLOCK flag so that read()
calls will wait for any object changes, including deltas.
Object format
Objects appear as files in the PPS filesystem. For example, to view
the contents of an object called AA:BA:19:B2:AA:70
(in this case, the filename is a device's MAC address) under the
/pps/services/bluetooth/remote_devices/ directory, you can
simply use cat at the command line:
cat /pps/services/bluetooth/remote_devices/AA:BA:19:B2:AA:70
The object's contents might look like this:
@AA:BA:19:B2:AA:70
[n]cod::0x007a020c
[n]name::My mobile
[n]paired:b:false
[n]rssi::0x00
The first line always begins with an AT sign (@), immediately
followed by the object's name. Each line afterwards begins with a qualifier,
followed by an attribute name, followed by its encoding, followed by its value.
For example, this line:
[n]paired:b:false
means that the nonpersistence qualifier ([n]) has been set and
that the attribute paired has the Boolean value of
false.
Note:
For details on encodings and on qualifiers, see these sections in the
Persistent Publish/Subscribe Developer's Guide:
Format for messages to server objects
Messages written to server objects must have this format:
msg::command_string\nid::ID_number\ndat:json:{JSON_data}
where:
- command_string
- Name of the command being sent to the object.
- ID_number
- Any ID that identifies this instance of the message. The server always
reflects the ID back in the response.
- JSON_data
- The dat attribute is usually JSON-encoded, because it may contain
more than a simple string.
Format for responses
Responses always reflect the command_string and
ID_number that were sent in the message, along with any
errors:
res::command_string\nid::ID_number\ndat:json:{JSON_data}\nerr::errno_number\nerrstr::error_description
Changing the directory for persistent storage
The root PPS object tree (/pps by default) may look
something like this:
# pwd
/pps
# ls -1F
accounts/
applications/
qnx/
services/
system/
#
PPS populates its root object tree from the persistence tree
(/var/pps by default), where the objects and attributes
that you want to persist are stored.
To specify a different directory for persistent storage:
- Create your own persistence directory (e.g., mkdir
/myobjects).
Start the PPS service from a different mountpoint (e.g.,
/fs/pps) and specify your new persistence
directory:
pps -m /fs/pps -p /myobjects
Note:
You may want to run PPS with the
-t option, which lets you specify the time period (in
milliseconds) that the service will use to write to persistent storage. Without
the -t, you won't see any changes in your persistence directory
until PPS exits.