diff --git a/doc/errorref.txt b/doc/errorref.txt
index e62fe15..997c800 100644
--- a/doc/errorref.txt
+++ b/doc/errorref.txt
@@ -1,1136 +1,1137 @@
# errorref.txt - Description of error codes
# Copyright (C) 2003-2004, 2010, 2013-2016 g10 Code GmbH
#
# This file is part of libgpg-error.
#
# libgpg-error is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1 of
# the License, or (at your option) any later version.
#
# libgpg-error is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, see .
##
## Note that lines with a leading double hash will not installed.
## Please do not put any tabs into this file.
##
## find ~/s/{gnupg,libgpg-error,libksba,libgcrypt,gpgme,gpa} -type f \
## -name '*.[ch]' -print0 | xargs -0 grep -n GPG_ERR_
GPG_ERR_UNKNOWN_PACKET Unknown packet
GNUPG: - Redefined to G10ERR_UNKNOWN_PACKET in gpg.
GPG_ERR_UNKNOWN_VERSION Unknown version in packet
Used by GnuPG 2.1 to identify valid OpenPGP packets with an
unknown version.
GPG_ERR_PUBKEY_ALGO Invalid public key algorithm
GNUPG: - Redefined to G10ERR_PUBKEY_ALGO in gpg.
- Public key algorithm is not allowed by OpenPGP.
GCRYPT: - Public key algorithm is not defined or not available.
Note that this is also the case if the algorithm has
been disabled.
- [version < 1.5] Checking of the RSA secret key failed
(consistency check).
GPG_ERR_DIGEST_ALGO Invalid digest algorithm
GNUPG: - Digest algorithm is not supported.
- Redefined to G10ERR_PUBKEY_ALGO in gpg.
- Digest algorithm is not allowed by OpenPGP.
- Unsupported algorithm given to "--hash=" option of
certain Assuan server commands.
- Signature creation or verification failed due to
an unsupported hash algorithm.
GCRYPT: - Digest key algorithm is not defined or not available.
Note that this is also the case if the algorithm has
been disabled.
- Unsupported digest algorithm in a selftest.
- Invalid digest algorithm used in FIPS mode. Note that
in enforced-FIPS mode there is no such error return.
- Message digested or HMAC computation finished with no
message algorithm enabled for the hash context.
- Bad digest algorithm given to public key function.
GPG_ERR_BAD_PUBKEY Bad public key
GNUPG: - Redefined to G10ERR_BAD_PUBKEY in gpg.
- Missing public or domain parameter in an s-expression.
If the curve name is mssing GPG_ERR_INV_CURVE may be
used as well.
GPG_ERR_BAD_SECKEY Bad secret key
GNUPG: - Invalid format of a S-expression encoded private key in
gpg-agent.
- Missing secret parameter in an s-expression.
- A protected or shadowed private key was passed to the
OpenPGP card application for storing it on the card.
- A private key passed to the OpenPGP card application does
not match the requirements of the card or misses required
parameters.
- Gpg'agents import key command is not able to convert
the key to the internal format.
GCRYPT: - Checking the secret key failed (consistency check).
GPG_ERR_BAD_SIGNATURE Bad signature
GNUPG: - Redefined to G10ERR_BAD_SIGN in gpg.
- The MDC check of an OpenPGP encrypted message failed.
- A OpenPGP key signature did not verify.
- A signature with a key flagged as "never trust" was made.
GCRYPT: - A public key signature did not verify.
GPG_ERR_NO_PUBKEY No public key
GNUPG: - Redefined to G10ERR_NO_PUBKEY in gpg.
- A key was requested from an OpenPGP card but the key is
not stored on the card.
- The public key could not be retrieved from a corresponding
certificate on a card (command READKEY in scd).
- A requested certificate was not found or an unspecified
error occurred while selecting a X.509 certificate in
gpgsm.
- The specified certificate or key was not found. This
does not necessary mean that the certifciate is not
available but the specification method may not be usable
for the given certificate. May also happen for
certificates somewhere in the chain while validaiting a
certificate chain.
- The requested encryption certificate was not found.
- A certificate specified in a CMS message is not
available and thus the signature could not be verified
or details of the certificate be shown.
GPA: - No key was given for encryption.
- The selected encryption protocol is not available.
GPG_ERR_CHECKSUM Checksum error
GNUPG: - The checksum of an unprotected OpenPGP secret key packet
is wrong.
GCRYPT: - Decryption in AESWRAP mode does not match the expected IV.
[more to come]
GPG_ERR_BAD_PASSPHRASE Bad passphrase
GNUPG: - The entered passphrase does not verify
GPG_ERR_CIPHER_ALGO Invalid cipher algorithm
GPG_ERR_KEYRING_OPEN Cannot open keyring
GPG_ERR_INV_PACKET Invalid packet
GPG_ERR_INV_ARMOR Invalid armor
GPG_ERR_NO_USER_ID No user ID
GPG_ERR_NO_SECKEY No secret key
NTBTLS: - No private key or pre-shared key available.
GPG_ERR_WRONG_SECKEY Wrong secret key used
GPG_ERR_BAD_KEY Bad session key
GNUPG: - gpg-agent's command IMPORT_KEY or EXPORT_KEY is used
without a prior KEYWRAP_KEY command.
[more to come]
GPG_ERR_COMPR_ALGO Unknown compression algorithm
GPG_ERR_NO_PRIME Number is not prime
GPG_ERR_NO_ENCODING_METHOD Invalid encoding method
GPG_ERR_NO_ENCRYPTION_SCHEME Invalid encryption scheme
GPG_ERR_NO_SIGNATURE_SCHEME Invalid signature scheme
GPG_ERR_INV_ATTR Invalid attribute
GPG_ERR_NO_VALUE No value
GNUPG: - A timestamp value is expect but there is none.
KSBA: - A timestamp value is expect but there is none.
- A certificate is missing a required property.
- A CMS object is missing a required property.
- Converting a Distinguised Name to an RFC2253 string failed.
GPG_ERR_NOT_FOUND Not found
A search operation did not return a matching value.
GPG_ERR_VALUE_NOT_FOUND Value not found
GNUPG: - A keyblock or a cert object was requested but not
found. This might indicate an internal error here.
GPG_ERR_SYNTAX Syntax error
GPG_ERR_BAD_MPI Bad MPI value
GPG_ERR_INV_PASSPHRASE Invalid passphrase
GNUPG: - Required constraints of the passphrase are not met.
GPG_ERR_SIG_CLASS Invalid signature class
GPG_ERR_RESOURCE_LIMIT Resources exhausted
GPG_ERR_INV_KEYRING Invalid keyring
GPG_ERR_TRUSTDB Trust DB error
GPG_ERR_BAD_CERT Bad certificate
NTBTLS: - No subject found in the certifciate.
GPG_ERR_INV_USER_ID Invalid user ID
GNUPG: - Used to indicate a bad specification of a user id.
[more to come]
GPG_ERR_UNEXPECTED Unexpected error
GPG_ERR_TIME_CONFLICT Time conflict
GPG_ERR_KEYSERVER Keyserver error
GPG_ERR_WRONG_PUBKEY_ALGO Wrong public key algorithm
GNUPG: - The algorithm is not expected. For example a DSA
algorithm is used where a non-DSA algorithm is expected
or vice versa. May indicate an internal error.
NTBTLS: - Public key type mismatch. The peer presented a
different key type than requested.
GPG_ERR_TRIBUTE_TO_D_A Tribute to D. A.
GPG_ERR_WEAK_KEY Weak encryption key
GPG_ERR_INV_KEYLEN Invalid key length
GPG_ERR_INV_ARG Invalid argument
GCRYPT: - Unsupported length of input data in encrypt or decrypt
cipher functions. For example not matching the block
lengths of the algorithm.
- Incompatible args given; e.g. two or none if exactly one
is required.
[more to come]
GPG_ERR_BAD_URI Syntax error in URI
GPG_ERR_INV_URI Invalid URI
GPG_ERR_NETWORK Network error
GPG_ERR_UNKNOWN_HOST Unknown host
Used instead of the non-portable EHOSTNOTFOUND which is
returned by some systems as a mapping of h_errno's
HOST_NOT_FOUND
GPG_ERR_SELFTEST_FAILED Selftest failed
GPG_ERR_NOT_ENCRYPTED Data not encrypted
GPG_ERR_NOT_PROCESSED Data not processed
GPG_ERR_UNUSABLE_PUBKEY Unusable public key
GPG_ERR_UNUSABLE_SECKEY Unusable secret key
GPG_ERR_INV_VALUE Invalid value
NTBTLS: - A DH parameter is out of range
GPG_ERR_BAD_CERT_CHAIN Bad certificate chain
GPG_ERR_MISSING_CERT Missing certificate
NTBTLS: - The server needs to send a certifciate but none has been
set. See also GPG_ERR_MISSING_ISSUER_CERT and
GPG_ERR_MISSING_CLIENT_CERT.
GPG_ERR_NO_DATA No data
GPG_ERR_BUG Bug
GPG_ERR_NOT_SUPPORTED Not supported
Used if a feature is currently not supported but may be
enabled for example using a program option. Commonly used if
a feature has been disabled by an administrator. See also
GPG_ERR_NOT_ENABLED. Sometimes also used for features which
are not yet supported.
GPG_ERR_INV_OP Invalid operation code
GPG_ERR_TIMEOUT Timeout
Some function or network access timed out.
GPG_ERR_INTERNAL Internal error
GPG_ERR_EOF_GCRYPT EOF (gcrypt)
GPG_ERR_INV_OBJ Invalid object
GPG_ERR_TOO_SHORT Provided object is too short
GPG_ERR_TOO_LARGE Provided object is too large
GPG_ERR_NO_OBJ Missing item in object
GPG_ERR_NOT_IMPLEMENTED Not implemented
NTBTLS: - The requested feature is not implemented.
GPG_ERR_CONFLICT Conflicting use
NTBTLS: - Function has already been called and may not be called
again at this protocol state.
GNUPG: - Returned by g13 when creating a new container on a device
which seems to be in use.
GPG_ERR_INV_CIPHER_MODE Invalid cipher mode
GPG_ERR_INV_FLAG Invalid flag
GPGME: Used to indicate an invalid combination of flags.
GPG_ERR_INV_HANDLE Invalid handle
GPG_ERR_TRUNCATED Result truncated
GPG_ERR_INCOMPLETE_LINE Incomplete line
GPG_ERR_INV_RESPONSE Invalid response
GPG_ERR_NO_AGENT No agent running
GPG_ERR_AGENT agent error
GPG_ERR_INV_DATA Invalid data
GNUPG: - Used in app-openpgp.c for a badly formatted request.
GCRYPT: - No passphrase given for gcry_kdf_derive.
- An opaque MPI is given to a public key function but not
expected.
GPG_ERR_ASSUAN_SERVER_FAULT Unspecific Assuan server fault
GPG_ERR_ASSUAN General Assuan error
GNUPG: - Used by Assuan command handler if they fail to do basic
things like an es_fdopen or es_fopencookie.
GPG_ERR_INV_SESSION_KEY Invalid session key
GPG_ERR_INV_SEXP Invalid S-expression
GPG_ERR_UNSUPPORTED_ALGORITHM Unsupported algorithm
GPG_ERR_NO_PIN_ENTRY No pinentry
GPG_ERR_PIN_ENTRY pinentry error
GPG_ERR_BAD_PIN Bad PIN
GPG_ERR_INV_NAME Invalid name
GNUPG: - Formerly used in GPGSM to indicate an error in
the specification of a user id. Later replaced by
GPG_ERR_INV_USER_ID.
- In G13 to indicate a bad file name (e.g. one with
an embedded Nul byte when given as escaped string.
- In SCDAEMON for an unknown attribute name.
Also used for URLs which have non-acceptable characters for the
specific application.
[more to come]
GPG_ERR_BAD_DATA Bad data
GPG_ERR_INV_PARAMETER Invalid parameter
GNUPG: - Returned if gpg-agent sends a new generated key with
unknown parameter names.
- Invalid parameter in the parameter file for key
generation by gpgsm.
GPG_ERR_WRONG_CARD Wrong card
GPG_ERR_NO_DIRMNGR No dirmngr
GPG_ERR_DIRMNGR dirmngr error
GPG_ERR_CERT_REVOKED Certificate revoked
GPG_ERR_NO_CRL_KNOWN No CRL known
GPG_ERR_CRL_TOO_OLD CRL too old
GPG_ERR_LINE_TOO_LONG Line too long
GPG_ERR_NOT_TRUSTED Not trusted
GPG_ERR_CANCELED Operation cancelled
GPG_ERR_BAD_CA_CERT Bad CA certificate
GPG_ERR_CERT_EXPIRED Certificate expired
GPG_ERR_CERT_TOO_YOUNG Certificate too young
GPG_ERR_UNSUPPORTED_CERT Unsupported certificate
GPG_ERR_UNKNOWN_SEXP Unknown S-expression
GPG_ERR_UNSUPPORTED_PROTECTION Unsupported protection
GPG_ERR_CORRUPTED_PROTECTION Corrupted protection
GPG_ERR_AMBIGUOUS_NAME Ambiguous name
GPG_ERR_CARD Card error
GPG_ERR_CARD_RESET Card reset required
GPG_ERR_CARD_REMOVED Card removed
GPG_ERR_INV_CARD Invalid card
GPG_ERR_CARD_NOT_PRESENT Card not present
GPG_ERR_NO_PKCS15_APP No PKCS15 application
GPG_ERR_NOT_CONFIRMED Not confirmed
GPG_ERR_CONFIGURATION Configuration error
GPG_ERR_NO_POLICY_MATCH No policy match
GPG_ERR_INV_INDEX Invalid index
GPG_ERR_INV_ID Invalid ID
GPG_ERR_NO_SCDAEMON No SmartCard daemon
GPG_ERR_SCDAEMON SmartCard daemon error
GPG_ERR_UNSUPPORTED_PROTOCOL Unsupported protocol
GPG: - An unsupported keyserver protocol.
GPG_AGENT: - Invalid shadow_info protocol (not "t1-v1")
LIBKSBA: - Unknown OID of the OCSP response bytes
GPGME: - GPGME_PROTOCOL_xxx not supported.
NTBTLS: - Handshake protocol version not supported.
GPG_ERR_BAD_PIN_METHOD Bad PIN method
GPG_ERR_CARD_NOT_INITIALIZED Card not initialized
SCDAEMON: - A card function is called but the card has not yet
been initialized. This may be due to a conflict with
another card using connection or due to a bug.
GPG_ERR_UNSUPPORTED_OPERATION Unsupported operation
GPG_ERR_WRONG_KEY_USAGE Wrong key usage
GNUPG: - Key usage not possible with selected algorithm.
GPG_ERR_NOTHING_FOUND Nothing found
Indicates that the operation was not possible because nothing has
been found. For example an update request for non existent data.
GPG_ERR_WRONG_BLOB_TYPE Wrong blob type
GPG_ERR_MISSING_VALUE Missing value
GNUPG: - Not enough parameters for a secret key send to gpg-agent.
GCRYPT: - A required parameter has not been given.
GPG_ERR_HARDWARE Hardware problem
GPG_ERR_PIN_BLOCKED PIN blocked
GPG_ERR_USE_CONDITIONS Conditions of use not satisfied
GPG_ERR_PIN_NOT_SYNCED PINs are not synced
GPG_ERR_INV_CRL Invalid CRL
GPG_ERR_BAD_BER BER error
GPG_ERR_INV_BER Invalid BER
GPG_ERR_ELEMENT_NOT_FOUND Element not found
GPG_ERR_IDENTIFIER_NOT_FOUND Identifier not found
GPG_ERR_INV_TAG Invalid tag
GPG_ERR_INV_LENGTH Invalid length
GCRYPT: - Bad block length for certain cipher algorithms and
modes.
- Bad length of input data; e.g. not a multiple of the
block length.
- A length does not match the size of the digest
algorithm.
- Length of signature or public key is not as expected
(e.g. in EdDSA).
[more to come]
GNUPG: - Invalid hash length for a pubkey
[more to come]
GPG_ERR_INV_KEYINFO Invalid key info
KSBA: - Returned if the ASN.1 Keyinfo structure is not valid
GPG_ERR_UNEXPECTED_TAG Unexpected tag
GPG_ERR_NOT_DER_ENCODED Not DER encoded
GPG_ERR_NO_CMS_OBJ No CMS object
GPG_ERR_INV_CMS_OBJ Invalid CMS object
GPG_ERR_UNKNOWN_CMS_OBJ Unknown CMS object
GPG_ERR_UNSUPPORTED_CMS_OBJ Unsupported CMS object
GPG_ERR_UNSUPPORTED_ENCODING Unsupported encoding
GNUPG: - Returned by Dirmngr if a keyserver returns a HTML document.
GPG_ERR_UNSUPPORTED_CMS_VERSION Unsupported CMS version
GPG_ERR_UNKNOWN_ALGORITHM Unknown algorithm
GCRYPT: gcry_kdf_proc for an unknown kdf algorithm
GPG_ERR_INV_ENGINE Invalid crypto engine
GPGME: Several uses use cases. For example:
- Unexpected format of a status line.
GPG_ERR_PUBKEY_NOT_TRUSTED Public key not trusted
GPG_ERR_DECRYPT_FAILED Decryption failed
GPG_ERR_KEY_EXPIRED Key expired
GPG_ERR_SIG_EXPIRED Signature expired
GPG_ERR_ENCODING_PROBLEM Encoding problem
GPG_ERR_INV_STATE Invalid state
The state (of a protocol) is not possible or not defined at all.
NTBTLS: - Data received in an unexpected state.
- A function is called while not being in the right state.
GPG_ERR_DUP_VALUE Duplicated value
GPG_ERR_MISSING_ACTION Missing action
GNUPG: - In G13 the server command "MOUNT" is used without prior
use of the command "OPEN".
others: - The libassuan ce-server test program uses this to
indicate that the client did not connect to the server
as requested.
GPG_ERR_MODULE_NOT_FOUND ASN.1 module not found
GPG_ERR_INV_OID_STRING Invalid OID string
GPG_ERR_INV_TIME Invalid time
GPG_ERR_INV_CRL_OBJ Invalid CRL object
GPG_ERR_UNSUPPORTED_CRL_VERSION Unsupported CRL version
GPG_ERR_INV_CERT_OBJ Invalid certificate object
GPGME: - A bad certificate (gpgme_key_t) has been passed to a
function. For example it might be incomplete due to a
missing fingerprint.
+ GNUPG: - A certificate has a length of zero.
GPG_ERR_UNKNOWN_NAME Unknown name
Used by GPG to indicate an unknown ECC curve name (may also
indicate missing ECC support). It is also used to indicate an
unsuported parameter name in functions which take a name and
value to update state. Note that GPG_ERR_UNKNOWN_CURVE is
used instead by newer code.
GPG_ERR_LOCALE_PROBLEM A locale function failed
GPG_ERR_NOT_LOCKED Not locked
GPG_ERR_PROTOCOL_VIOLATION Protocol violation
GNUPG: - Used for invalid HTTP responses.
GPG_ERR_INV_MAC Invalid MAC
The length, algo, or other properties of a MAC are not met.
See also GPG_ERR_BAD_MAC.
GPG_ERR_INV_REQUEST Invalid request
GPG_ERR_UNKNOWN_EXTN Unknown extension
GPG_ERR_UNKNOWN_CRIT_EXTN Unknown critical extension
GPG_ERR_LOCKED Locked
GPG_ERR_UNKNOWN_OPTION Unknown option
GPG_ERR_UNKNOWN_COMMAND Unknown command
GPG_ERR_NOT_OPERATIONAL Not operational
GPG_ERR_NO_PASSPHRASE No passphrase given
GPG_ERR_NO_PIN No PIN given
GPG_ERR_NOT_ENABLED Not enabled
Similar to GPG_ERR_NOT_SUPPORTED. In general this error is
used for disabled features which can be expected to be enabled
by the user.
GPG_ERR_NO_ENGINE No crypto engine
GPG_ERR_MISSING_KEY Missing key
GNUPG: - gpg-agent returns this error on import or export if a key
wrapping transport key has not been specified.
- It is used when the name "Key" is not found while looking
up name value pairs of the extended private key format
GCRYPT: - A key has not been set when calling a symmetric
encryption function.
GPG_ERR_TOO_MANY Too many objects
GPG: - Dirmngr KS_GET called with too many pattern so that the
maximum Assuan line length would overflow.
- gpgsm's command export --secret called with too man keys.
GPGME: - To many patterns in gpgme-tools's KEYLIST command.
GPG_ERR_LIMIT_REACHED Limit reached
A programmed limit has been reached.
GnuPG: gpgtar: Extract directory can't be created because too
many of directories with a similar name are already existing.
GPG_ERR_NOT_INITIALIZED Not initialized
An operation can't be performed because something has not been
initialized. This might be a missing initialization of an entire
subsystems or a prerequisite for using a function is not
fulfilled.
GPG_ERR_MISSING_ISSUER_CERT Missing issuer certificate
GPG_ERR_NO_KEYSERVER No keyserver available
No keyserver configured or no keyserver available due to
missing support for the requested protocol. Found in Dirmngr.
GPG_ERR_INV_CURVE Invalid elliptic curve
The curve parameter is missing or the curve is invalid; for
example it is not possible to get affine coordinates for the
public key.
GPG_ERR_UNKNOWN_CURVE Unknown elliptic curve
The curve is not known or not supported by the protocol.
GPG_ERR_DUP_KEY Duplicated key
A duplicated key was detected. For example a unique key in a
database occurred more than once. Also used if in a protocol
an expected key was returned more than once.
GPG_ERR_AMBIGUOUS Ambiguous search
A search etc returned an ambigious result. This usually means
that the search string was not specific enough.
GPG_ERR_NO_CRYPT_CTX No crypto context
A crypto context was expected but not given. Commonly used by
Libgcrypt.
GPG_ERR_WRONG_CRYPT_CTX Wrong crypto context
The given crypto context does not match the requirements. For
example in Libgcrypt a crypto context has private data
pertaining to certain algorithms. This error is for example
returned if a crypto context initialized for a different
algorithm is used.
GPG_ERR_BAD_CRYPT_CTX Bad crypto context
The is a problem with the crypto context. For example it has
not been properly initialized.
GPG_ERR_CRYPT_CTX_CONFLICT Conflict in the crypto context
Conflicting use of a crypto context. For example if a context
is used with objects that don't match the state of the
context.
GPG_ERR_BROKEN_PUBKEY Broken public key
The public key was mathematically not correctly generated.
(It would have been nicer if we would have used BAD_PUBKEY for
this, but that error code is in long time use to describe for
example policy and encoding problems with a key. Using
INV_PUBKEY would have been better for these purposes)
GPG_ERR_BROKEN_SECKEY Broken secret key
The secret key was mathematically not correctly generated.
GPG_ERR_MAC_ALGO
GCRYPT: - MAC key algorithm is not defined or not available.
GPG_ERR_FULLY_CANCELED Operation fully cancelled
GPG_ERR_UNFINISHED Operation not yet finished
GPG_ERR_BUFFER_TOO_SHORT Buffer too short
GPG_ERR_SEXP_INV_LEN_SPEC Invalid length specifier in S-expression
GPG_ERR_SEXP_STRING_TOO_LONG String too long in S-expression
GPG_ERR_SEXP_UNMATCHED_PAREN Unmatched parentheses in S-expression
GPG_ERR_SEXP_NOT_CANONICAL S-expression not canonical
GPG_ERR_SEXP_BAD_CHARACTER Bad character in S-expression
GPG_ERR_SEXP_BAD_QUOTATION Bad quotation in S-expression
GPG_ERR_SEXP_ZERO_PREFIX Zero prefix in S-expression
GPG_ERR_SEXP_NESTED_DH Nested display hints in S-expression
GPG_ERR_SEXP_UNMATCHED_DH Unmatched display hints
GPG_ERR_SEXP_UNEXPECTED_PUNC Unexpected reserved punctuation in S-expression
GPG_ERR_SEXP_BAD_HEX_CHAR Bad hexadecimal character in S-expression
GPG_ERR_SEXP_ODD_HEX_NUMBERS Odd hexadecimal numbers in S-expression
GPG_ERR_SEXP_BAD_OCT_CHAR Bad octal character in S-expression
GPG_ERR_SUBKEYS_EXP_REV All subkeys are expired or revoked
GPG_ERR_DB_CORRUPTED Database is corrupted
GPG_ERR_SERVER_FAILED Server indicated a failure
GPG_ERR_NO_NAME No name
GNUPG: - No component given in gpgconf runs.
- A field name is missing in an import/export filter.
- "Domain not found".
- "Host not found".
- Host or service name not found (EAI_NONAME).
- No or erroneous SRV record.
GPG_ERR_NO_KEY No key
Some kind of key was not found.
GPG_ERR_LEGACY_KEY Legacy key
Used by GnuPG to identify version 2 and 3 OpenPGP key packets.
GPG_ERR_REQUEST_TOO_SHORT Request too short
A received request is too short to continue processing.
GPG_ERR_REQUEST_TOO_LONG Request too long
A received request is too long to continue processing. This may
be due to an internal limitation, a protocol violation, or due to
the use of a newer version of a protocol.
GPG_ERR_OBJ_TERM_STATE Object is in termination state
For cards this is the ISO status word 0x6285 (file is in
termination state).
GPG_ERR_NO_CERT_CHAIN No certificate chain
NTBTLS: - A CA chain has not been set but is required.
GPG_ERR_CERT_TOO_LARGE Certificate is too large
NTBTLS: - A certificate is too large to be used by the protocol.
GPG_ERR_INV_RECORD Invalid record
NTBTLS: - An invalid record was received
GPG_ERR_BAD_MAC The MAC does not verify
NTBTLS: - MAC verification of the message failed.
GPG_ERR_UNEXPECTED_MSG Unexpected message
GNUPG: - An unexpected WKS message was received.
NTBTLS: - Unexpected message received.
GPG_ERR_COMPR_FAILED Compression or decompression failed
NTBTLS: - As the description says.
GPG_ERR_WOULD_WRAP A counter would wrap
NTBTLS: - Too many messages exchanged
Other: - A counter would wrap.
GPG_ERR_FATAL_ALERT Fatal alert message received
NTBTLS: - Fatal alert message received from the peer.
GPG_ERR_NO_CIPHER No cipher algorithm
NTBTLS: - Server and client have no algo in common
GPG_ERR_MISSING_CLIENT_CERT Missing client certificate
NTBTLS: - No certificate received from client.
GPG_ERR_CLOSE_NOTIFY Close notification received
NTBTLS: - Alert with a close notification received
GPG_ERR_TICKET_EXPIRED Ticket expired
NTBTLS: - Session ticket has expired.
GPG_ERR_BAD_TICKET Bad ticket
NTBTLS: - Bad new session ticket message.
GPG_ERR_UNKNOWN_IDENTITY Unknown identity
NTBTLS: - Unknown PSK identify received
GPG_ERR_BAD_HS_CERT Bad certificate message in handshake
NTBTLS: - As the description says.
GPG_ERR_BAD_HS_CERT_REQ Bad certificate request message in handshake
NTBTLS: - As the description says.
GPG_ERR_BAD_HS_CERT_VER Bad certificate verify message in handshake
NTBTLS: - As the description says.
GPG_ERR_BAD_HS_CHANGE_CIPHER Bad change cipher message in handshake
NTBTLS: - As the description says.
GPG_ERR_BAD_HS_CLIENT_HELLO Bad client hello message in handshake
NTBTLS: - As the description says.
GPG_ERR_BAD_HS_SERVER_HELLO Bad server hello message in handshake
NTBTLS: - As the description says.
GPG_ERR_BAD_HS_SERVER_HELLO_DONE Bad server hello done message in handshake
NTBTLS: - As the description says.
GPG_ERR_BAD_HS_FINISHED Bad finished message in handshake
NTBTLS: - As the description says.
GPG_ERR_BAD_HS_SERVER_KEX Bad server key exchange message in handshake
NTBTLS: - As the description says.
GPG_ERR_BAD_HS_CLIENT_KEX Bad client key exchange message in handshake
NTBTLS: - As the description says.
GPG_ERR_BOGUS_STRING Bogus string
Used if a protocol sends length prefixed strings which contain a
Nul byte and further processing would discard the rest of the
string. May also be used if a string contains unexpected and
possible dangerous characters (e.g. control characters in a domain
name).
GPG_ERR_FORBIDDEN Forbidden
The use of a features is not allowed due to insufficient rights.
Use by gpg-agent as an error codes for restricted commands.
GPG_ERR_KEY_DISABLED Key disabled
GNUPG: - The key has been disabled by the user.
GPG_ERR_KEY_ON_CARD Not possible with a card based key
GNUPG: - The gpg-agent returns this if a DELETE_KEY commands is
used for a smartcard based key.
GPG_ERR_INV_LOCK_OBJ Invalid lock object
GPGRT: - The provided lock object is not valid. This indicates an
internal problem in libgpg-error or more likely a
programming error.
GPG_ERR_TRUE True
Used to return the boolean value True. Note that GPG_ERR_NO_ERROR
(with the value 0) is also often used to indicate the value true.
GPG_ERR_FALSE False
Used to return the boolean value False.
GPG_ERR_ASS_GENERAL General IPC error
GPG_ERR_ASS_ACCEPT_FAILED IPC accept call failed
GPG_ERR_ASS_CONNECT_FAILED IPC connect call failed
GPG_ERR_ASS_INV_RESPONSE Invalid IPC response
GPG_ERR_ASS_INV_VALUE Invalid value passed to IPC
GPG_ERR_ASS_INCOMPLETE_LINE Incomplete line passed to IPC
GPG_ERR_ASS_LINE_TOO_LONG Line passed to IPC too long
GPG_ERR_ASS_NESTED_COMMANDS Nested IPC commands
GPG_ERR_ASS_NO_DATA_CB No data callback in IPC
GPG_ERR_ASS_NO_INQUIRE_CB No inquire callback in IPC
GPG_ERR_ASS_NOT_A_SERVER Not an IPC server
GPG_ERR_ASS_NOT_A_CLIENT Not an IPC client
GPG_ERR_ASS_SERVER_START Problem starting IPC server
GPG_ERR_ASS_READ_ERROR IPC read error
GPG_ERR_ASS_WRITE_ERROR IPC write error
GPG_ERR_ASS_TOO_MUCH_DATA Too much data for IPC layer
GPG_ERR_ASS_UNEXPECTED_CMD Unexpected IPC command
GPG_ERR_ASS_UNKNOWN_CMD Unknown IPC command
GPG_ERR_ASS_SYNTAX IPC syntax error
GPG_ERR_ASS_CANCELED IPC call has been cancelled
GPG_ERR_ASS_NO_INPUT No input source for IPC
GPG_ERR_ASS_NO_OUTPUT No output source for IPC
GPG_ERR_ASS_PARAMETER IPC parameter error
GPG_ERR_ASS_UNKNOWN_INQUIRE Unknown IPC inquire
GPG_ERR_ENGINE_TOO_OLD Crypto engine too old
GPG_ERR_WINDOW_TOO_SMALL Screen or window too small
Pinentry: - The size of the screen is too small.
GPG_ERR_WINDOW_TOO_LARGE Screen or window too large
GPG_ERR_MISSING_ENVVAR Required environment variable not set
Pinentry: - The size of the screen can't be determined.
GPG_ERR_USER_ID_EXISTS User ID already exists
GNUPG: - Existing user ID in --quick-gen-key.
GPG_ERR_NAME_EXISTS Name already exists
GPG_ERR_DUP_NAME Duplicated name
GPG_ERR_TOO_YOUNG Objects is too young
For example used if a file is younger than expected.
GPG_ERR_TOO_OLD Objects is too old
Used if an object is too old to be used. This is a more generic
code than GPG_ERR_ENGINE_TOO_OLD or GPG_ERR_CRL_TOO_OLD.
GPG_ERR_UNKNOWN_FLAG Unknown flag
The flag is not known.
GNUPG: - The flag part of the string given to the
option --default-new-key-algo value is not known.
GPG_ERR_INV_ORDER Invalid execution order
GNUPG: - In Dirmngr used for the libdns error code DNS_EORDER.
GPG_ERR_ALREADY_FETCHED Already fetched
GNUPG: - In Dirmngr used for the libdns error code DNS_EFETCHED.
GPG_ERR_TRY_LATER Try again later
This indicates that a server asked to try again later; thus it is
different from EAGAIN which is used by the local system. This
code is for example used instead of h_error's TRY_AGAIN.
GPG_ERR_WRONG_NAME Wrong name
NTBTLS: - Hostname does not match the certificate
GPG_ERR_NO_AUTH Not authenticated
GnuPG: - A smartcard requires authentication
GPG_ERR_BAD_AUTH Bad authentication
GnuPG: - A smartcard could not be authenticated. For example
a wrong authentication key was used with a PIV card.
GPG_ERR_SYSTEM_BUG System bug detected
The underlying operating system misbehaved. For example it wrote
more to a buffer than the told maximum size.
GPG_ERR_DNS_UNKNOWN Unknown DNS error
Used by Dirmngr for DNS errors from libdns (DNS_EUNKNOWN);
GPG_ERR_DNS_SECTION Invalid DNS section
Used by Dirmngr for DNS errors from libdns (DNS_ESECTION);
GPG_ERR_DNS_ADDRESS Invalid textual address form
Used by Dirmngr for DNS errors from libdns (DNS_EADDRESS);
GPG_ERR_DNS_NO_QUERY Missing DNS query packet
Used by Dirmngr for DNS errors from libdns (DNS_ENOQUERY);
GPG_ERR_DNS_NO_ANSWER Missing DNS answer packet
Used by Dirmngr for DNS errors from libdns (DNS_ENOANSWER);
GPG_ERR_DNS_CLOSED Connection closed in DNS
Used by Dirmngr for DNS errors from libdns (DNS_ECONNFIN);
GPG_ERR_DNS_VERIFY Verification failed in DNS
Used by Dirmngr for DNS errors from libdns (DNS_EVERIFY);
GPG_ERR_DNS_TIMEOUT DNS Timeout
A DNS query timed out
GPG_ERR_LDAP_GENERAL LDAP General error
Catch all error for LDAP. Use when an error code could not be
mapped to a gpg-error code.
GPG_ERR_LDAP_ATTR_GENERAL LDAP General attribute error
GPG_ERR_LDAP_NAME_GENERAL LDAP General name error
GPG_ERR_LDAP_SECURITY_GENERAL LDAP General security error
GPG_ERR_LDAP_SERVICE_GENERAL LDAP General service error
GPG_ERR_LDAP_UPDATE_GENERAL LDAP General update error
GPG_ERR_LDAP_E_GENERAL LDAP Experimental error code
GPG_ERR_LDAP_X_GENERAL LDAP Private error code
GPG_ERR_LDAP_OTHER_GENERAL LDAP Other general error
The 8 GPG_ERR_LDAP_*_GENERAL error codes may be used to map ranges
of LDAP errors to one specific code. OpenLDAP uses LDAP_xxx_RANGE(n)
macros for that mapping. "Other general error" may be used similar
to "General error" for mapping of ranges. Here are macros from
OpenLDAP for reference:
#define LDAP_ATTR_ERROR(n) LDAP_RANGE((n),0x10,0x15) /* 16-21 */
#define LDAP_NAME_ERROR(n) LDAP_RANGE((n),0x20,0x24) /* 32-34,36 */
#define LDAP_SECURITY_ERROR(n) LDAP_RANGE((n),0x2F,0x32) /* 47-50 */
#define LDAP_SERVICE_ERROR(n) LDAP_RANGE((n),0x33,0x36) /* 51-54 */
#define LDAP_UPDATE_ERROR(n) LDAP_RANGE((n),0x40,0x47) /* 64-69,71 */
#define LDAP_E_ERROR(n) LDAP_RANGE((n),0x1000,0x3FFF)
#define LDAP_X_ERROR(n) LDAP_RANGE((n),0x4000,0xFFFF)
## end of errorref.txt
diff --git a/src/gpgrt-int.h b/src/gpgrt-int.h
index c6188e1..a13f5fb 100644
--- a/src/gpgrt-int.h
+++ b/src/gpgrt-int.h
@@ -1,814 +1,820 @@
/* gpgrt-int.h - Internal definitions
* Copyright (C) 2014, 2017 g10 Code GmbH
*
* This file is part of libgpg-error.
*
* libgpg-error is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* libgpg-error is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see .
* SPDX-License-Identifier: LGPL-2.1+
*/
#ifndef _GPGRT_GPGRT_INT_H
#define _GPGRT_GPGRT_INT_H
#include "gpg-error.h"
#include "visibility.h"
/*
* Internal i18n macros.
*/
#ifdef ENABLE_NLS
# ifdef HAVE_W32_SYSTEM
# include "gettext.h"
# else
# include
# endif
# define _(a) gettext (a)
# ifdef gettext_noop
# define N_(a) gettext_noop (a)
# else
# define N_(a) (a)
# endif
#else /*!ENABLE_NLS*/
# define _(a) (a)
# define N_(a) (a)
#endif /*!ENABLE_NLS */
/*
* Hacks mainly required for Slowaris.
*/
#ifdef _GPGRT_NEED_AFLOCAL
# ifndef HAVE_W32_SYSTEM
# include
# include
# else
# ifdef HAVE_WINSOCK2_H
# include
# endif
# include
# endif
# ifndef PF_LOCAL
# ifdef PF_UNIX
# define PF_LOCAL PF_UNIX
# else
# define PF_LOCAL AF_UNIX
# endif
# endif /*PF_LOCAL*/
# ifndef AF_LOCAL
# define AF_LOCAL AF_UNIX
# endif /*AF_UNIX*/
/* We used to avoid this macro in GnuPG and inlined the AF_LOCAL name
* length computation directly with the little twist of adding 1 extra
* byte. It seems that this was needed once on an old HP/UX box and
* there are also rumours that 4.3 Reno and DEC systems need it. This
* one-off buglet did not harm any current system until it came to Mac
* OS X where the kernel (as of May 2009) exhibited a strange bug: The
* systems basically froze in the connect call if the passed name
* contained an invalid directory part. Ignore the old Unices. */
# ifndef SUN_LEN
# define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
+ strlen ((ptr)->sun_path))
# endif /*SUN_LEN*/
#endif /*_GPGRT_NEED_AFLOCAL*/
/*
* Common helper macros.
*/
#ifndef DIM
# define DIM(array) (sizeof (array) / sizeof (*array))
#endif
/*
* Local error function prototypes.
*/
const char *_gpg_strerror (gpg_error_t err);
int _gpg_strerror_r (gpg_error_t err, char *buf, size_t buflen);
const char *_gpg_strsource (gpg_error_t err);
gpg_err_code_t _gpg_err_code_from_errno (int err);
int _gpg_err_code_to_errno (gpg_err_code_t code);
gpg_err_code_t _gpg_err_code_from_syserror (void);
void _gpg_err_set_errno (int err);
gpg_error_t _gpg_err_init (void);
void _gpg_err_deinit (int mode);
void _gpgrt_add_emergency_cleanup (void (*f)(void));
void _gpgrt_abort (void) GPGRT_ATTR_NORETURN;
void _gpgrt_set_alloc_func (void *(*f)(void *a, size_t n));
void *_gpgrt_realloc (void *a, size_t n);
void *_gpgrt_malloc (size_t n);
void *_gpgrt_calloc (size_t n, size_t m);
char *_gpgrt_strdup (const char *string);
char *_gpgrt_strconcat (const char *s1, ...) GPGRT_ATTR_SENTINEL(0);
void _gpgrt_free (void *a);
/* The next is only to be used by visibility.c. */
char *_gpgrt_strconcat_core (const char *s1, va_list arg_ptr);
#define xfree(a) _gpgrt_free ((a))
#define xtrymalloc(a) _gpgrt_malloc ((a))
#define xtrycalloc(a,b) _gpgrt_calloc ((a),(b))
#define xtryrealloc(a,b) _gpgrt_realloc ((a),(b))
#define xtrystrdup(a) _gpgrt_strdup ((a))
void _gpgrt_pre_syscall (void);
void _gpgrt_post_syscall (void);
const char *_gpg_error_check_version (const char *req_version);
gpg_err_code_t _gpgrt_lock_init (gpgrt_lock_t *lockhd);
gpg_err_code_t _gpgrt_lock_lock (gpgrt_lock_t *lockhd);
gpg_err_code_t _gpgrt_lock_trylock (gpgrt_lock_t *lockhd);
gpg_err_code_t _gpgrt_lock_unlock (gpgrt_lock_t *lockhd);
gpg_err_code_t _gpgrt_lock_destroy (gpgrt_lock_t *lockhd);
gpg_err_code_t _gpgrt_yield (void);
/*
* Tracing
*/
/* The trace macro is used this way:
* trace (("enter - foo=%d bar=%s", foo, bar));
* Note the double parenthesis, they are important.
* To append the current errno to the output, use
* trace_errno (EXTPR,("leave - baz=%d", faz));
* If EXPR evaluates to true the output of strerror (errno)
* is appended to the output. Note that the trace function does
* not modify ERRNO. To enable tracing you need to have this
* #define ENABLE_TRACING "modulename"
* before you include gpgrt-int.h.
*/
#ifdef ENABLE_TRACING
# define trace(X) do { \
_gpgrt_internal_trace_begin \
(ENABLE_TRACING, __func__, __LINE__, 0); \
_gpgrt_internal_trace X; \
_gpgrt_internal_trace_end (); \
} while (0)
# define trace_errno(C,X) do { \
_gpgrt_internal_trace_begin \
(ENABLE_TRACING, __func__, __LINE__, (C)); \
_gpgrt_internal_trace X; \
_gpgrt_internal_trace_end (); \
} while (0)
# define trace_start(X) do { \
_gpgrt_internal_trace_begin \
(ENABLE_TRACING, __func__, __LINE__, 0); \
_gpgrt_internal_trace_printf X; \
} while (0)
# define trace_append(X) do { \
_gpgrt_internal_trace_printf X; \
} while (0)
# define trace_finish(X) do { \
_gpgrt_internal_trace_printf X; \
_gpgrt_internal_trace_end (); \
} while (0)
#else
# define trace(X) do { } while (0)
# define trace_errno(C,X) do { } while (0)
# define trace_start(X) do { } while (0)
# define trace_append(X) do { } while (0)
# define trace_finish(X) do { } while (0)
#endif /*!ENABLE_TRACING*/
void _gpgrt_internal_trace_begin (const char *mod, const char *file, int line,
int with_errno);
void _gpgrt_internal_trace (const char *format,
...) GPGRT_ATTR_PRINTF(1,2);
void _gpgrt_internal_trace_printf (const char *format,
...) GPGRT_ATTR_PRINTF(1,2);
void _gpgrt_internal_trace_end (void);
/*
* Local definitions for estream.
*/
#if HAVE_W32_SYSTEM
# ifndef O_NONBLOCK
# define O_NONBLOCK 0x40000000 /* FIXME: Is that safe? */
# endif
#endif
/*
* A private cookie function to implement an internal IOCTL service.
*/
typedef int (*cookie_ioctl_function_t) (void *cookie, int cmd,
void *ptr, size_t *len);
#define COOKIE_IOCTL_SNATCH_BUFFER 1
#define COOKIE_IOCTL_NONBLOCK 2
#define COOKIE_IOCTL_TRUNCATE 3
/* An internal variant of gpgrt_cookie_close_function_t with a slot
* for the ioctl function. */
struct cookie_io_functions_s
{
struct _gpgrt_cookie_io_functions public;
cookie_ioctl_function_t func_ioctl;
};
typedef enum
{
BACKEND_MEM,
BACKEND_FD,
BACKEND_W32,
BACKEND_FP,
BACKEND_USER,
BACKEND_W32_POLLABLE
} gpgrt_stream_backend_kind_t;
/*
* A type to hold notification functions.
*/
struct notify_list_s
{
struct notify_list_s *next;
void (*fnc) (estream_t, void*); /* The notification function. */
void *fnc_value; /* The value to be passed to FNC. */
};
typedef struct notify_list_s *notify_list_t;
/*
* Buffer management layer.
*/
-#define BUFFER_BLOCK_SIZE BUFSIZ
+/* BUFSIZ on Windows is 512 but on current Linux it is 8k. We better
+ * use the 8k for Windows as well. */
+#ifdef HAVE_W32_SYSTEM
+# define BUFFER_BLOCK_SIZE 8192
+#else
+# define BUFFER_BLOCK_SIZE BUFSIZ
+#endif
#define BUFFER_UNREAD_SIZE 16
/*
* The private object describing a stream.
*/
struct _gpgrt_stream_internal
{
unsigned char buffer[BUFFER_BLOCK_SIZE];
unsigned char unread_buffer[BUFFER_UNREAD_SIZE];
gpgrt_lock_t lock; /* Lock. Used by *_stream_lock(). */
gpgrt_stream_backend_kind_t kind;
void *cookie; /* Cookie. */
void *opaque; /* Opaque data. */
unsigned int modeflags; /* Flags for the backend. */
char *printable_fname; /* Malloced filename for es_fname_get. */
gpgrt_off_t offset;
gpgrt_cookie_read_function_t func_read;
gpgrt_cookie_write_function_t func_write;
gpgrt_cookie_seek_function_t func_seek;
gpgrt_cookie_close_function_t func_close;
cookie_ioctl_function_t func_ioctl;
int strategy;
es_syshd_t syshd; /* A copy of the system handle. */
struct
{
unsigned int err: 1;
unsigned int eof: 1;
unsigned int hup: 1;
} indicators;
unsigned int deallocate_buffer: 1;
unsigned int is_stdstream:1; /* This is a standard stream. */
unsigned int stdstream_fd:2; /* 0, 1 or 2 for a standard stream. */
unsigned int printable_fname_inuse: 1; /* es_fname_get has been used. */
unsigned int samethread: 1; /* The "samethread" mode keyword. */
size_t print_ntotal; /* Bytes written from in print_writer. */
notify_list_t onclose; /* On close notify function list. */
};
typedef struct _gpgrt_stream_internal *estream_internal_t;
/*
* Local prototypes for estream.
*/
int _gpgrt_estream_init (void);
void _gpgrt_set_syscall_clamp (void (*pre)(void), void (*post)(void));
void _gpgrt_get_syscall_clamp (void (**r_pre)(void), void (**r_post)(void));
gpgrt_stream_t _gpgrt_fopen (const char *_GPGRT__RESTRICT path,
const char *_GPGRT__RESTRICT mode);
gpgrt_stream_t _gpgrt_mopen (void *_GPGRT__RESTRICT data,
size_t data_n, size_t data_len,
unsigned int grow,
void *(*func_realloc) (void *mem, size_t size),
void (*func_free) (void *mem),
const char *_GPGRT__RESTRICT mode);
gpgrt_stream_t _gpgrt_fopenmem (size_t memlimit,
const char *_GPGRT__RESTRICT mode);
gpgrt_stream_t _gpgrt_fopenmem_init (size_t memlimit,
const char *_GPGRT__RESTRICT mode,
const void *data, size_t datalen);
gpgrt_stream_t _gpgrt_fdopen (int filedes, const char *mode);
gpgrt_stream_t _gpgrt_fdopen_nc (int filedes, const char *mode);
gpgrt_stream_t _gpgrt_sysopen (gpgrt_syshd_t *syshd, const char *mode);
gpgrt_stream_t _gpgrt_sysopen_nc (gpgrt_syshd_t *syshd, const char *mode);
gpgrt_stream_t _gpgrt_fpopen (FILE *fp, const char *mode);
gpgrt_stream_t _gpgrt_fpopen_nc (FILE *fp, const char *mode);
gpgrt_stream_t _gpgrt_freopen (const char *_GPGRT__RESTRICT path,
const char *_GPGRT__RESTRICT mode,
gpgrt_stream_t _GPGRT__RESTRICT stream);
gpgrt_stream_t _gpgrt_fopencookie (void *_GPGRT__RESTRICT cookie,
const char *_GPGRT__RESTRICT mode,
gpgrt_cookie_io_functions_t functions);
int _gpgrt_fclose (gpgrt_stream_t stream);
int _gpgrt_fclose_snatch (gpgrt_stream_t stream,
void **r_buffer, size_t *r_buflen);
int _gpgrt_onclose (gpgrt_stream_t stream, int mode,
void (*fnc) (gpgrt_stream_t, void*), void *fnc_value);
int _gpgrt_fileno (gpgrt_stream_t stream);
int _gpgrt_fileno_unlocked (gpgrt_stream_t stream);
int _gpgrt_syshd (gpgrt_stream_t stream, gpgrt_syshd_t *syshd);
int _gpgrt_syshd_unlocked (gpgrt_stream_t stream, gpgrt_syshd_t *syshd);
void _gpgrt__set_std_fd (int no, int fd);
gpgrt_stream_t _gpgrt__get_std_stream (int fd);
/* The es_stderr et al macros are pretty common so that we want to use
* them too. This requires that we redefine them. */
#undef es_stdin
#define es_stdin _gpgrt__get_std_stream (0)
#undef es_stdout
#define es_stdout _gpgrt__get_std_stream (1)
#undef es_stderr
#define es_stderr _gpgrt__get_std_stream (2)
void _gpgrt_flockfile (gpgrt_stream_t stream);
int _gpgrt_ftrylockfile (gpgrt_stream_t stream);
void _gpgrt_funlockfile (gpgrt_stream_t stream);
int _gpgrt_feof (gpgrt_stream_t stream);
int _gpgrt_feof_unlocked (gpgrt_stream_t stream);
int _gpgrt_ferror (gpgrt_stream_t stream);
int _gpgrt_ferror_unlocked (gpgrt_stream_t stream);
void _gpgrt_clearerr (gpgrt_stream_t stream);
void _gpgrt_clearerr_unlocked (gpgrt_stream_t stream);
int _gpgrt__pending (gpgrt_stream_t stream);
int _gpgrt__pending_unlocked (gpgrt_stream_t stream);
int _gpgrt_fflush (gpgrt_stream_t stream);
int _gpgrt_fseek (gpgrt_stream_t stream, long int offset, int whence);
int _gpgrt_fseeko (gpgrt_stream_t stream, gpgrt_off_t offset, int whence);
long int _gpgrt_ftell (gpgrt_stream_t stream);
gpgrt_off_t _gpgrt_ftello (gpgrt_stream_t stream);
void _gpgrt_rewind (gpgrt_stream_t stream);
int _gpgrt_ftruncate (estream_t stream, gpgrt_off_t length);
int _gpgrt_fgetc (gpgrt_stream_t stream);
int _gpgrt_fputc (int c, gpgrt_stream_t stream);
int _gpgrt__getc_underflow (gpgrt_stream_t stream);
int _gpgrt__putc_overflow (int c, gpgrt_stream_t stream);
/* Note: Keeps the next two macros in sync
with their counterparts in gpg-error.h. */
#define _gpgrt_getc_unlocked(stream) \
(((!(stream)->flags.writing) \
&& ((stream)->data_offset < (stream)->data_len) \
&& (! (stream)->unread_data_len)) \
? ((int) (stream)->buffer[((stream)->data_offset)++]) \
: _gpgrt__getc_underflow ((stream)))
#define _gpgrt_putc_unlocked(c, stream) \
(((stream)->flags.writing \
&& ((stream)->data_offset < (stream)->buffer_size) \
&& (c != '\n')) \
? ((int) ((stream)->buffer[((stream)->data_offset)++] = (c))) \
: _gpgrt__putc_overflow ((c), (stream)))
int _gpgrt_ungetc (int c, gpgrt_stream_t stream);
int _gpgrt_read (gpgrt_stream_t _GPGRT__RESTRICT stream,
void *_GPGRT__RESTRICT buffer, size_t bytes_to_read,
size_t *_GPGRT__RESTRICT bytes_read);
int _gpgrt_write (gpgrt_stream_t _GPGRT__RESTRICT stream,
const void *_GPGRT__RESTRICT buffer, size_t bytes_to_write,
size_t *_GPGRT__RESTRICT bytes_written);
int _gpgrt_write_sanitized (gpgrt_stream_t _GPGRT__RESTRICT stream,
const void *_GPGRT__RESTRICT buffer, size_t length,
const char *delimiters,
size_t *_GPGRT__RESTRICT bytes_written);
int _gpgrt_write_hexstring (gpgrt_stream_t _GPGRT__RESTRICT stream,
const void *_GPGRT__RESTRICT buffer, size_t length,
int reserved,
size_t *_GPGRT__RESTRICT bytes_written);
size_t _gpgrt_fread (void *_GPGRT__RESTRICT ptr, size_t size, size_t nitems,
gpgrt_stream_t _GPGRT__RESTRICT stream);
size_t _gpgrt_fwrite (const void *_GPGRT__RESTRICT ptr,
size_t size, size_t memb,
gpgrt_stream_t _GPGRT__RESTRICT stream);
char *_gpgrt_fgets (char *_GPGRT__RESTRICT s, int n,
gpgrt_stream_t _GPGRT__RESTRICT stream);
int _gpgrt_fputs (const char *_GPGRT__RESTRICT s,
gpgrt_stream_t _GPGRT__RESTRICT stream);
int _gpgrt_fputs_unlocked (const char *_GPGRT__RESTRICT s,
gpgrt_stream_t _GPGRT__RESTRICT stream);
gpgrt_ssize_t _gpgrt_getline (char *_GPGRT__RESTRICT *_GPGRT__RESTRICT lineptr,
size_t *_GPGRT__RESTRICT n,
gpgrt_stream_t stream);
gpgrt_ssize_t _gpgrt_read_line (gpgrt_stream_t stream,
char **addr_of_buffer, size_t *length_of_buffer,
size_t *max_length);
int _gpgrt_fprintf (gpgrt_stream_t _GPGRT__RESTRICT stream,
const char *_GPGRT__RESTRICT format, ...)
GPGRT_ATTR_PRINTF(2,3);
int _gpgrt_fprintf_unlocked (gpgrt_stream_t _GPGRT__RESTRICT stream,
const char *_GPGRT__RESTRICT format, ...)
GPGRT_ATTR_PRINTF(2,3);
int _gpgrt_vfprintf (gpgrt_stream_t _GPGRT__RESTRICT stream,
gpgrt_string_filter_t sf, void *sfvalue,
const char *_GPGRT__RESTRICT format, va_list ap)
GPGRT_ATTR_PRINTF(4,0);
int _gpgrt_vfprintf_unlocked (gpgrt_stream_t _GPGRT__RESTRICT stream,
gpgrt_string_filter_t sf, void *sfvalue,
const char *_GPGRT__RESTRICT format, va_list ap)
GPGRT_ATTR_PRINTF(4,0);
int _gpgrt_setvbuf (gpgrt_stream_t _GPGRT__RESTRICT stream,
char *_GPGRT__RESTRICT buf, int mode, size_t size);
void _gpgrt_set_binary (gpgrt_stream_t stream);
int _gpgrt_set_nonblock (gpgrt_stream_t stream, int onoff);
int _gpgrt_get_nonblock (gpgrt_stream_t stream);
int _gpgrt_poll (gpgrt_poll_t *fds, unsigned int nfds, int timeout);
gpgrt_stream_t _gpgrt_tmpfile (void);
void _gpgrt_opaque_set (gpgrt_stream_t _GPGRT__RESTRICT stream,
void *_GPGRT__RESTRICT opaque);
void *_gpgrt_opaque_get (gpgrt_stream_t stream);
void _gpgrt_fname_set (gpgrt_stream_t stream, const char *fname);
const char *_gpgrt_fname_get (gpgrt_stream_t stream);
#include "estream-printf.h"
/* Make sure we always use our snprintf */
#undef snprintf
#define snprintf _gpgrt_estream_snprintf
#if HAVE_W32_SYSTEM
/* Prototypes for w32-estream.c. */
extern struct cookie_io_functions_s _gpgrt_functions_w32_pollable;
int _gpgrt_w32_pollable_create (void *_GPGRT__RESTRICT *_GPGRT__RESTRICT cookie,
unsigned int modeflags,
struct cookie_io_functions_s next_functions,
void *next_cookie);
int _gpgrt_w32_poll (gpgrt_poll_t *fds, size_t nfds, int timeout);
#endif /*HAVE_W32_SYSTEM*/
/*
* Local prototypes for the encoders.
*/
struct _gpgrt_b64state
{
int idx;
int quad_count;
estream_t stream;
char *title;
unsigned char radbuf[4];
unsigned int crc;
gpg_err_code_t lasterr;
unsigned int flags;
int stop_seen:1;
int invalid_encoding:1;
int using_decoder:1;
};
gpgrt_b64state_t _gpgrt_b64enc_start (estream_t stream, const char *title);
gpg_err_code_t _gpgrt_b64enc_write (gpgrt_b64state_t state,
const void *buffer, size_t nbytes);
gpg_err_code_t _gpgrt_b64enc_finish (gpgrt_b64state_t state);
gpgrt_b64state_t _gpgrt_b64dec_start (const char *title);
gpg_err_code_t _gpgrt_b64dec_proc (gpgrt_b64state_t state, void *buffer,
size_t length, size_t *r_nbytes);
gpg_err_code_t _gpgrt_b64dec_finish (gpgrt_b64state_t state);
/*
* Local prototypes for logging
*/
int _gpgrt_get_errorcount (int clear);
void _gpgrt_inc_errorcount (void);
void _gpgrt_log_set_sink (const char *name, estream_t stream, int fd);
void _gpgrt_log_set_socket_dir_cb (const char *(*fnc)(void));
void _gpgrt_log_set_pid_suffix_cb (int (*cb)(unsigned long *r_value));
void _gpgrt_log_set_prefix (const char *text, unsigned int flags);
const char *_gpgrt_log_get_prefix (unsigned int *flags);
int _gpgrt_log_test_fd (int fd);
int _gpgrt_log_get_fd (void);
estream_t _gpgrt_log_get_stream (void);
void _gpgrt_log (int level, const char *fmt, ...) GPGRT_ATTR_PRINTF(2,3);
void _gpgrt_logv (int level, const char *fmt, va_list arg_ptr);
void _gpgrt_logv_prefix (int level, const char *prefix,
const char *fmt, va_list arg_ptr);
void _gpgrt_log_string (int level, const char *string);
void _gpgrt_log_bug (const char *fmt, ...) GPGRT_ATTR_NR_PRINTF(1,2);
void _gpgrt_log_fatal (const char *fmt, ...) GPGRT_ATTR_NR_PRINTF(1,2);
void _gpgrt_log_error (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
void _gpgrt_log_info (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
void _gpgrt_log_debug (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
void _gpgrt_log_debug_string (const char *string, const char *fmt,
...) GPGRT_ATTR_PRINTF(2,3);
void _gpgrt_log_printf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
void _gpgrt_log_flush (void);
void _gpgrt_logv_printhex (const void *buffer, size_t length,
const char *fmt, va_list arg_ptr);
void _gpgrt_log_printhex (const void *buffer, size_t length,
const char *fmt, ...) GPGRT_ATTR_PRINTF(3,4);;
void _gpgrt_logv_clock (const char *fmt, va_list arg_ptr);
void _gpgrt_log_clock (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
void _gpgrt__log_assert (const char *expr, const char *file, int line,
const char *func) GPGRT_ATTR_NORETURN;
/* Redefine the assert macro to use our internal function. */
#undef gpgrt_assert
#ifdef GPGRT_HAVE_MACRO_FUNCTION
#define gpgrt_assert(expr) \
((expr) \
? (void) 0 \
: _gpgrt__log_assert (#expr, __FILE__, __LINE__, __FUNCTION__))
#else /*!GPGRT_HAVE_MACRO_FUNCTION*/
/* # define BUG() bug_at( __FILE__ , __LINE__ ) */
#define gpgrt_assert(expr) \
((expr) \
? (void) 0 \
: _gpgrt__log_assert (#expr, __FILE__, __LINE__, NULL))
#endif /*!GPGRT_HAVE_MACRO_FUNCTION*/
/* Note: The next function is only to be used by visibility.c. */
int _gpgrt_logv_internal (int level, int ignore_arg_ptr,
const char *extrastring,
const char *prefmt, const char *fmt,
va_list arg_ptr);
/*
* Local prototypes for the spawn functions.
*
* We put the docs here because we have separate implementations in
* the files spawn-posix.c and spawn-w32.c
*/
/* Return the maximum number of currently allowed file descriptors.
* Only useful on POSIX systems. */
/* int get_max_fds (void); */
/* Close all file descriptors starting with descriptor FIRST. If
* EXCEPT is not NULL, it is expected to be a list of file descriptors
* which are not to close. This list shall be sorted in ascending
* order with its end marked by -1. */
/* void close_all_fds (int first, int *except); */
/* Returns an array with all currently open file descriptors. The end
* of the array is marked by -1. The caller needs to release this
* array using the *standard free* and not with xfree. This allow the
* use of this function right at startup even before libgcrypt has
* been initialized. Returns NULL on error and sets ERRNO accordingly. */
/* int *get_all_open_fds (void); */
/* Create a pipe. The DIRECTION parameter gives the type of the created pipe:
* DIRECTION < 0 := Inbound pipe: On Windows the write end is inheritable.
* DIRECTION > 0 := Outbound pipe: On Windows the read end is inheritable.
* If R_FP is NULL a standard pipe and no stream is created, DIRECTION
* should then be 0. */
gpg_err_code_t _gpgrt_make_pipe (int filedes[2], estream_t *r_fp,
int direction, int nonblock);
/* Convenience macros to create a pipe. */
#define _gpgrt_create_pipe(a) _gpgrt_make_pipe ((a),NULL, 0, 0);
#define _gpgrt_create_inbound_pipe(a,b,c) _gpgrt_make_pipe ((a), (b), -1, (c));
#define _gpgrt_create_outbound_pipe(a,b,c) _gpgrt_make_pipe ((a), (b), 1, (c));
/* Fork and exec the program PGMNAME.
*
* If R_INFP is NULL connect stdin of the new process to /dev/null; if
* it is not NULL store the address of a pointer to a new estream
* there. If R_OUTFP is NULL connect stdout of the new process to
* /dev/null; if it is not NULL store the address of a pointer to a
* new estream there. If R_ERRFP is NULL connect stderr of the new
* process to /dev/null; if it is not NULL store the address of a
* pointer to a new estream there. On success the pid of the new
* process is stored at PID. On error -1 is stored at PID and if
* R_OUTFP or R_ERRFP are not NULL, NULL is stored there.
*
* The arguments for the process are expected in the NULL terminated
* array ARGV. The program name itself should not be included there.
* If PREEXEC is not NULL, the given function will be called right
* before the exec.
*
* IF EXCEPT is not NULL, it is expected to be an ordered list of file
* descriptors, terminated by an entry with the value (-1). These
* file descriptors won't be closed before spawning a new program.
*
* Returns 0 on success or an error code. Calling gpgrt_wait_process
* and gpgrt_release_process is required if the function succeeded.
*
* FLAGS is a bit vector:
*
* GPGRT_SPAWN_NONBLOCK
* If set the two output streams are created in non-blocking
* mode and the input stream is switched to non-blocking mode.
* This is merely a convenience feature because the caller
* could do the same with gpgrt_set_nonblock. Does not yet
* work for Windows.
*
* GPGRT_SPAWN_DETACHED
* If set the process will be started as a background process.
* This flag is only useful under W32 (but not W32CE) systems,
* so that no new console is created and pops up a console
* window when starting the server. Does not work on W32CE.
*
* GPGRT_SPAWN_RUN_ASFW
* On W32 (but not on W32CE) run AllowSetForegroundWindow for
* the child. Note that due to unknown problems this actually
* allows SetForegroundWindow for all children of this process.
*/
gpg_err_code_t
_gpgrt_spawn_process (const char *pgmname, const char *argv[],
int *execpt, void (*preexec)(void), unsigned int flags,
estream_t *r_infp,
estream_t *r_outfp,
estream_t *r_errfp,
pid_t *pid);
/* Simplified version of gpgrt_spawn_process. This function forks and
* then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
* and ERRFD to stderr (any of them may be -1 to connect them to
* /dev/null). The arguments for the process are expected in the NULL
* terminated array ARGV. The program name itself should not be
* included there. Calling gpgrt_wait_process and
* gpgrt_release_process is required. Returns 0 on success or an
* error code. */
gpg_err_code_t _gpgrt_spawn_process_fd (const char *pgmname,
const char *argv[],
int infd, int outfd, int errfd,
pid_t *pid);
/* Spawn a new process and immediately detach from it. The name of
* the program to exec is PGMNAME and its arguments are in ARGV (the
* programname is automatically passed as first argument).
* Environment strings in ENVP are set. An error is returned if
* pgmname is not executable; to make this work it is necessary to
* provide an absolute file name. */
gpg_err_code_t _gpgrt_spawn_process_detached (const char *pgmname,
const char *argv[],
const char *envp[] );
/* If HANG is true, waits for the process identified by PID to exit;
* if HANG is false, checks whether the process has terminated.
* PGMNAME should be the same as supplied to the spawn function and is
* only used for diagnostics. Return values:
*
* 0
* The process exited successful. 0 is stored at R_EXITCODE.
*
* GPG_ERR_GENERAL
* The process exited without success. The exit code of process
* is then stored at R_EXITCODE. An exit code of -1 indicates
* that the process terminated abnormally (e.g. due to a signal).
*
* GPG_ERR_TIMEOUT
* The process is still running (returned only if HANG is false).
*
* GPG_ERR_INV_VALUE
* An invalid PID has been specified.
*
* Other error codes may be returned as well. Unless otherwise noted,
* -1 will be stored at R_EXITCODE. R_EXITCODE may be passed as NULL
* if the exit code is not required (in that case an error message will
* be printed). Note that under Windows PID is not the process id but
* the handle of the process. */
gpg_err_code_t _gpgrt_wait_process (const char *pgmname, pid_t pid, int hang,
int *r_exitcode);
/* Like _gpgrt_wait_process, but for COUNT processes. */
gpg_err_code_t _gpgrt_wait_processes (const char **pgmnames, pid_t *pids,
size_t count, int hang, int *r_exitcodes);
/* Kill a process; that is send an appropriate signal to the process.
* gpgrt_wait_process must be called to actually remove the process
* from the system. An invalid PID is ignored. */
void _gpgrt_kill_process (pid_t pid);
/* Release the process identified by PID. This function is actually
* only required for Windows but it does not harm to always call it.
* It is a nop if PID is invalid. */
void _gpgrt_release_process (pid_t pid);
/*
* Local prototypes for argparse.
*/
int _gpgrt_argparse (estream_t fp, gpgrt_argparse_t *arg, gpgrt_opt_t *opts);
void _gpgrt_usage (int level);
const char *_gpgrt_strusage (int level);
void _gpgrt_set_strusage (const char *(*f)(int));
void _gpgrt_set_usage_outfnc (int (*fnc)(int, const char *));
void _gpgrt_set_fixed_string_mapper (const char *(*f)(const char*));
/*
* Various helper functions
*/
int _gpgrt_cmp_version (const char *a, const char *b, int level);
/*
* Internal platform abstraction functions (sysutils.c)
*/
/* Return true if FD is valid. */
int _gpgrt_fd_valid_p (int fd);
/* A getenv variant which returns a malloced copy. */
char *_gpgrt_getenv (const char *name);
/* A setenv variant which can be used for unsetenv by setting VALUE to
* NULL and OVERRIDE to true. */
gpg_err_code_t _gpgrt_setenv (const char *name,
const char *value, int overwrite);
/* A wrapper around mkdir using a string for the mode (permissions). */
gpg_err_code_t _gpgrt_mkdir (const char *name, const char *modestr);
/* A simple wrapper around chdir. */
gpg_err_code_t _gpgrt_chdir (const char *name);
/* Return the current WD as a malloced string. */
char *_gpgrt_getcwd (void);
/*
* Platform specific functions (Windows)
*/
#ifdef HAVE_W32_SYSTEM
char *_gpgrt_w32_reg_query_string (const char *root,
const char *dir,
const char *name);
#endif /*HAVE_W32_SYSTEM*/
/*
* Missing functions implemented inline.
*/
#ifndef HAVE_STPCPY
static GPG_ERR_INLINE char *
_gpgrt_stpcpy (char *a, const char *b)
{
while (*b)
*a++ = *b++;
*a = 0;
return a;
}
#define stpcpy(a,b) _gpgrt_stpcpy ((a), (b))
#endif /*!HAVE_STPCPY*/
#endif /*_GPGRT_GPGRT_INT_H*/
diff --git a/src/logging.c b/src/logging.c
index 86cf7c3..329982b 100644
--- a/src/logging.c
+++ b/src/logging.c
@@ -1,1342 +1,1342 @@
/* logging.c - Useful logging functions
* Copyright (C) 1998-2001, 2003-2006, 2009-2010,
* 2017 Free Software Foundation, Inc.
* Copyright (C) 1998-1999, 2001-2006, 2008-2017 Werner Koch
*
* This file is part of Libgpg-error.
*
* Libgpg-error is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* Libgpg-error is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see .
* SPDX-License-Identifier: LGPL-2.1+
*
* This file was originally a part of GnuPG.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef HAVE_W32_SYSTEM
# ifdef HAVE_WINSOCK2_H
# include
# endif
# include
#else /*!HAVE_W32_SYSTEM*/
# include
# include
# include
# include
#endif /*!HAVE_W32_SYSTEM*/
#include
#include
/* #include */
#define _GPGRT_NEED_AFLOCAL 1
#include "gpgrt-int.h"
#ifdef HAVE_W32_SYSTEM
# ifndef S_IRWXG
# define S_IRGRP S_IRUSR
# define S_IWGRP S_IWUSR
# endif
# ifndef S_IRWXO
# define S_IROTH S_IRUSR
# define S_IWOTH S_IWUSR
# endif
#endif
#ifdef HAVE_W32CE_SYSTEM
# define isatty(a) (0)
#endif
#undef WITH_IPV6
#if defined (AF_INET6) && defined(PF_INET) \
&& defined (INET6_ADDRSTRLEN) && defined(HAVE_INET_PTON)
# define WITH_IPV6 1
#endif
#ifndef EAFNOSUPPORT
# define EAFNOSUPPORT EINVAL
#endif
#ifndef INADDR_NONE /* Slowaris is missing that. */
#define INADDR_NONE ((unsigned long)(-1))
#endif /*INADDR_NONE*/
#ifdef HAVE_W32_SYSTEM
#define sock_close(a) closesocket(a)
#else
#define sock_close(a) close(a)
#endif
static estream_t logstream;
static int log_socket = -1;
static char prefix_buffer[80];
static int with_time;
static int with_prefix;
static int with_pid;
#ifdef HAVE_W32_SYSTEM
static int no_registry;
#endif
static int (*get_pid_suffix_cb)(unsigned long *r_value);
static const char * (*socket_dir_cb)(void);
static int running_detached;
static int force_prefixes;
static int missing_lf;
static int errorcount;
/* An object to convey data to the fmt_string_filter. */
struct fmt_string_filter_s
{
char *last_result;
};
/* Get the error count as maintained by the log fucntions. With CLEAR
* set reset the counter. */
int
_gpgrt_get_errorcount (int clear)
{
int n = errorcount;
if (clear)
errorcount = 0;
return n;
}
/* Increment the error count as maintainer by the log functions. */
void
_gpgrt_inc_errorcount (void)
{
errorcount++;
}
/* The following 3 functions are used by _gpgrt_fopencookie to write logs
to a socket. */
struct fun_cookie_s
{
int fd;
int quiet;
int want_socket;
int is_socket;
#ifdef HAVE_W32CE_SYSTEM
int use_writefile;
#endif
char name[1];
};
/* Write NBYTES of BUFFER to file descriptor FD. */
static int
writen (int fd, const void *buffer, size_t nbytes, int is_socket)
{
const char *buf = buffer;
size_t nleft = nbytes;
int nwritten;
#ifndef HAVE_W32_SYSTEM
(void)is_socket; /* Not required. */
#endif
while (nleft > 0)
{
#ifdef HAVE_W32_SYSTEM
if (is_socket)
nwritten = send (fd, buf, nleft, 0);
else
#endif
nwritten = write (fd, buf, nleft);
if (nwritten < 0 && errno == EINTR)
continue;
if (nwritten < 0)
return -1;
nleft -= nwritten;
buf = buf + nwritten;
}
return 0;
}
/* Returns true if STR represents a valid port number in decimal
notation and no garbage is following. */
static int
parse_portno (const char *str, unsigned short *r_port)
{
unsigned int value;
for (value=0; *str && (*str >= '0' && *str <= '9'); str++)
{
value = value * 10 + (*str - '0');
if (value > 65535)
return 0;
}
if (*str || !value)
return 0;
*r_port = value;
return 1;
}
static gpgrt_ssize_t
fun_writer (void *cookie_arg, const void *buffer, size_t size)
{
struct fun_cookie_s *cookie = cookie_arg;
/* FIXME: Use only estream with a callback for socket writing. This
avoids the ugly mix of fd and estream code. */
/* Note that we always try to reconnect to the socket but print
error messages only the first time an error occurred. If
RUNNING_DETACHED is set we don't fall back to stderr and even do
not print any error messages. This is needed because detached
processes often close stderr and by writing to file descriptor 2
we might send the log message to a file not intended for logging
(e.g. a pipe or network connection). */
if (cookie->want_socket && cookie->fd == -1)
{
#ifdef WITH_IPV6
struct sockaddr_in6 srvr_addr_in6;
#endif
struct sockaddr_in srvr_addr_in;
#ifndef HAVE_W32_SYSTEM
struct sockaddr_un srvr_addr_un;
#endif
const char *name_for_err = "";
size_t addrlen;
struct sockaddr *srvr_addr = NULL;
unsigned short port = 0;
int af = AF_LOCAL;
int pf = PF_LOCAL;
const char *name = cookie->name;
/* Not yet open or meanwhile closed due to an error. */
cookie->is_socket = 0;
/* Check whether this is a TCP socket or a local socket. */
if (!strncmp (name, "tcp://", 6) && name[6])
{
name += 6;
af = AF_INET;
pf = PF_INET;
}
#ifndef HAVE_W32_SYSTEM
else if (!strncmp (name, "socket://", 9))
name += 9;
#endif
if (af == AF_LOCAL)
{
addrlen = 0;
#ifndef HAVE_W32_SYSTEM
memset (&srvr_addr, 0, sizeof srvr_addr);
srvr_addr_un.sun_family = af;
if (!*name && (name = socket_dir_cb ()) && *name)
{
if (strlen (name) + 7 < sizeof (srvr_addr_un.sun_path)-1)
{
strncpy (srvr_addr_un.sun_path,
name, sizeof (srvr_addr_un.sun_path)-1);
strcat (srvr_addr_un.sun_path, "/S.log");
srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path)-1] = 0;
srvr_addr = (struct sockaddr *)&srvr_addr_un;
addrlen = SUN_LEN (&srvr_addr_un);
name_for_err = srvr_addr_un.sun_path;
}
}
else
{
if (*name && strlen (name) < sizeof (srvr_addr_un.sun_path)-1)
{
strncpy (srvr_addr_un.sun_path,
name, sizeof (srvr_addr_un.sun_path)-1);
srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path)-1] = 0;
srvr_addr = (struct sockaddr *)&srvr_addr_un;
addrlen = SUN_LEN (&srvr_addr_un);
}
}
#endif /*!HAVE_W32SYSTEM*/
}
else
{
char *addrstr, *p;
#ifdef HAVE_INET_PTON
void *addrbuf = NULL;
#endif /*HAVE_INET_PTON*/
addrstr = _gpgrt_malloc (strlen (name) + 1);
if (!addrstr)
addrlen = 0; /* This indicates an error. */
else if (*name == '[')
{
/* Check for IPv6 literal address. */
strcpy (addrstr, name+1);
p = strchr (addrstr, ']');
if (!p || p[1] != ':' || !parse_portno (p+2, &port))
{
_gpg_err_set_errno (EINVAL);
addrlen = 0;
}
else
{
*p = 0;
#ifdef WITH_IPV6
af = AF_INET6;
pf = PF_INET6;
memset (&srvr_addr_in6, 0, sizeof srvr_addr_in6);
srvr_addr_in6.sin6_family = af;
srvr_addr_in6.sin6_port = htons (port);
#ifdef HAVE_INET_PTON
addrbuf = &srvr_addr_in6.sin6_addr;
#endif /*HAVE_INET_PTON*/
srvr_addr = (struct sockaddr *)&srvr_addr_in6;
addrlen = sizeof srvr_addr_in6;
#else
_gpg_err_set_errno (EAFNOSUPPORT);
addrlen = 0;
#endif
}
}
else
{
/* Check for IPv4 literal address. */
strcpy (addrstr, name);
p = strchr (addrstr, ':');
if (!p || !parse_portno (p+1, &port))
{
_gpg_err_set_errno (EINVAL);
addrlen = 0;
}
else
{
*p = 0;
memset (&srvr_addr_in, 0, sizeof srvr_addr_in);
srvr_addr_in.sin_family = af;
srvr_addr_in.sin_port = htons (port);
#ifdef HAVE_INET_PTON
addrbuf = &srvr_addr_in.sin_addr;
#endif /*HAVE_INET_PTON*/
srvr_addr = (struct sockaddr *)&srvr_addr_in;
addrlen = sizeof srvr_addr_in;
}
}
if (addrlen)
{
#ifdef HAVE_INET_PTON
if (inet_pton (af, addrstr, addrbuf) != 1)
addrlen = 0;
#else /*!HAVE_INET_PTON*/
/* We need to use the old function. If we are here v6
support isn't enabled anyway and thus we can do fine
without. Note that Windows has a compatible inet_pton
function named inetPton, but only since Vista. */
srvr_addr_in.sin_addr.s_addr = inet_addr (addrstr);
if (srvr_addr_in.sin_addr.s_addr == INADDR_NONE)
addrlen = 0;
#endif /*!HAVE_INET_PTON*/
}
_gpgrt_free (addrstr);
}
cookie->fd = addrlen? socket (pf, SOCK_STREAM, 0) : -1;
if (cookie->fd == -1)
{
if (!cookie->quiet && !running_detached
&& isatty (_gpgrt_fileno (es_stderr)))
_gpgrt_fprintf (es_stderr,
"failed to create socket for logging: %s\n",
strerror (errno));
}
else
{
if (connect (cookie->fd, srvr_addr, addrlen) == -1)
{
if (!cookie->quiet && !running_detached
&& isatty (_gpgrt_fileno (es_stderr)))
_gpgrt_fprintf (es_stderr, "can't connect to '%s%s': %s\n",
cookie->name, name_for_err, strerror(errno));
sock_close (cookie->fd);
cookie->fd = -1;
}
}
if (cookie->fd == -1)
{
if (!running_detached)
{
/* Due to all the problems with apps not running
detached but being called with stderr closed or used
for a different purposes, it does not make sense to
switch to stderr. We therefore disable it. */
if (!cookie->quiet)
{
/* fputs ("switching logging to stderr\n", stderr);*/
cookie->quiet = 1;
}
cookie->fd = -1; /*fileno (stderr);*/
}
}
else /* Connection has been established. */
{
cookie->quiet = 0;
cookie->is_socket = 1;
}
}
log_socket = cookie->fd;
if (cookie->fd != -1)
{
#ifdef HAVE_W32CE_SYSTEM
if (cookie->use_writefile)
{
DWORD nwritten;
WriteFile ((HANDLE)cookie->fd, buffer, size, &nwritten, NULL);
return (gpgrt_ssize_t)size; /* Okay. */
}
#endif
if (!writen (cookie->fd, buffer, size, cookie->is_socket))
return (gpgrt_ssize_t)size; /* Okay. */
}
if (!running_detached && cookie->fd != -1
&& isatty (_gpgrt_fileno (es_stderr)))
{
if (*cookie->name)
_gpgrt_fprintf (es_stderr, "error writing to '%s': %s\n",
cookie->name, strerror(errno));
else
_gpgrt_fprintf (es_stderr, "error writing to file descriptor %d: %s\n",
cookie->fd, strerror(errno));
}
if (cookie->is_socket && cookie->fd != -1)
{
sock_close (cookie->fd);
cookie->fd = -1;
log_socket = -1;
}
return (gpgrt_ssize_t)size;
}
static int
fun_closer (void *cookie_arg)
{
struct fun_cookie_s *cookie = cookie_arg;
if (cookie->fd != -1 && cookie->fd != 2)
sock_close (cookie->fd);
_gpgrt_free (cookie);
log_socket = -1;
return 0;
}
/* Common function to either set the logging to a file or a file
descriptor. */
static void
set_file_fd (const char *name, int fd, estream_t stream)
{
estream_t fp;
int want_socket = 0;
#ifdef HAVE_W32CE_SYSTEM
int use_writefile = 0;
#endif
struct fun_cookie_s *cookie;
/* Close an open log stream. */
if (logstream)
{
if (logstream != es_stderr)
_gpgrt_fclose (logstream);
logstream = NULL;
}
if (stream)
{
/* We don't use a cookie to log directly to a stream. */
fp = stream;
goto leave;
}
/* Figure out what kind of logging we want. */
if (name && !strcmp (name, "-"))
{
name = NULL;
fd = _gpgrt_fileno (es_stderr);
}
if (name && !strncmp (name, "tcp://", 6) && name[6])
want_socket = 1;
#ifndef HAVE_W32_SYSTEM
else if (name && !strncmp (name, "socket://", 9))
want_socket = 2;
#endif /*HAVE_W32_SYSTEM*/
#ifdef HAVE_W32CE_SYSTEM
else if (name && !strcmp (name, "GPG2:"))
{
HANDLE hd;
ActivateDevice (L"Drivers\\"GNUPG_NAME"_Log", 0);
/* Ignore a filename and write the debug output to the GPG2:
device. */
hd = CreateFile (L"GPG2:", GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
fd = (hd == INVALID_HANDLE_VALUE)? -1 : (int)hd;
name = NULL;
force_prefixes = 1;
use_writefile = 1;
}
#endif /*HAVE_W32CE_SYSTEM*/
/* Setup a new stream. */
cookie = _gpgrt_malloc (sizeof *cookie + (name? strlen (name):0));
if (!cookie)
return; /* oops */
strcpy (cookie->name, name? name:"");
cookie->quiet = 0;
cookie->is_socket = 0;
cookie->want_socket = want_socket;
#ifdef HAVE_W32CE_SYSTEM
cookie->use_writefile = use_writefile;
#endif
if (!name)
cookie->fd = fd;
else if (want_socket)
cookie->fd = -1;
else
{
do
cookie->fd = open (name, O_WRONLY|O_APPEND|O_CREAT,
(S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH));
while (cookie->fd == -1 && errno == EINTR);
}
log_socket = cookie->fd;
{
es_cookie_io_functions_t io = { NULL };
io.func_write = fun_writer;
io.func_close = fun_closer;
fp = _gpgrt_fopencookie (cookie, "w", io);
}
/* On error default to a stderr based estream. */
if (!fp)
fp = es_stderr;
leave:
_gpgrt_setvbuf (fp, NULL, _IOLBF, 0);
logstream = fp;
/* We always need to print the prefix and the pid for socket mode,
so that the server reading the socket can do something
meaningful. */
force_prefixes = want_socket;
missing_lf = 0;
}
/* Set the file to write log to. The special names NULL and "-" may
* be used to select stderr and names formatted like
* "socket:///home/foo/mylogs" may be used to write the logging to the
* socket "/home/foo/mylogs". If the connection to the socket fails
* or a write error is detected, the function writes to stderr and
* tries the next time again to connect the socket. Calling this
* function with (NULL, NULL, -1) sets the default sink.
* Warning: This function is not thread-safe.
*/
void
_gpgrt_log_set_sink (const char *name, estream_t stream, int fd)
{
if (name && !stream && fd == -1)
set_file_fd (name, -1, NULL);
else if (!name && !stream && fd != -1)
{
if (!_gpgrt_fd_valid_p (fd))
_gpgrt_log_fatal ("gpgrt_log_set_sink: fd is invalid: %s\n",
strerror (errno));
set_file_fd (NULL, fd, NULL);
}
else if (!name && stream && fd == -1)
{
set_file_fd (NULL, -1, stream);
}
else /* default */
set_file_fd ("-", -1, NULL);
}
/* Set a function to retrieve the directory name of a socket if
* only "socket://" has been given to log_set_file.
* Warning: This function is not thread-safe. */
void
_gpgrt_log_set_socket_dir_cb (const char *(*fnc)(void))
{
socket_dir_cb = fnc;
}
/* Warning: This function is not thread-safe. */
void
_gpgrt_log_set_pid_suffix_cb (int (*cb)(unsigned long *r_value))
{
get_pid_suffix_cb = cb;
}
/* Warning: Changing TEXT is not thread-safe. Changing only flags
* might be thread-safe. */
void
_gpgrt_log_set_prefix (const char *text, unsigned int flags)
{
if (text)
{
strncpy (prefix_buffer, text, sizeof (prefix_buffer)-1);
prefix_buffer[sizeof (prefix_buffer)-1] = 0;
}
with_prefix = (flags & GPGRT_LOG_WITH_PREFIX);
with_time = (flags & GPGRT_LOG_WITH_TIME);
with_pid = (flags & GPGRT_LOG_WITH_PID);
running_detached = (flags & GPGRT_LOG_RUN_DETACHED);
#ifdef HAVE_W32_SYSTEM
no_registry = (flags & GPGRT_LOG_NO_REGISTRY);
#endif
}
const char *
_gpgrt_log_get_prefix (unsigned int *flags)
{
if (flags)
{
*flags = 0;
if (with_prefix)
*flags |= GPGRT_LOG_WITH_PREFIX;
if (with_time)
*flags |= GPGRT_LOG_WITH_TIME;
if (with_pid)
*flags |= GPGRT_LOG_WITH_PID;
if (running_detached)
*flags |= GPGRT_LOG_RUN_DETACHED;
#ifdef HAVE_W32_SYSTEM
if (no_registry)
*flags |= GPGRT_LOG_NO_REGISTRY;
#endif
}
return prefix_buffer;
}
/* This function returns true if the file descriptor FD is in use for
* logging. This is preferable over a test using log_get_fd in that
* it allows the logging code to use more then one file descriptor. */
int
_gpgrt_log_test_fd (int fd)
{
if (logstream)
{
int tmp = _gpgrt_fileno (logstream);
if ( tmp != -1 && tmp == fd)
return 1;
}
if (log_socket != -1 && log_socket == fd)
return 1;
return 0;
}
int
_gpgrt_log_get_fd ()
{
return logstream? _gpgrt_fileno (logstream) : -1;
}
estream_t
_gpgrt_log_get_stream ()
{
if (!logstream)
{
/* Make sure a log stream has been set. */
_gpgrt_log_set_sink (NULL, NULL, -1);
if (!logstream)
{
fputs ("gpgrt fatal: failed to init log stream\n", stderr);
_gpgrt_abort ();
}
}
return logstream;
}
-/* A fiter used with the fprintf_sf function to sanitize the args for
+/* A filter used with the fprintf_sf function to sanitize the args for
* "%s" format specifiers. */
static char *
fmt_string_filter (const char *string, int no, void *opaque)
{
struct fmt_string_filter_s *state = opaque;
const unsigned char *p;
size_t buflen;
char *d;
int any;
if (no == -1)
{
/* The printf engine asked us to release resources. */
if (state->last_result)
{
_gpgrt_free (state->last_result);
state->last_result = NULL;
}
return NULL;
}
if (!string)
return NULL; /* Nothing to filter - printf handles NULL nicely. */
/* Check whether escaping is needed and count needed length. */
any = 0;
buflen = 1;
for (p = (const unsigned char *)string; *p; p++)
{
switch (*p)
{
case '\n':
case '\r':
case '\f':
case '\v':
case '\b':
case '\t':
case '\a':
case '\\':
buflen += 2;
any = 1;
break;
default:
if (*p < 0x20 || *p == 0x7f)
{
buflen += 5;
any = 1;
}
else
buflen++;
}
}
if (!any)
return (char*)string; /* Nothing to escape. */
/* Create a buffer and escape the input. */
_gpgrt_free (state->last_result);
state->last_result = _gpgrt_malloc (buflen);
if (!state->last_result)
return "[out_of_core_in_format_string_filter]";
d = state->last_result;
for (p = (const unsigned char *)string; *p; p++)
{
switch (*p)
{
case '\n': *d++ = '\\'; *d++ = 'n'; break;
case '\r': *d++ = '\\'; *d++ = 'r'; break;
case '\f': *d++ = '\\'; *d++ = 'f'; break;
case '\v': *d++ = '\\'; *d++ = 'v'; break;
case '\b': *d++ = '\\'; *d++ = 'b'; break;
case '\t': *d++ = '\\'; *d++ = 't'; break;
case '\a': *d++ = '\\'; *d++ = 'a'; break;
case '\\': *d++ = '\\'; *d++ = '\\'; break;
default:
if (*p < 0x20 || *p == 0x7f)
{
snprintf (d, 5, "\\x%02x", *p);
d += 4;
}
else
*d++ = *p;
}
}
*d = 0;
return state->last_result;
}
/* Note: LOGSTREAM is expected to be locked. */
static int
print_prefix (int level, int leading_backspace)
{
int rc;
int length = 0;
if (level != GPGRT_LOGLVL_CONT)
{ /* Note this does not work for multiple line logging as we would
* need to print to a buffer first */
if (with_time && !force_prefixes)
{
struct tm *tp;
time_t atime = time (NULL);
tp = localtime (&atime);
rc = _gpgrt_fprintf_unlocked (logstream,
"%04d-%02d-%02d %02d:%02d:%02d ",
1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
tp->tm_hour, tp->tm_min, tp->tm_sec );
if (rc > 0)
length += rc;
}
if (with_prefix || force_prefixes)
{
_gpgrt_fputs_unlocked (prefix_buffer, logstream);
length += strlen (prefix_buffer);
}
if (with_pid || force_prefixes)
{
unsigned long pidsuf;
int pidfmt;
if (get_pid_suffix_cb && (pidfmt=get_pid_suffix_cb (&pidsuf)))
rc = _gpgrt_fprintf_unlocked (logstream,
pidfmt == 1? "[%u.%lu]":"[%u.%lx]",
(unsigned int)getpid (), pidsuf);
else
rc = _gpgrt_fprintf_unlocked (logstream, "[%u]",
(unsigned int)getpid ());
if (rc > 0)
length += rc;
}
if ((!with_time && (with_prefix || with_pid)) || force_prefixes)
{
_gpgrt_putc_unlocked (':', logstream);
length++;
}
/* A leading backspace suppresses the extra space so that we can
correctly output, programname, filename and linenumber. */
if (!leading_backspace
&& (with_time || with_prefix || with_pid || force_prefixes))
{
_gpgrt_putc_unlocked (' ', logstream);
length++;
}
}
switch (level)
{
case GPGRT_LOGLVL_BEGIN: break;
case GPGRT_LOGLVL_CONT: break;
case GPGRT_LOGLVL_INFO: break;
case GPGRT_LOGLVL_WARN: break;
case GPGRT_LOGLVL_ERROR: break;
case GPGRT_LOGLVL_FATAL:
_gpgrt_fputs_unlocked ("Fatal: ", logstream);
length += 7;
break;
case GPGRT_LOGLVL_BUG:
_gpgrt_fputs_unlocked ("Ohhhh jeeee: ", logstream);
length += 13;
break;
case GPGRT_LOGLVL_DEBUG:
_gpgrt_fputs_unlocked ("DBG: ", logstream);
length += 5;
break;
default:
rc = _gpgrt_fprintf_unlocked (logstream,
"[Unknown log level %d]: ", level);
if (rc > 0)
length += rc;
break;
}
return length;
}
/* Internal worker function. Exported so that we can use it in
* visibility.c. Returs the number of characters printed or 0 if the
* line ends in a LF. */
int
_gpgrt_logv_internal (int level, int ignore_arg_ptr, const char *extrastring,
const char *prefmt, const char *fmt, va_list arg_ptr)
{
int leading_backspace = (fmt && *fmt == '\b');
int length;
int rc;
if (!logstream)
{
#ifdef HAVE_W32_SYSTEM
char *tmp;
tmp = (no_registry
? NULL
: _gpgrt_w32_reg_query_string (NULL, "Software\\\\GNU\\\\GnuPG",
"DefaultLogFile"));
_gpgrt_log_set_sink (tmp && *tmp? tmp : NULL, NULL, -1);
_gpgrt_free (tmp);
#else
/* Make sure a log stream has been set. */
_gpgrt_log_set_sink (NULL, NULL, -1);
#endif
if (!logstream)
{
fputs ("gpgrt fatal: failed to init log stream\n", stderr);
_gpgrt_abort ();
}
}
_gpgrt_flockfile (logstream);
if (missing_lf && level != GPGRT_LOGLVL_CONT)
_gpgrt_putc_unlocked ('\n', logstream );
missing_lf = 0;
length = print_prefix (level, leading_backspace);
if (leading_backspace)
fmt++;
if (fmt)
{
if (prefmt)
{
_gpgrt_fputs_unlocked (prefmt, logstream);
length += strlen (prefmt);
}
if (ignore_arg_ptr)
{ /* This is used by log_string and comes with the extra
* feature that after a LF the next line is indent at the
* length of the prefix. Note that we do not yet include
* the length of the timestamp and pid in the indent
* computation. */
const char *p, *pend;
for (p = fmt; (pend = strchr (p, '\n')); p = pend+1)
{
rc = _gpgrt_fprintf_unlocked (logstream, "%*s%.*s",
(int)((p != fmt
&& (with_prefix || force_prefixes))
?strlen (prefix_buffer)+2:0), "",
(int)(pend - p)+1, p);
if (rc > 0)
length += rc;
}
_gpgrt_fputs_unlocked (p, logstream);
length += strlen (p);
}
else
{
struct fmt_string_filter_s sf = {NULL};
rc = _gpgrt_vfprintf_unlocked (logstream, fmt_string_filter, &sf,
fmt, arg_ptr);
if (rc > 0)
length += rc;
}
if (*fmt && fmt[strlen(fmt)-1] != '\n')
missing_lf = 1;
}
/* If we have an EXTRASTRING print it now while we still hold the
* lock on the logstream. */
if (extrastring)
{
int c;
if (missing_lf)
{
_gpgrt_putc_unlocked ('\n', logstream);
missing_lf = 0;
length = 0;
}
length += print_prefix (level, leading_backspace);
_gpgrt_fputs_unlocked (">> ", logstream);
length += 3;
missing_lf = 1;
while ((c = *extrastring++))
{
missing_lf = 1;
if (c == '\\')
{
_gpgrt_fputs_unlocked ("\\\\", logstream);
length += 2;
}
else if (c == '\r')
{
_gpgrt_fputs_unlocked ("\\r", logstream);
length += 2;
}
else if (c == '\n')
{
_gpgrt_fputs_unlocked ("\\n\n", logstream);
length = 0;
if (*extrastring)
{
length += print_prefix (level, leading_backspace);
_gpgrt_fputs_unlocked (">> ", logstream);
length += 3;
}
else
missing_lf = 0;
}
else
{
_gpgrt_putc_unlocked (c, logstream);
length++;
}
}
if (missing_lf)
{
_gpgrt_putc_unlocked ('\n', logstream);
length = 0;
missing_lf = 0;
}
}
if (level == GPGRT_LOGLVL_FATAL)
{
if (missing_lf)
_gpgrt_putc_unlocked ('\n', logstream);
_gpgrt_funlockfile (logstream);
exit (2);
}
else if (level == GPGRT_LOGLVL_BUG)
{
if (missing_lf)
_gpgrt_putc_unlocked ('\n', logstream );
_gpgrt_funlockfile (logstream);
/* Using backtrace requires a configure test and to pass
* -rdynamic to gcc. Thus we do not enable it now. */
/* { */
/* void *btbuf[20]; */
/* int btidx, btlen; */
/* char **btstr; */
/* btlen = backtrace (btbuf, DIM (btbuf)); */
/* btstr = backtrace_symbols (btbuf, btlen); */
/* if (btstr) */
/* for (btidx=0; btidx < btlen; btidx++) */
/* log_debug ("[%d] %s\n", btidx, btstr[btidx]); */
/* } */
_gpgrt_abort ();
}
else
_gpgrt_funlockfile (logstream);
/* Bumb the error counter for log_error. */
if (level == GPGRT_LOGLVL_ERROR)
{
/* Protect against counter overflow. */
if (errorcount < 30000)
errorcount++;
}
return length;
}
void
_gpgrt_log (int level, const char *fmt, ...)
{
va_list arg_ptr ;
va_start (arg_ptr, fmt) ;
_gpgrt_logv_internal (level, 0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
}
void
_gpgrt_logv (int level, const char *fmt, va_list arg_ptr)
{
_gpgrt_logv_internal (level, 0, NULL, NULL, fmt, arg_ptr);
}
/* Same as log_logv but PREFIX is printed immediately before FMT.
* Note that PREFIX is an additional string and independent of the
* prefix set by gpgrt_log_set_prefix. */
void
_gpgrt_logv_prefix (int level, const char *prefix,
const char *fmt, va_list arg_ptr)
{
_gpgrt_logv_internal (level, 0, NULL, prefix, fmt, arg_ptr);
}
static void
do_log_ignore_arg (int level, const char *str, ...)
{
va_list arg_ptr;
va_start (arg_ptr, str);
_gpgrt_logv_internal (level, 1, NULL, NULL, str, arg_ptr);
va_end (arg_ptr);
}
/* Log STRING at LEVEL but indent from the second line on by the
* length of the prefix. */
void
_gpgrt_log_string (int level, const char *string)
{
/* We need a dummy arg_ptr, but there is no portable way to create
* one. So we call the _gpgrt_logv_internal function through a
* variadic wrapper. */
do_log_ignore_arg (level, string);
}
void
_gpgrt_log_info (const char *fmt, ...)
{
va_list arg_ptr ;
va_start (arg_ptr, fmt);
_gpgrt_logv_internal (GPGRT_LOGLVL_INFO, 0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
}
void
_gpgrt_log_error (const char *fmt, ...)
{
va_list arg_ptr ;
va_start (arg_ptr, fmt);
_gpgrt_logv_internal (GPGRT_LOGLVL_ERROR, 0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
}
void
_gpgrt_log_fatal (const char *fmt, ...)
{
va_list arg_ptr ;
va_start (arg_ptr, fmt);
_gpgrt_logv_internal (GPGRT_LOGLVL_FATAL, 0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
_gpgrt_abort (); /* Never called; just to make the compiler happy. */
}
void
_gpgrt_log_bug (const char *fmt, ...)
{
va_list arg_ptr ;
va_start (arg_ptr, fmt);
_gpgrt_logv_internal (GPGRT_LOGLVL_BUG, 0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
_gpgrt_abort (); /* Never called; just to make the compiler happy. */
}
void
_gpgrt_log_debug (const char *fmt, ...)
{
va_list arg_ptr;
va_start (arg_ptr, fmt);
_gpgrt_logv_internal (GPGRT_LOGLVL_DEBUG, 0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
}
/* The same as log_debug but at the end of the output STRING is
* printed with LFs expanded to include the prefix and a final --end--
* marker. */
void
_gpgrt_log_debug_string (const char *string, const char *fmt, ...)
{
va_list arg_ptr;
va_start (arg_ptr, fmt);
_gpgrt_logv_internal (GPGRT_LOGLVL_DEBUG, 0, string, NULL, fmt, arg_ptr);
va_end (arg_ptr);
}
void
_gpgrt_log_printf (const char *fmt, ...)
{
va_list arg_ptr;
va_start (arg_ptr, fmt);
_gpgrt_logv_internal (fmt ? GPGRT_LOGLVL_CONT : GPGRT_LOGLVL_BEGIN,
0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
}
/* Flush the log - this is useful to make sure that the trailing
linefeed has been printed. */
void
_gpgrt_log_flush (void)
{
do_log_ignore_arg (GPGRT_LOGLVL_CONT, NULL);
}
/* Print a hexdump of (BUFFER,LENGTH). With FMT passed as NULL print
* just the raw dump (in this case ARG_PTR is not used), with FMT
* being an empty string, print a trailing linefeed, otherwise print
* an entire debug line with the expanded FMT followed by a possible
* wrapped hexdump and a final LF. */
void
_gpgrt_logv_printhex (const void *buffer, size_t length,
const char *fmt, va_list arg_ptr)
{
int wrap = 0;
int cnt = 0;
const unsigned char *p;
/* FIXME: This printing is not yet protected by _gpgrt_flockfile. */
if (fmt && *fmt)
{
_gpgrt_logv_internal (GPGRT_LOGLVL_DEBUG, 0, NULL, NULL, fmt, arg_ptr);
wrap = 1;
}
if (length)
{
if (wrap)
_gpgrt_log_printf (" ");
for (p = buffer; length--; p++)
{
_gpgrt_log_printf ("%02x", *p);
if (wrap && ++cnt == 32 && length)
{
cnt = 0;
/* (we indicate continuations with a backslash) */
_gpgrt_log_printf (" \\\n");
_gpgrt_log_debug ("%s", "");
if (fmt && *fmt)
_gpgrt_log_printf (" ");
}
}
}
if (fmt)
_gpgrt_log_printf ("\n");
}
/* Print a hexdump of (BUFFER,LENGTH). With FMT passed as NULL print
* just the raw dump, with FMT being an empty string, print a trailing
* linefeed, otherwise print an entire debug line with the expanded
* FMT followed by the hexdump and a final LF. */
void
_gpgrt_log_printhex (const void *buffer, size_t length,
const char *fmt, ...)
{
va_list arg_ptr;
if (fmt)
{
va_start (arg_ptr, fmt);
_gpgrt_logv_printhex (buffer, length, fmt, arg_ptr);
va_end (arg_ptr);
}
else
{
/* va_list is not necessary a pointer and thus we can't use NULL
* because that would conflict with platforms using a straight
* struct for it (e.g. arm64). We use a dummy variable instead;
* the static is a simple way zero it out so to not get
* complains about uninitialized use. */
static va_list dummy_argptr;
_gpgrt_logv_printhex (buffer, length, NULL, dummy_argptr);
}
}
/* Print a microsecond timestamp followed by FMT. */
void
_gpgrt_logv_clock (const char *fmt, va_list arg_ptr)
{
#if ENABLE_LOG_CLOCK
static unsigned long long initial;
struct timespec tv;
unsigned long long now;
char clockbuf[50];
if (clock_gettime (CLOCK_REALTIME, &tv))
{
_gpgrt_log_debug ("error getting the realtime clock value\n");
return;
}
now = tv.tv_sec * 1000000000ull;
now += tv.tv_nsec;
if (!initial)
initial = now;
snprintf (clockbuf, sizeof clockbuf, "[%6llu] ", (now - initial)/1000);
_gpgrt_logv_internal (GPGRT_LOGLVL_DEBUG, 0, NULL, clockbuf, fmt, arg_ptr);
#else /*!ENABLE_LOG_CLOCK*/
/* You may need to link with -ltr to use the above code. */
_gpgrt_logv_internal (GPGRT_LOGLVL_DEBUG,
0, NULL, "[no clock] ", fmt, arg_ptr);
#endif /*!ENABLE_LOG_CLOCK*/
}
/* Print a microsecond timestamp followed by FMT. */
void
_gpgrt_log_clock (const char *fmt, ...)
{
va_list arg_ptr;
va_start (arg_ptr, fmt);
_gpgrt_logv_clock (fmt, arg_ptr);
va_end (arg_ptr);
}
void
_gpgrt__log_assert (const char *expr, const char *file,
int line, const char *func)
{
#ifdef GPGRT_HAVE_MACRO_FUNCTION
_gpgrt_log (GPGRT_LOGLVL_BUG, "Assertion \"%s\" in %s failed (%s:%d)\n",
expr, func, file, line);
#else /*!GPGRT_HAVE_MACRO_FUNCTION*/
_gpgrt_log (GPGRT_LOGLVL_BUG, "Assertion \"%s\" failed (%s:%d)\n",
expr, file, line);
#endif /*!GPGRT_HAVE_MACRO_FUNCTION*/
_gpgrt_abort (); /* Never called; just to make the compiler happy. */
}
diff --git a/src/w32-estream.c b/src/w32-estream.c
index 9e33cdd..8ce9419 100644
--- a/src/w32-estream.c
+++ b/src/w32-estream.c
@@ -1,1064 +1,1064 @@
/* w32-estream.c - es_poll support on W32.
* Copyright (C) 2000 Werner Koch (dd9jn)
* Copyright (C) 2001, 2002, 2003, 2004, 2007, 2010, 2016 g10 Code GmbH
*
* This file is part of libgpg-error.
*
* libgpg-error is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* libgpg-error is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see .
*/
/*
* This file is based on GPGME's w32-io.c started in 2001.
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#ifdef HAVE_SYS_TIME_H
# include
#endif
#ifdef HAVE_SYS_TYPES_H
# include
#endif
#include
#include
/* Enable tracing. The value is the module name to be printed. */
/*#define ENABLE_TRACING "estream" */
#include "gpgrt-int.h"
/*
* In order to support es_poll on Windows, we create a proxy shim that
* we use as the estream I/O functions. This shim creates reader and
* writer threads that use the original I/O functions.
*/
/* Calculate array dimension. */
#ifndef DIM
#define DIM(array) (sizeof (array) / sizeof (*array))
#endif
-#define READBUF_SIZE 4096
-#define WRITEBUF_SIZE 4096
+#define READBUF_SIZE 8192
+#define WRITEBUF_SIZE 8192
typedef struct estream_cookie_w32_pollable *estream_cookie_w32_pollable_t;
struct reader_context_s
{
estream_cookie_w32_pollable_t pcookie;
HANDLE thread_hd;
CRITICAL_SECTION mutex;
int stop_me;
int eof;
int eof_shortcut;
int error;
int error_code;
/* This is manually reset. */
HANDLE have_data_ev;
/* This is automatically reset. */
HANDLE have_space_ev;
/* This is manually reset but actually only triggered once. */
HANDLE close_ev;
size_t readpos, writepos;
char buffer[READBUF_SIZE];
};
struct writer_context_s
{
estream_cookie_w32_pollable_t pcookie;
HANDLE thread_hd;
CRITICAL_SECTION mutex;
int stop_me;
int error;
int error_code;
/* This is manually reset. */
HANDLE have_data;
HANDLE is_empty;
HANDLE close_ev;
size_t nbytes;
char buffer[WRITEBUF_SIZE];
};
/* Cookie for pollable objects. */
struct estream_cookie_w32_pollable
{
unsigned int modeflags;
struct cookie_io_functions_s next_functions;
void *next_cookie;
struct reader_context_s *reader;
struct writer_context_s *writer;
};
static DWORD CALLBACK
reader (void *arg)
{
struct reader_context_s *ctx = arg;
int nbytes;
ssize_t nread;
trace (("%p: reader starting", ctx));
for (;;)
{
EnterCriticalSection (&ctx->mutex);
/* Leave a 1 byte gap so that we can see whether it is empty or
full. */
while ((ctx->writepos + 1) % READBUF_SIZE == ctx->readpos)
{
/* Wait for space. */
if (!ResetEvent (ctx->have_space_ev))
trace (("%p: ResetEvent failed: ec=%d", ctx, (int)GetLastError()));
LeaveCriticalSection (&ctx->mutex);
trace (("%p: waiting for space", ctx));
WaitForSingleObject (ctx->have_space_ev, INFINITE);
trace (("%p: got space", ctx));
EnterCriticalSection (&ctx->mutex);
}
gpgrt_assert (((ctx->writepos + 1) % READBUF_SIZE != ctx->readpos));
if (ctx->stop_me)
{
LeaveCriticalSection (&ctx->mutex);
break;
}
nbytes = (ctx->readpos + READBUF_SIZE
- ctx->writepos - 1) % READBUF_SIZE;
gpgrt_assert (nbytes);
if (nbytes > READBUF_SIZE - ctx->writepos)
nbytes = READBUF_SIZE - ctx->writepos;
LeaveCriticalSection (&ctx->mutex);
trace (("%p: reading up to %d bytes", ctx, nbytes));
nread = ctx->pcookie->next_functions.public.func_read
(ctx->pcookie->next_cookie, ctx->buffer + ctx->writepos, nbytes);
trace (("%p: got %d bytes", ctx, nread));
if (nread < 0)
{
ctx->error_code = (int) errno;
/* NOTE (W32CE): Do not ignore ERROR_BUSY! Check at
least stop_me if that happens. */
if (ctx->error_code == ERROR_BROKEN_PIPE)
{
ctx->eof = 1;
trace (("%p: got EOF (broken pipe)", ctx));
}
else
{
ctx->error = 1;
trace (("%p: read error: ec=%d", ctx, ctx->error_code));
}
break;
}
EnterCriticalSection (&ctx->mutex);
if (ctx->stop_me)
{
LeaveCriticalSection (&ctx->mutex);
break;
}
if (!nread)
{
ctx->eof = 1;
trace (("%p: got eof", ctx));
LeaveCriticalSection (&ctx->mutex);
break;
}
ctx->writepos = (ctx->writepos + nread) % READBUF_SIZE;
if (!SetEvent (ctx->have_data_ev))
trace (("%p: SetEvent (%p) failed: ec=%d",
ctx, ctx->have_data_ev, (int)GetLastError ()));
LeaveCriticalSection (&ctx->mutex);
}
/* Indicate that we have an error or EOF. */
if (!SetEvent (ctx->have_data_ev))
trace (("%p: SetEvent (%p) failed: ec=%d",
ctx, ctx->have_data_ev, (int)GetLastError ()));
trace (("%p: waiting for close", ctx));
WaitForSingleObject (ctx->close_ev, INFINITE);
CloseHandle (ctx->close_ev);
CloseHandle (ctx->have_data_ev);
CloseHandle (ctx->have_space_ev);
CloseHandle (ctx->thread_hd);
DeleteCriticalSection (&ctx->mutex);
free (ctx); /* Standard free! See comment in create_reader. */
return 0;
}
static struct reader_context_s *
create_reader (estream_cookie_w32_pollable_t pcookie)
{
struct reader_context_s *ctx;
SECURITY_ATTRIBUTES sec_attr;
DWORD tid;
memset (&sec_attr, 0, sizeof sec_attr);
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
/* The CTX must be allocated in standard system memory so that we
* won't use any custom allocation handler which may use our lock
* primitives for its implementation. The problem here is that the
* syscall clamp mechanism (e.g. nPth) would be called recursively:
* 1. For example by the caller of _gpgrt_w32_poll and 2. by
* gpgrt_lock_lock on behalf of the the custom allocation and free
* functions. */
ctx = calloc (1, sizeof *ctx);
if (!ctx)
{
return NULL;
}
ctx->pcookie = pcookie;
ctx->have_data_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
if (ctx->have_data_ev)
ctx->have_space_ev = CreateEvent (&sec_attr, FALSE, TRUE, NULL);
if (ctx->have_space_ev)
ctx->close_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
if (!ctx->have_data_ev || !ctx->have_space_ev || !ctx->close_ev)
{
trace (("%p: CreateEvent failed: ec=%d", ctx, (int)GetLastError ()));
if (ctx->have_data_ev)
CloseHandle (ctx->have_data_ev);
if (ctx->have_space_ev)
CloseHandle (ctx->have_space_ev);
if (ctx->close_ev)
CloseHandle (ctx->close_ev);
_gpgrt_free (ctx);
return NULL;
}
InitializeCriticalSection (&ctx->mutex);
#ifdef HAVE_W32CE_SYSTEM
ctx->thread_hd = CreateThread (&sec_attr, 64 * 1024, reader, ctx,
STACK_SIZE_PARAM_IS_A_RESERVATION, &tid);
#else
ctx->thread_hd = CreateThread (&sec_attr, 0, reader, ctx, 0, &tid);
#endif
if (!ctx->thread_hd)
{
trace (("%p: CreateThread failed: ec=%d", ctx, (int)GetLastError ()));
DeleteCriticalSection (&ctx->mutex);
if (ctx->have_data_ev)
CloseHandle (ctx->have_data_ev);
if (ctx->have_space_ev)
CloseHandle (ctx->have_space_ev);
if (ctx->close_ev)
CloseHandle (ctx->close_ev);
_gpgrt_free (ctx);
return NULL;
}
else
{
#if 0
/* We set the priority of the thread higher because we know that
it only runs for a short time. This greatly helps to
increase the performance of the I/O. */
SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ());
#endif
}
return ctx;
}
/* Prepare destruction of the reader thread for CTX. Returns 0 if a
call to this function is sufficient and destroy_reader_finish shall
not be called. */
static void
destroy_reader (struct reader_context_s *ctx)
{
EnterCriticalSection (&ctx->mutex);
ctx->stop_me = 1;
if (ctx->have_space_ev)
SetEvent (ctx->have_space_ev);
LeaveCriticalSection (&ctx->mutex);
#ifdef HAVE_W32CE_SYSTEM
/* Scenario: We never create a full pipe, but already started
reading. Then we need to unblock the reader in the pipe driver
to make our reader thread notice that we want it to go away. */
if (ctx->file_hd != INVALID_HANDLE_VALUE)
{
if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK,
NULL, 0, NULL, 0, NULL, NULL))
{
trace (("%p: unblock control call failed: ec=%d",
ctx, (int)GetLastError ()));
}
}
#endif
/* XXX is it feasible to unblock the thread? */
/* After setting this event CTX is void. */
SetEvent (ctx->close_ev);
}
/*
* Read function for pollable objects.
*/
static gpgrt_ssize_t
func_w32_pollable_read (void *cookie, void *buffer, size_t count)
{
estream_cookie_w32_pollable_t pcookie = cookie;
gpgrt_ssize_t nread;
struct reader_context_s *ctx;
trace (("%p: enter buffer=%p count=%u", cookie, buffer, count));
/* FIXME: implement pending check if COUNT==0 */
ctx = pcookie->reader;
if (ctx == NULL)
{
pcookie->reader = ctx = create_reader (pcookie);
if (!ctx)
{
_gpg_err_set_errno (EBADF);
nread = -1;
goto leave;
}
trace (("%p: new reader %p", cookie, pcookie->reader));
}
if (ctx->eof_shortcut)
{
nread = 0;
goto leave;
}
EnterCriticalSection (&ctx->mutex);
trace (("%p: readpos: %d, writepos %d", cookie, ctx->readpos, ctx->writepos));
if (ctx->readpos == ctx->writepos && !ctx->error)
{
/* No data available. */
int eof = ctx->eof;
LeaveCriticalSection (&ctx->mutex);
if (pcookie->modeflags & O_NONBLOCK && ! eof)
{
_gpg_err_set_errno (EAGAIN);
nread = -1;
goto leave;
}
trace (("%p: waiting for data", cookie));
WaitForSingleObject (ctx->have_data_ev, INFINITE);
trace (("%p: data available", cookie));
EnterCriticalSection (&ctx->mutex);
}
if (ctx->readpos == ctx->writepos || ctx->error)
{
LeaveCriticalSection (&ctx->mutex);
ctx->eof_shortcut = 1;
if (ctx->eof)
return 0;
if (!ctx->error)
{
trace (("%p: EOF but ctx->eof flag not set", cookie));
nread = 0;
goto leave;
}
_gpg_err_set_errno (ctx->error_code);
return -1;
}
nread = ctx->readpos < ctx->writepos
? ctx->writepos - ctx->readpos
: READBUF_SIZE - ctx->readpos;
if (nread > count)
nread = count;
memcpy (buffer, ctx->buffer + ctx->readpos, nread);
ctx->readpos = (ctx->readpos + nread) % READBUF_SIZE;
if (ctx->readpos == ctx->writepos && !ctx->eof)
{
if (!ResetEvent (ctx->have_data_ev))
{
trace (("%p: ResetEvent failed: ec=%d",
cookie, (int)GetLastError ()));
LeaveCriticalSection (&ctx->mutex);
/* FIXME: Should translate the error code. */
_gpg_err_set_errno (EIO);
nread = -1;
goto leave;
}
}
if (!SetEvent (ctx->have_space_ev))
{
trace (("%p: SetEvent (%p) failed: ec=%d",
cookie, ctx->have_space_ev, (int)GetLastError ()));
LeaveCriticalSection (&ctx->mutex);
/* FIXME: Should translate the error code. */
_gpg_err_set_errno (EIO);
nread = -1;
goto leave;
}
LeaveCriticalSection (&ctx->mutex);
leave:
trace_errno (nread==-1,("%p: leave nread=%d", cookie, (int)nread));
return nread;
}
/* The writer does use a simple buffering strategy so that we are
informed about write errors as soon as possible (i. e. with the the
next call to the write function. */
static DWORD CALLBACK
writer (void *arg)
{
struct writer_context_s *ctx = arg;
ssize_t nwritten;
trace (("%p: writer starting", ctx));
for (;;)
{
EnterCriticalSection (&ctx->mutex);
if (ctx->stop_me && !ctx->nbytes)
{
LeaveCriticalSection (&ctx->mutex);
break;
}
if (!ctx->nbytes)
{
if (!SetEvent (ctx->is_empty))
trace (("%p: SetEvent failed: ec=%d", ctx, (int)GetLastError ()));
if (!ResetEvent (ctx->have_data))
trace (("%p: ResetEvent failed: ec=%d", ctx, (int)GetLastError ()));
LeaveCriticalSection (&ctx->mutex);
trace (("%p: idle", ctx));
WaitForSingleObject (ctx->have_data, INFINITE);
trace (("%p: got data to write", ctx));
EnterCriticalSection (&ctx->mutex);
}
if (ctx->stop_me && !ctx->nbytes)
{
LeaveCriticalSection (&ctx->mutex);
break;
}
LeaveCriticalSection (&ctx->mutex);
trace (("%p: writing up to %d bytes", ctx, ctx->nbytes));
nwritten = ctx->pcookie->next_functions.public.func_write
(ctx->pcookie->next_cookie, ctx->buffer, ctx->nbytes);
trace (("%p: wrote %d bytes", ctx, nwritten));
if (nwritten < 1)
{
/* XXX */
if (errno == ERROR_BUSY)
{
/* Probably stop_me is set now. */
trace (("%p: pipe busy (unblocked?)", ctx));
continue;
}
ctx->error_code = errno;
ctx->error = 1;
trace (("%p: write error: ec=%d", ctx, ctx->error_code));
break;
}
EnterCriticalSection (&ctx->mutex);
ctx->nbytes -= nwritten;
LeaveCriticalSection (&ctx->mutex);
}
/* Indicate that we have an error. */
if (!SetEvent (ctx->is_empty))
trace (("%p: SetEvent failed: ec=%d", ctx, (int)GetLastError ()));
trace (("%p: waiting for close", ctx));
WaitForSingleObject (ctx->close_ev, INFINITE);
if (ctx->nbytes)
trace (("%p: still %d bytes in buffer at close time", ctx, ctx->nbytes));
CloseHandle (ctx->close_ev);
CloseHandle (ctx->have_data);
CloseHandle (ctx->is_empty);
CloseHandle (ctx->thread_hd);
DeleteCriticalSection (&ctx->mutex);
trace (("%p: writer is destroyed", ctx));
free (ctx); /* Standard free! See comment in create_writer. */
return 0;
}
static struct writer_context_s *
create_writer (estream_cookie_w32_pollable_t pcookie)
{
struct writer_context_s *ctx;
SECURITY_ATTRIBUTES sec_attr;
DWORD tid;
memset (&sec_attr, 0, sizeof sec_attr);
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
/* See comment at create_reader. */
ctx = calloc (1, sizeof *ctx);
if (!ctx)
{
return NULL;
}
ctx->pcookie = pcookie;
ctx->have_data = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
if (ctx->have_data)
ctx->is_empty = CreateEvent (&sec_attr, TRUE, TRUE, NULL);
if (ctx->is_empty)
ctx->close_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
if (!ctx->have_data || !ctx->is_empty || !ctx->close_ev)
{
trace (("%p: CreateEvent failed: ec=%d", ctx, (int)GetLastError ()));
if (ctx->have_data)
CloseHandle (ctx->have_data);
if (ctx->is_empty)
CloseHandle (ctx->is_empty);
if (ctx->close_ev)
CloseHandle (ctx->close_ev);
_gpgrt_free (ctx);
return NULL;
}
InitializeCriticalSection (&ctx->mutex);
#ifdef HAVE_W32CE_SYSTEM
ctx->thread_hd = CreateThread (&sec_attr, 64 * 1024, writer, ctx,
STACK_SIZE_PARAM_IS_A_RESERVATION, &tid);
#else
ctx->thread_hd = CreateThread (&sec_attr, 0, writer, ctx, 0, &tid );
#endif
if (!ctx->thread_hd)
{
trace (("%p: CreateThread failed: ec=%d", ctx, (int)GetLastError ()));
DeleteCriticalSection (&ctx->mutex);
if (ctx->have_data)
CloseHandle (ctx->have_data);
if (ctx->is_empty)
CloseHandle (ctx->is_empty);
if (ctx->close_ev)
CloseHandle (ctx->close_ev);
_gpgrt_free (ctx);
return NULL;
}
else
{
#if 0
/* We set the priority of the thread higher because we know
that it only runs for a short time. This greatly helps to
increase the performance of the I/O. */
SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ());
#endif
}
return ctx;
}
static void
destroy_writer (struct writer_context_s *ctx)
{
trace (("%p: enter pollable_destroy_writer", ctx));
EnterCriticalSection (&ctx->mutex);
trace (("%p: setting stopme", ctx));
ctx->stop_me = 1;
if (ctx->have_data)
SetEvent (ctx->have_data);
LeaveCriticalSection (&ctx->mutex);
trace (("%p: waiting for empty", ctx));
/* Give the writer a chance to flush the buffer. */
WaitForSingleObject (ctx->is_empty, INFINITE);
#ifdef HAVE_W32CE_SYSTEM
/* Scenario: We never create a full pipe, but already started
writing more than the pipe buffer. Then we need to unblock the
writer in the pipe driver to make our writer thread notice that
we want it to go away. */
if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK,
NULL, 0, NULL, 0, NULL, NULL))
{
trace (("%p: unblock control call failed: ec=%d",
ctx, (int)GetLastError ()));
}
#endif
/* After setting this event CTX is void. */
trace (("%p: set close_ev", ctx));
SetEvent (ctx->close_ev);
trace (("%p: leave pollable_destroy_writer", ctx));
}
/*
* Write function for pollable objects.
*/
static gpgrt_ssize_t
func_w32_pollable_write (void *cookie, const void *buffer, size_t count)
{
estream_cookie_w32_pollable_t pcookie = cookie;
struct writer_context_s *ctx = pcookie->writer;
int nwritten;
trace (("%p: enter buffer: %p count: %d", cookie, buffer, count));
if (count == 0)
{
nwritten = 0;
goto leave;
}
if (ctx == NULL)
{
pcookie->writer = ctx = create_writer (pcookie);
if (!ctx)
{
nwritten = -1;
goto leave;
}
trace (("%p: new writer %p", cookie, pcookie->writer));
}
EnterCriticalSection (&ctx->mutex);
trace (("%p: buffer: %p, count: %d, nbytes: %d",
cookie, buffer, count, ctx->nbytes));
if (!ctx->error && ctx->nbytes)
{
/* Bytes are pending for send. */
/* Reset the is_empty event. Better safe than sorry. */
if (!ResetEvent (ctx->is_empty))
{
trace (("%p: ResetEvent failed: ec=%d",
cookie, (int)GetLastError ()));
LeaveCriticalSection (&ctx->mutex);
/* FIXME: Should translate the error code. */
_gpg_err_set_errno (EIO);
nwritten = -1;
goto leave;
}
LeaveCriticalSection (&ctx->mutex);
if (pcookie->modeflags & O_NONBLOCK)
{
trace (("%p: would block", cookie));
_gpg_err_set_errno (EAGAIN);
nwritten = -1;
goto leave;
}
trace (("%p: waiting for empty buffer", cookie));
WaitForSingleObject (ctx->is_empty, INFINITE);
trace (("%p: buffer is empty", cookie));
EnterCriticalSection (&ctx->mutex);
}
if (ctx->error)
{
LeaveCriticalSection (&ctx->mutex);
if (ctx->error_code == ERROR_NO_DATA)
_gpg_err_set_errno (EPIPE);
else
_gpg_err_set_errno (EIO);
nwritten = -1;
goto leave;
}
/* If no error occurred, the number of bytes in the buffer must be
zero. */
gpgrt_assert (!ctx->nbytes);
if (count > WRITEBUF_SIZE)
count = WRITEBUF_SIZE;
memcpy (ctx->buffer, buffer, count);
ctx->nbytes = count;
/* We have to reset the is_empty event early, because it is also
used by the select() implementation to probe the channel. */
if (!ResetEvent (ctx->is_empty))
{
trace (("%p: ResetEvent failed: ec=%d", cookie, (int)GetLastError ()));
LeaveCriticalSection (&ctx->mutex);
/* FIXME: Should translate the error code. */
_gpg_err_set_errno (EIO);
nwritten = -1;
goto leave;
}
if (!SetEvent (ctx->have_data))
{
trace (("%p: SetEvent failed: ec=%d", cookie, (int)GetLastError ()));
LeaveCriticalSection (&ctx->mutex);
/* FIXME: Should translate the error code. */
_gpg_err_set_errno (EIO);
nwritten = -1;
goto leave;
}
LeaveCriticalSection (&ctx->mutex);
nwritten = count;
leave:
trace_errno (nwritten==-1,("%p: leave nwritten=%d", cookie, nwritten));
return nwritten;
}
/* This is the core of _gpgrt_poll. The caller needs to make sure that
* the syscall clamp has been engaged. */
int
_gpgrt_w32_poll (gpgrt_poll_t *fds, size_t nfds, int timeout)
{
HANDLE waitbuf[MAXIMUM_WAIT_OBJECTS];
int waitidx[MAXIMUM_WAIT_OBJECTS];
#ifdef ENABLE_TRACING
char waitinfo[MAXIMUM_WAIT_OBJECTS];
#endif
unsigned int code;
int nwait;
int i;
int any;
int count;
#if 0
restart:
#endif
any = 0;
nwait = 0;
count = 0;
for (i = 0; i < nfds; i++)
{
struct estream_cookie_w32_pollable *pcookie;
if (fds[i].ignore)
continue;
if (fds[i].stream->intern->kind != BACKEND_W32_POLLABLE)
{
/* This stream does not support polling. */
fds[i].got_err = 1;
continue;
}
pcookie = fds[i].stream->intern->cookie;
if (fds[i].want_read || fds[i].want_write)
{
/* XXX: What if one wants read and write, is that supported? */
if (fds[i].want_read)
{
struct reader_context_s *ctx = pcookie->reader;
if (ctx == NULL)
{
pcookie->reader = ctx = create_reader (pcookie);
if (!ctx)
{
/* FIXME: Is the error code appropriate? */
_gpg_err_set_errno (EBADF);
return -1;
}
trace (("%p: new reader %p", pcookie, pcookie->reader));
}
trace (("%p: using reader %p", pcookie, pcookie->reader));
if (nwait >= DIM (waitbuf))
{
trace (("oops: too many objects for WFMO"));
/* FIXME: Should translate the error code. */
_gpg_err_set_errno (EIO);
return -1;
}
waitidx[nwait] = i;
#ifdef ENABLE_TRACING
waitinfo[nwait] = 'r';
#endif /*ENABLE_TRACING*/
waitbuf[nwait++] = ctx->have_data_ev;
any = 1;
}
else if (fds[i].want_write)
{
struct writer_context_s *ctx = pcookie->writer;
if (ctx == NULL)
{
pcookie->writer = ctx = create_writer (pcookie);
if (!ctx)
{
trace (("oops: create writer failed"));
/* FIXME: Is the error code appropriate? */
_gpg_err_set_errno (EBADF);
return -1;
}
trace (("%p: new writer %p", pcookie, pcookie->writer));
}
trace (("%p: using writer %p", pcookie, pcookie->writer));
if (nwait >= DIM (waitbuf))
{
trace (("oops: Too many objects for WFMO"));
/* FIXME: Should translate the error code. */
_gpg_err_set_errno (EIO);
return -1;
}
waitidx[nwait] = i;
#ifdef ENABLE_TRACING
waitinfo[nwait] = 'w';
#endif /*ENABLE_TRACING*/
waitbuf[nwait++] = ctx->is_empty;
any = 1;
}
}
}
#ifdef ENABLE_TRACING
trace_start (("poll on [ "));
for (i = 0; i < nwait; i++)
trace_append (("%d/%c ", waitidx[i], waitinfo[i]));
trace_finish (("]"));
#endif /*ENABLE_TRACING*/
if (!any)
return 0;
code = WaitForMultipleObjects (nwait, waitbuf, 0,
timeout == -1 ? INFINITE : timeout);
if (code < WAIT_OBJECT_0 + nwait)
{
/* This WFMO is a really silly function: It does return either
the index of the signaled object or if 2 objects have been
signalled at the same time, the index of the object with the
lowest object is returned - so and how do we find out how
many objects have been signaled???. The only solution I can
imagine is to test each object starting with the returned
index individually - how dull. */
any = 0;
for (i = code - WAIT_OBJECT_0; i < nwait; i++)
{
if (WaitForSingleObject (waitbuf[i], 0) == WAIT_OBJECT_0)
{
gpgrt_assert (waitidx[i] >=0 && waitidx[i] < nfds);
/* XXX: What if one wants read and write, is that
supported? */
if (fds[waitidx[i]].want_read)
fds[waitidx[i]].got_read = 1;
else if (fds[waitidx[i]].want_write)
fds[waitidx[i]].got_write = 1;
any = 1;
count++;
}
}
if (!any)
{
trace (("no signaled objects found after WFMO"));
count = -1;
}
}
else if (code == WAIT_TIMEOUT)
trace (("WFMO timed out"));
else if (code == WAIT_FAILED)
{
trace (("WFMO failed: ec=%d", (int)GetLastError ()));
#if 0
if (GetLastError () == ERROR_INVALID_HANDLE)
{
int k;
int j = handle_to_fd (waitbuf[i]);
trace (("WFMO invalid handle %d removed", j));
for (k = 0 ; k < nfds; k++)
{
if (fds[k].fd == j)
{
fds[k].want_read = fds[k].want_write = 0;
goto restart;
}
}
trace ((" oops, or not???"));
}
#endif
count = -1;
}
else
{
trace (("WFMO returned %u", code));
count = -1;
}
if (count > 0)
{
trace_start (("poll OK [ "));
for (i = 0; i < nfds; i++)
{
if (fds[i].ignore)
continue;
if (fds[i].got_read || fds[i].got_write)
trace_append (("%c%d ", fds[i].want_read ? 'r' : 'w', i));
}
trace_finish (("]"));
}
if (count < 0)
{
/* FIXME: Should determine a proper error code. */
_gpg_err_set_errno (EIO);
}
return count;
}
/*
* Implementation of pollable I/O on Windows.
*/
/*
* Constructor for pollable objects.
*/
int
_gpgrt_w32_pollable_create (void *_GPGRT__RESTRICT *_GPGRT__RESTRICT cookie,
unsigned int modeflags,
struct cookie_io_functions_s next_functions,
void *next_cookie)
{
estream_cookie_w32_pollable_t pcookie;
int err;
pcookie = _gpgrt_malloc (sizeof *pcookie);
if (!pcookie)
err = -1;
else
{
pcookie->modeflags = modeflags;
pcookie->next_functions = next_functions;
pcookie->next_cookie = next_cookie;
pcookie->reader = NULL;
pcookie->writer = NULL;
*cookie = pcookie;
err = 0;
}
trace_errno (err,("cookie=%p", *cookie));
return err;
}
/*
* Seek function for pollable objects.
*/
static int
func_w32_pollable_seek (void *cookie, gpgrt_off_t *offset, int whence)
{
estream_cookie_w32_pollable_t pcookie = cookie;
(void) pcookie;
(void) offset;
(void) whence;
/* XXX */
_gpg_err_set_errno (EOPNOTSUPP);
return -1;
}
/*
* The IOCTL function for pollable objects.
*/
static int
func_w32_pollable_ioctl (void *cookie, int cmd, void *ptr, size_t *len)
{
estream_cookie_w32_pollable_t pcookie = cookie;
cookie_ioctl_function_t func_ioctl = pcookie->next_functions.func_ioctl;
if (cmd == COOKIE_IOCTL_NONBLOCK)
{
if (ptr)
pcookie->modeflags |= O_NONBLOCK;
else
pcookie->modeflags &= ~O_NONBLOCK;
return 0;
}
if (func_ioctl)
return func_ioctl (pcookie->next_cookie, cmd, ptr, len);
_gpg_err_set_errno (EOPNOTSUPP);
return -1;
}
/*
* The destroy function for pollable objects.
*/
static int
func_w32_pollable_destroy (void *cookie)
{
estream_cookie_w32_pollable_t pcookie = cookie;
if (cookie)
{
if (pcookie->reader)
destroy_reader (pcookie->reader);
if (pcookie->writer)
destroy_writer (pcookie->writer);
pcookie->next_functions.public.func_close (pcookie->next_cookie);
_gpgrt_free (pcookie);
}
return 0;
}
/*
* Access object for the pollable functions.
*/
struct cookie_io_functions_s _gpgrt_functions_w32_pollable =
{
{
func_w32_pollable_read,
func_w32_pollable_write,
func_w32_pollable_seek,
func_w32_pollable_destroy,
},
func_w32_pollable_ioctl,
};