Hallo, dies ist ein Test.
PWD: /www/data-lst1/unixsoft/unixsoft/kaempfer/.public_html
Running in File Mode
Relative path: ./../../../../../../usr/include/suri_impl.h
Real path: /usr/include/suri_impl.h
Zurück
/* * Copyright (c) 2012, 2023, Oracle and/or its affiliates. */ #ifndef _SURI_IMPL_H #define _SURI_IMPL_H /* * This is an internal libsuri header file. Should be never included from * outside of libsuri itself. */ #ifdef __cplusplus extern "C" { #endif #include <sys/param.h> #include <netinet/in.h> #include <netdb.h> /* Needed for ima.h */ #define SOLARIS 1 #include <ima.h> #include <suri.h> #include <sys/stat.h> #include <sys/mount.h> #include <lofi.h> /* Convert a number to a string */ #define SURI_STR_VALUE(arg) #arg #define SURI_NAME(name) SURI_STR_VALUE(name) /* * Default flags for file/NFS based SURI_OP_CREATE(). Flags for open can * never be changed. Files are created with mode 600. */ #define SURI_CREATE_OPEN_FLAGS (O_WRONLY | O_CREAT | O_EXCL) #define SURI_CREATE_OPEN_MODE (S_IRUSR | S_IWUSR) /* * Flags for NFS based SURI_OP_CREATE(), SURI_OP_MAP(). * Used to create the directories needed for the default mountpoint with * mode of 755. */ #define SURI_MKDIR_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) /* * Defines used for NFS and FILE suris. */ #define SURI_FILE_STRING "file://%s:%s@%s" #define SURI_NFS_STRING "nfs://%s:%s@%s%s%s" #define SURI_ISCSI_DEFAULT_PORT 3260 #define SURI_ISCSI_DEFAULT_PORT_STR SURI_NAME(SURI_ISCSI_DEFAULT_PORT) /* * SunCluster Device ID (DID) block device pathnames * DID devices are considered 'shared' between nodes in a cluster */ #define _PATH_DID_DSK "/dev/did/dsk" #define _PLEN_DID_DSK (sizeof (_PATH_DID_DSK) - 1) /* * Logical unit or target/initiator name type strings. They must all be * 3-character strings. */ #define SURI_EUI "eui" #define SURI_IQN "iqn" #define SURI_NAA "naa" /* Use this to avoid using numbers 3 and 4 in the code */ #define SURI_GTYPE_LEN (3) #define SURI_GTYPE_W_PERIOD_LEN (SURI_GTYPE_LEN + 1) /* Used string for our iSCSI/LU URIs */ #define SURI_INITIATOR "initiator" #define SURI_LUN "lun" #define SURI_LUNAME "luname" #define SURI_TARGET "target" /* Flags for GUIDs. Two bytes should be enough (16 types). */ #define SURI_GF_UNKNOWN 0x0000 #define SURI_GF_EUI 0x0001 #define SURI_GF_IQN 0x0002 #define SURI_GF_NAA 0x0004 /* Flags for prefixes. Leave two lower bytes empty for GUID types. */ #define SURI_GF_INITIATOR 0x010000 #define SURI_GF_LUNAME 0x020000 #define SURI_GF_TARGET 0x040000 #define SURI_MAX_LUN INT_MAX /* for suri_remove_duplicate_handles() */ #define SURI_URI_SORT_NORMAL B_FALSE #define SURI_URI_SORT_REVERSE B_TRUE /* * A common function works with the device tree and needs to know what exactly * it is supposed to do and on which URI type. */ typedef enum suri_lu_iscsi_action { SURI_ISCSI_MAP_LUNAME = 1, SURI_ISCSI_LOOKUP_URI, SURI_LU_MAP, SURI_LU_LOOKUP_URI, SURI_ISCSI_MAP_TARGET_LUN } suri_lu_iscsi_action_t; /* * Services libsuri may temporarily enable. */ #define SURI_ISCSI_INITIATOR_SVC "network/iscsi/initiator:default" #define SURI_CHASSIS_SVC "system/devchassis:daemon" /* * validation macros for public data types */ #define SURI_VALID_STATE(x) \ (((x) > SURI_STATE_INVALID) && ((x) < SURI_STATE_MAX)) #define SURI_VALID_TYPE(x) (((x) == SURI_TYPE_ANY) || \ (((x) > SURI_TYPE_INVALID) && ((x) < SURI_TYPE_MAX))) /* * Regular expression compiled with the parser code together with all other * regular expressions present in libsuri but used from another module. */ extern regex_t suri_re_ctds_name; extern regex_t suri_re_ctds_path; extern regex_t suri_re_ctdp_path; extern regex_t suri_re_ctd_path; extern regex_t suri_re_did_path; extern regex_t suri_re_dids_path; typedef struct suri_dev suri_dev_t; typedef struct suri_file suri_file_t; typedef struct suri_iscsi suri_iscsi_t; typedef struct suri_lu suri_lu_t; typedef struct suri_nfs suri_nfs_t; typedef struct suri_ops suri_ops_t; typedef struct suri_mdev_node suri_mdev_node_t; #define SURIH2DEV(h) ((suri_dev_t *)(h)->sh_data) #define SURIH2FILE(h) ((suri_file_t *)(h)->sh_data) #define SURIH2ISCSI(h) ((suri_iscsi_t *)(h)->sh_data) #define SURIH2LU(h) ((suri_lu_t *)(h)->sh_data) #define SURIH2NFS(h) ((suri_nfs_t *)(h)->sh_data) /* Allow enough space for a long URI or a unix path and a message. */ #define SURI_ERR_STR_LEN (2 * SURI_MAX_URI_LEN) /* Error messages shouldn't be longer than 1/4 of the error string length */ #define SURI_ERRMSG_LEN (SURI_MAX_URI_LEN/4) /* Warnings may contain full URIs so be careful here. */ #define SURI_WARN_STR_LEN 320 /* * The fundamental handle for the consumer to deal with suri objects. */ struct suri_handle { /* URI string */ char sh_uri[SURI_MAX_URI_LEN]; /* URI type of attached shared object */ suri_type_t sh_uri_type; /* * List of mapped devices. Here we store SURI_PROP_RO_MAPPED_DEV * and SURI_PROP_RO_MAPPED_DEVS as the OS device name. */ list_t sh_mdev_list; /* Number of nodes in the mdev list. */ size_t sh_mdev_count; /* Length of the longest string in the mdev list. */ size_t sh_mdev_max_len; /* * Array of mapped devices. This is the data returned to the consumer of * the libsuri by suri_get_mvprop_str(). This array is constructed when * the state of handle becomes SURI_STATE_MAPPED by * suri_set_mapped_state() and it uses data in sh_mdev_list. Thus, it's * illegal to access this data when the state is not SURI_STATE_MAPPED * and sh_mdev_list must not be freed as long as sh_mdev_array is used. */ const char **sh_mdev_array; /* state of attached shared object */ suri_state_t sh_state; /* A pointer to a URI type specific structure with object properties. */ void *sh_data; /* Operations specific to the URI type. */ suri_ops_t *sh_ops; /* * Cached return code for the last operation. Useful with lookup-uri * that can return multiple handles. Use this entry only in public API * functions. */ int sh_err_code; /* * Dynamic action error message on what failed. Is supposed to include * the object being operated on (URI, mapped-path). */ char sh_err_action[SURI_ERR_STR_LEN]; /* * Error message on why it failed. Use either a dynamic message or a * static description string, not both. */ char sh_err_desc[SURI_ERR_STR_LEN]; char *sh_err_static_desc; /* We can have only two warnings at the same time. */ char *sh_warn[3]; char sh_warnings[2][SURI_WARN_STR_LEN]; }; typedef struct suri_gen_uri { char sgu_scheme[SURI_MAX_URI_LEN]; char sgu_authority[SURI_MAX_URI_LEN]; char sgu_path[SURI_MAX_URI_LEN]; char sgu_query[SURI_MAX_URI_LEN]; char sgu_fragment[SURI_MAX_URI_LEN]; } suri_gen_uri_t; /* * List node of mapped devices. */ struct suri_mdev_node { char sh_mdev[MAXPATHLEN]; list_node_t node_info; }; /* * suri specific operations */ struct suri_ops { suri_err_t (*op_parse)(struct suri_handle *, const suri_gen_uri_t *, nvlist_t *); suri_err_t (*op_map)(struct suri_handle *, nvlist_t *); suri_err_t (*op_lookup_mapping)(struct suri_handle *, nvlist_t *); suri_err_t (*op_lookup_uri)(struct suri_handle *, nvlist_t *, struct suri_handle ***); suri_err_t (*op_unmap)(struct suri_handle *, nvlist_t *, boolean_t); boolean_t (*op_prop_valid)(suri_prop_t); boolean_t (*op_prop_defined)(struct suri_handle *, suri_prop_t); const char *(*op_get_prop_str)(struct suri_handle *, suri_prop_t); uint64_t (*op_get_prop_uint64)(struct suri_handle *, suri_prop_t); boolean_t (*op_get_prop_bool)(struct suri_handle *, suri_prop_t); suri_err_t (*op_create)(struct suri_handle *, nvlist_t *); suri_err_t (*op_destroy)(struct suri_handle *, nvlist_t *); suri_err_t (*op_normalize)(struct suri_handle *, nvlist_t *); }; #define SURI_OP_PARSE(h, s, p) (h->sh_ops->op_parse)(h, s, p) #define SURI_OP_MAP(h, p) (h->sh_ops->op_map)(h, p) #define SURI_OP_LOOKUP_MAPPING(h, p) (h->sh_ops->op_lookup_mapping)(h, p) #define SURI_OP_LOOKUP_URI(h, l, ph) (h->sh_ops->op_lookup_uri)(h, l, ph) #define SURI_OP_UNMAP(h, p, t) (h->sh_ops->op_unmap)(h, p, t) #define SURI_OP_PROP_DEFINED(h, prop) \ (h->sh_ops->op_prop_defined)(h, prop) #define SURI_OP_PROP_VALID(h, prop) \ (h->sh_ops->op_prop_valid)(prop) #define SURI_OP_GET_PROP_STR(h, prop) \ (h->sh_ops->op_get_prop_str)(h, prop) #define SURI_OP_GET_PROP_UINT64(h, prop) \ (h->sh_ops->op_get_prop_uint64)(h, prop) #define SURI_OP_GET_PROP_BOOL(h, prop) \ (h->sh_ops->op_get_prop_bool)(h, prop) #define SURI_OP_GET_PROP_SOTYPE(h, t) \ (h->sh_ops->op_get_prop_sotype)(h, t) #define SURI_OP_CREATE(h, p) (h->sh_ops->op_create)(h, p) #define SURI_OP_DESTROY(h, p) (h->sh_ops->op_destroy)(h, p) #define SURI_OP_NORMALIZE(h, p) (h->sh_ops->op_normalize)(h, p) /* * the file URI specific storage object components */ struct suri_file { /* SURI_PROP_RO_PATH, unix path from URI */ char sf_prop_path[MAXPATHLEN]; /* SURI_PROP_RO_USER, user from URI */ char sf_prop_user[LOGNAME_MAX + 1]; /* SURI_PROP_RO_GROUP, group from URI */ char sf_prop_group[LOGNAME_MAX + 1]; /* SURI_PROP_RW_CREATE_SIZE */ char sf_prop_create_size[SURI_MAX_UINT64_LEN]; /* SURI_PROP_RW_FILE_PERMS */ uint64_t sf_prop_file_perms; /* SURI_PROP_RW_REMOVABLE */ boolean_t sf_prop_removable; /* * These properties are for internal purpose only and are not exposed * to the consumer. sf_uid/gid is the uid/gid derived from the URI. */ uid_t sf_uid; gid_t sf_gid; }; /* * the NFS URI specific storage object components */ struct suri_nfs { /* SURI_PROP_RO_PATH, unix path from URI, nfs path */ char sn_prop_path[MAXPATHLEN]; /* SURI_PROP_RO_USER, user from URI */ char sn_prop_user[LOGNAME_MAX + 1]; /* SURI_PROP_RO_GROUP, group from URI */ char sn_prop_group[LOGNAME_MAX + 1]; /* SURI_PROP_RW_CREATE_SIZE */ char sn_prop_create_size[SURI_MAX_UINT64_LEN]; /* SURI_PROP_RW_FILE_PERMS */ uint64_t sn_prop_file_perms; /* SURI_PROP_RO_HOSTNAME, nfs hostname */ char sn_prop_hname[NI_MAXHOST]; /* SURI_PROP_RO_MOUNTPOINT */ char sn_prop_mountpoint[MAXPATHLEN]; /* SURI_PROP_RO_PORT, nfs port */ in_port_t sn_prop_port; /* SURI_PROP_RW_MOUNTPOINT_PREFIX */ char sn_prop_mountpoint_prefix[MAXPATHLEN]; /* SURI_PROP_RW_MOUNT_OPTIONS */ char sn_prop_mount_opts[MAX_MNTOPT_STR]; /* * These properties are for internal purpose only and are not exposed * to the consumer. * sn_share_pathname is the NFS share to be mounted on the client. * sn_mounted_filepath is the absolute path of the file in the * NFS share mounted on the client. * sn_uid and sn_gid are the validated values from user/group. */ char sn_share_pathname[MAXPATHLEN]; char sn_mounted_filepath[MAXPATHLEN]; uid_t sn_uid; gid_t sn_gid; }; /* * EUI-64 ID is 8 bytes and NAA 8 or 16 bytes long. Those GUIDs are always * formatted in hexadecimal digits, so double the string size needed, and we * store them with the "naa." or "eui." prefix and a terminating NUL character. */ #define SURI_MAX_ID_LEN 32 /* One +1 is for ".", the other one is for a terminating NUL. */ #define SURI_MAX_LUNAME_PROP_LEN (SURI_GTYPE_LEN + 1 + SURI_MAX_ID_LEN + 1) /* * When looking up URIs, we may internally pass any target GUID in a target * property of the handle for any URI type. For example, we may look up LU URIs * based on an IQN target if we have a multipathed node with multiple transport * types. So the target property must accommodate all target types. */ #define SURI_MAX_TARGET_PROP_LEN (IMA_NODE_NAME_LEN + 1) #define SURI_MAX_INITIATOR_PROP_LEN (SURI_MAX_LUNAME_PROP_LEN) #if SURI_MAX_TARGET_PROP_LEN < SURI_MAX_LUNAME_PROP_LEN #error "Maximum target length not sufficient to accommodate all target types." #endif /* * The logical unit specific storage object components. */ struct suri_lu { /* SURI_PROP_RO_LUNAME, a GUID with a ".<3-char-type>" prefix */ char sl_prop_luname[SURI_MAX_LUNAME_PROP_LEN]; /* SURI_PROP_RO_INITIATOR, a GUID with a ".<3-char-type>" prefix */ char sl_prop_initiator[SURI_MAX_INITIATOR_PROP_LEN]; /* SURI_PROP_RO_TARGET, a GUID with a ".<3-char-type>" prefix */ char sl_prop_target[SURI_MAX_TARGET_PROP_LEN]; /* * Internal only property used in the look-up URI code. You cannot get * this value via suri_get_prop_uint64(). */ uint64_t sl_prop_lun; }; /* * We borrow the maximum IQN length from libima but assert its value here * as max URI length is 1024 and if in the future we wish to have a URI that * contains a target and initiator IQN each cannot be greater than 512 */ #if IMA_NODE_NAME_LEN > 512 #error "Maximum IQN length cannot exceed 512. Libsuri conflicts with libima." #endif /* * The iSCSI target specific storage object components. */ struct suri_iscsi { char si_prop_hname[NI_MAXHOST]; /* target hostname */ in_port_t si_prop_port; /* target port */ /* SURI_PROP_RO_LUNAME. See suri_lu for more information. */ char si_prop_luname[SURI_MAX_LUNAME_PROP_LEN]; /* SURI_PROP_RO_TARGET. See suri_lu for more information. */ char si_prop_target[SURI_MAX_TARGET_PROP_LEN]; /* SURI_PROP_RO_LUN. Logical Unit Number of logical unit on target */ uint64_t si_prop_lun; }; /* * the dev URI specific storage object components */ struct suri_dev { /* SURI_PROP_RO_PATH, unix path from URI */ char sd_prop_path[MAXPATHLEN]; boolean_t sd_is_slice; boolean_t sd_is_shared; }; /* For parsing URIs. */ suri_err_t suri_get_uri_type(struct suri_handle *, suri_gen_uri_t *); suri_err_t suri_dev_parse(struct suri_handle *, const suri_gen_uri_t *, nvlist_t *); suri_err_t suri_file_parse(struct suri_handle *, const suri_gen_uri_t *, nvlist_t *); suri_err_t suri_lu_parse(struct suri_handle *, const suri_gen_uri_t *, nvlist_t *); suri_err_t suri_iscsi_parse(struct suri_handle *, const suri_gen_uri_t *, nvlist_t *); suri_err_t suri_nfs_parse(struct suri_handle *, const suri_gen_uri_t *, nvlist_t *); /* For normalizing URI's */ suri_err_t suri_dev_normalize(struct suri_handle *, nvlist_t *); suri_err_t suri_file_normalize(struct suri_handle *, nvlist_t *); suri_err_t suri_lu_iscsi_normalize(struct suri_handle *, nvlist_t *); suri_err_t suri_nfs_normalize(struct suri_handle *, nvlist_t *); void suri_normalize_usergroup(struct suri_handle *); void suri_normalize_host_port(struct suri_handle *, char *, size_t, in_port_t *); /* For mapping fibre channel and SAS shared objects. */ suri_err_t devinfo_target_mapping(struct suri_handle *, suri_lu_iscsi_action_t, struct suri_handle ***); /* For mapping iSCSI shared objects. */ suri_err_t suri_resolve_hostname(struct suri_handle *, const char *, struct addrinfo **, char *, size_t); suri_err_t suri_iscsi_add_disc_addrs(struct suri_handle *, struct addrinfo *); suri_err_t suri_get_discovery_addresses(struct suri_handle *, IMA_OID_LIST **); suri_err_t suri_get_auth_section_for_iscsi_tgt(struct suri_handle *, const char *, IMA_OID); suri_err_t suri_get_devname_for_iqn_lun(struct suri_handle *, const char *, size_t, char *, size_t); suri_err_t suri_get_iqn_lun_for_devpath(struct suri_handle *, const char *, char **, uint64_t *); /* Auxiliary functions */ suri_err_t suri_enable_service(struct suri_handle *h, char *); suri_err_t suri_enforce_real_devpath(struct suri_handle *, const char *, boolean_t); suri_err_t suri_enforce_block_dev_or_chassis(struct suri_handle *, const char *, boolean_t); struct suri_handle **suri_expand_handle_array(struct suri_handle **, int *); suri_err_t suri_clone(struct suri_handle *, struct suri_handle **); void suri_destroy_handle_array(struct suri_handle ***); int suri_get_handle_count(struct suri_handle **); void suri_remove_duplicate_handles(struct suri_handle **, boolean_t); boolean_t suri_ctd_path(const char *); boolean_t suri_ctds_path(const char *); boolean_t suri_ctdp_path(const char *); boolean_t suri_did_path(const char *); boolean_t suri_dids_path(const char *); boolean_t suri_devchassis_path(const char *); suri_err_t suri_check_lun(struct suri_handle *, const char *, uint64_t *); suri_err_t check_known_prefix_guid(struct suri_handle *, const char *, int, const char *); suri_err_t suri_validate_usergroup(struct suri_handle *, lofi_usergroup_t *); suri_err_t suri_convert_errno(int err); boolean_t suri_isnumber(const char *); suri_err_t suri_regular_file_check(struct suri_handle *, const char *); suri_err_t suri_get_create_size(struct suri_handle *, uint64_t *, mode_t *); suri_err_t suri_setid_stat(struct suri_handle *, struct stat64 *, uid_t, gid_t, const char *); suri_err_t suri_validate_set_props(struct suri_handle *, nvlist_t *, suri_prop_t *); suri_err_t suri_required_props(struct suri_handle *, nvlist_t *, suri_prop_t *); suri_err_t suri_nvprop_get_mountopts(struct suri_handle *, nvpair_t *, char *, size_t); suri_err_t suri_lofi_lookup_cmp(struct suri_handle *, struct suri_handle **); void suri_set_remap_props(struct suri_handle *, struct suri_handle *); void suri_get_uid_gid(struct suri_handle *, uid_t *, gid_t *); char *suri_add_p0_to_devpath(const char *); /* Internal interface for mapped devices. */ boolean_t suri_mdev_exist(struct suri_handle *); const char *suri_get_first_mdev(struct suri_handle *); suri_err_t suri_add_mdev(struct suri_handle *, const char *); suri_err_t suri_mdev_list_replace(struct suri_handle *, const char *); suri_err_t suri_set_mapped_state(struct suri_handle *); /* Lofi related functions */ suri_err_t suri_lofi_map(struct suri_handle *); suri_err_t suri_lofi_lookup(struct suri_handle *, const char *); suri_err_t suri_lofi_lookup_uri(struct suri_handle *); suri_err_t suri_lofi_unmap(struct suri_handle *, const char *); suri_err_t suri_lofi_teardown(struct suri_handle *, const char *); suri_err_t suri_lofi_enfor_assoc_ftype(struct suri_handle *, const char *, lofi_info_t *, boolean_t); void suri_lofi_free_usr_grp(lofi_info_t *); /* dev URI functions */ suri_err_t suri_get_compdev_for_chassis(struct suri_handle *, const char *); suri_err_t suri_get_chassis_for_compdev(struct suri_handle *, char **); /* Error functions */ void suri_err_set_action(struct suri_handle *, const char *fmt, ...); void suri_err_set_desc(struct suri_handle *, const char *fmt, ...); suri_err_t suri_err_set_static_desc(struct suri_handle *, suri_err_t); void suri_set_warn(struct suri_handle *, const char *, ...); void suri_err_clear(struct suri_handle *); boolean_t suri_err_check_null_warnings(struct suri_handle *); boolean_t suri_err_check_null_errors(struct suri_handle *); /* file URI functions */ suri_err_t suri_file_map(struct suri_handle *, nvlist_t *); suri_err_t suri_file_lookup_mapping(struct suri_handle *, nvlist_t *); suri_err_t suri_file_lookup_uri(struct suri_handle *, nvlist_t *, struct suri_handle ***); suri_err_t suri_file_unmap(struct suri_handle *, nvlist_t *, boolean_t); suri_err_t suri_file_create(struct suri_handle *, nvlist_t *); suri_err_t suri_file_destroy(struct suri_handle *, nvlist_t *); boolean_t suri_file_prop_valid(suri_prop_t); boolean_t suri_file_prop_defined(struct suri_handle *, suri_prop_t); const char *suri_file_get_prop_str(struct suri_handle *, suri_prop_t); boolean_t suri_file_get_prop_bool(struct suri_handle *, suri_prop_t); uint64_t suri_file_get_prop_uint64(struct suri_handle *, suri_prop_t); boolean_t suri_file_uri_path_valid(const char *); /* NFS URI functions */ suri_err_t suri_nfs_map(struct suri_handle *, nvlist_t *prop); suri_err_t suri_nfs_lookup_mapping(struct suri_handle *, nvlist_t *prop); suri_err_t suri_nfs_lookup_uri(struct suri_handle *, nvlist_t *, struct suri_handle ***); suri_err_t suri_nfs_unmap(struct suri_handle *, nvlist_t *prop, boolean_t); suri_err_t suri_nfs_create(struct suri_handle *, nvlist_t *prop); suri_err_t suri_nfs_destroy(struct suri_handle *, nvlist_t *prop); boolean_t suri_nfs_prop_valid(suri_prop_t); boolean_t suri_nfs_prop_defined(struct suri_handle *, suri_prop_t); const char *suri_nfs_get_prop_str(struct suri_handle *, suri_prop_t); boolean_t suri_nfs_get_prop_bool(struct suri_handle *, suri_prop_t); uint64_t suri_nfs_get_prop_uint64(struct suri_handle *, suri_prop_t); suri_err_t suri_nfs_set_prop_mountpoint(struct suri_handle *); boolean_t suri_nfs_libsuri_mountpoint(const char *); /* Setid Callback functions */ typedef struct __suri_setid_cb_arg *suri_setid_cb_arg_t; typedef int (*suri_setid_cb_t)(suri_setid_cb_arg_t arg, size_t arg_size, char *errbuf, size_t errbuf_size); suri_err_t suri_setid_cb(struct suri_handle *, char *, uid_t, gid_t, boolean_t, suri_setid_cb_t, suri_setid_cb_arg_t, size_t); suri_err_t suri_setid_create_file(struct suri_handle *, const char *, uint64_t, mode_t); int suri_setid_cb_destroy_file(suri_setid_cb_arg_t, size_t, char *, size_t); #ifdef __cplusplus } #endif #endif /* _SURI_IMPL_H */