@@ -923,6 +923,170 @@ static FnCallResult FnCallGetUsers(ARG_UNUSED EvalContext *ctx, ARG_UNUSED const
923
923
924
924
/*********************************************************************/
925
925
926
+
927
+ #if defined(HAVE_GETPWENT ) && !defined(__ANDROID__ )
928
+
929
+ static FnCallResult FnCallFindLocalUsers (EvalContext * ctx , ARG_UNUSED const Policy * policy , const FnCall * fp , const Rlist * finalargs )
930
+ {
931
+ assert (fp != NULL );
932
+ bool allocated = false;
933
+ JsonElement * json = VarNameOrInlineToJson (ctx , fp , finalargs , false, & allocated );
934
+
935
+ // we failed to produce a valid JsonElement, so give up
936
+ if (json == NULL )
937
+ {
938
+ Log (LOG_LEVEL_ERR , "Function '%s' couldn't parse argument '%s'" ,
939
+ fp -> name , RlistScalarValueSafe (finalargs ));
940
+ return FnFailure ();
941
+ }
942
+ else if (JsonGetElementType (json ) != JSON_ELEMENT_TYPE_CONTAINER )
943
+ {
944
+ Log (LOG_LEVEL_ERR , "Bad argument '%s' in function '%s': Expected data container or slist" ,
945
+ RlistScalarValueSafe (finalargs ), fp -> name );
946
+ JsonDestroyMaybe (json , allocated );
947
+ return FnFailure ();
948
+ }
949
+
950
+ JsonElement * parent = JsonObjectCreate (10 );
951
+ setpwent ();
952
+ struct passwd * pw ;
953
+ while ((pw = getpwent ()) != NULL )
954
+ {
955
+ JsonIterator iter = JsonIteratorInit (json );
956
+ bool can_add_to_json = true;
957
+ JsonElement * element = JsonIteratorNextValue (& iter );
958
+ while (element != NULL )
959
+ {
960
+ if (JsonGetElementType (element ) != JSON_ELEMENT_TYPE_PRIMITIVE )
961
+ {
962
+ Log (LOG_LEVEL_ERR , "Bad argument '%s' in function '%s': Filter cannot include nested data" ,
963
+ RlistScalarValueSafe (finalargs ), fp -> name );
964
+ JsonDestroyMaybe (json , allocated );
965
+ JsonDestroy (parent );
966
+ return FnFailure ();
967
+ }
968
+ const char * field = JsonPrimitiveGetAsString (element );
969
+ const Rlist * tuple = RlistFromSplitString (field , '=' );
970
+ assert (tuple != NULL );
971
+ const char * attribute = TrimWhitespace (RlistScalarValue (tuple ));
972
+
973
+ if (tuple -> next == NULL )
974
+ {
975
+ Log (LOG_LEVEL_ERR , "Invalid filter field '%s' in function '%s': Expected attributes and values to be separated with '='" ,
976
+ fp -> name , field );
977
+ JsonDestroyMaybe (json , allocated );
978
+ JsonDestroy (parent );
979
+ return FnFailure ();
980
+ }
981
+ const char * value = TrimWhitespace (RlistScalarValue (tuple -> next ));
982
+
983
+ if (StringEqual (attribute , "name" ))
984
+ {
985
+ if (!StringMatchFull (value , pw -> pw_name ))
986
+ {
987
+ can_add_to_json = false;
988
+ }
989
+ }
990
+ else if (StringEqual (attribute , "uid" ))
991
+ {
992
+ char uid_string [PRINTSIZE (pw -> pw_uid )];
993
+ int ret = snprintf (uid_string , sizeof (uid_string ), "%u" , pw -> pw_uid );
994
+
995
+ if (ret < 0 )
996
+ {
997
+ Log (LOG_LEVEL_ERR , "Couldn't convert the uid of '%s' to string in function '%s'" ,
998
+ pw -> pw_name , fp -> name );
999
+ JsonDestroyMaybe (json , allocated );
1000
+ JsonDestroy (parent );
1001
+ return FnFailure ();
1002
+ }
1003
+ assert ((size_t ) ret < sizeof (uid_string ));
1004
+
1005
+ if (!StringMatchFull (value , uid_string ))
1006
+ {
1007
+ can_add_to_json = false;
1008
+ }
1009
+ }
1010
+ else if (StringEqual (attribute , "gid" ))
1011
+ {
1012
+ char gid_string [PRINTSIZE (pw -> pw_uid )];
1013
+ int ret = snprintf (gid_string , sizeof (gid_string ), "%u" , pw -> pw_uid );
1014
+
1015
+ if (ret < 0 )
1016
+ {
1017
+ Log (LOG_LEVEL_ERR , "Couldn't convert the gid of '%s' to string in function '%s'" ,
1018
+ pw -> pw_name , fp -> name );
1019
+ JsonDestroyMaybe (json , allocated );
1020
+ JsonDestroy (parent );
1021
+ return FnFailure ();
1022
+ }
1023
+ assert ((size_t ) ret < sizeof (gid_string ));
1024
+
1025
+ if (!StringMatchFull (value , gid_string ))
1026
+ {
1027
+ can_add_to_json = false;
1028
+ }
1029
+ }
1030
+ else if (StringEqual (attribute , "gecos" ))
1031
+ {
1032
+ if (!StringMatchFull (value , pw -> pw_gecos ))
1033
+ {
1034
+ can_add_to_json = false;
1035
+ }
1036
+ }
1037
+ else if (StringEqual (attribute , "dir" ))
1038
+ {
1039
+ if ((!StringMatchFull (value , pw -> pw_dir )))
1040
+ {
1041
+ can_add_to_json = false;
1042
+ }
1043
+ }
1044
+ else if (StringEqual (attribute , "shell" ))
1045
+ {
1046
+ if (!StringMatchFull (value , pw -> pw_shell ))
1047
+ {
1048
+ can_add_to_json = false;
1049
+ }
1050
+ }
1051
+ else
1052
+ {
1053
+ Log (LOG_LEVEL_ERR , "Invalid attribute '%s' in function '%s': not supported" ,
1054
+ attribute , fp -> name );
1055
+ JsonDestroyMaybe (json , allocated );
1056
+ JsonDestroy (parent );
1057
+ return FnFailure ();
1058
+ }
1059
+ element = JsonIteratorNextValue (& iter );
1060
+ }
1061
+ if (can_add_to_json )
1062
+ {
1063
+ JsonElement * child = JsonObjectCreate (6 );
1064
+ JsonObjectAppendInteger (child , "uid" , pw -> pw_uid );
1065
+ JsonObjectAppendInteger (child , "gid" , pw -> pw_gid );
1066
+ JsonObjectAppendString (child , "gecos" , pw -> pw_gecos );
1067
+ JsonObjectAppendString (child , "dir" , pw -> pw_dir );
1068
+ JsonObjectAppendString (child , "shell" , pw -> pw_shell );
1069
+ JsonObjectAppendObject (parent , pw -> pw_name , child );
1070
+ }
1071
+ }
1072
+ endpwent ();
1073
+ JsonDestroyMaybe (json , allocated );
1074
+
1075
+ return FnReturnContainerNoCopy (parent );
1076
+ }
1077
+
1078
+ #else
1079
+
1080
+ static FnCallResult FnCallFindLocalUsers (ARG_UNUSED EvalContext * ctx , ARG_UNUSED const Policy * policy , ARG_UNUSED const FnCall * fp , ARG_UNUSED const Rlist * finalargs )
1081
+ {
1082
+ Log (LOG_LEVEL_ERR , "findlocalusers is not implemented" );
1083
+ return FnFailure ();
1084
+ }
1085
+
1086
+ #endif
1087
+
1088
+ /*********************************************************************/
1089
+
926
1090
static FnCallResult FnCallEscape (ARG_UNUSED EvalContext * ctx , ARG_UNUSED const Policy * policy , ARG_UNUSED const FnCall * fp , const Rlist * finalargs )
927
1091
{
928
1092
char buffer [CF_BUFSIZE ];
@@ -10467,7 +10631,11 @@ static const FnCallArg IS_DATATYPE_ARGS[] =
10467
10631
{CF_ANYSTRING , CF_DATA_TYPE_STRING , "Type" },
10468
10632
{NULL , CF_DATA_TYPE_NONE , NULL }
10469
10633
};
10470
-
10634
+ static const FnCallArg FIND_LOCAL_USERS_ARGS [] =
10635
+ {
10636
+ {CF_ANYSTRING , CF_DATA_TYPE_STRING , "Filter list" },
10637
+ {NULL , CF_DATA_TYPE_NONE , NULL }
10638
+ };
10471
10639
10472
10640
/*********************************************************/
10473
10641
/* FnCalls are rvalues in certain promise constraints */
@@ -10803,6 +10971,8 @@ const FnCallType CF_FNCALL_TYPES[] =
10803
10971
FNCALL_OPTION_VARARG , FNCALL_CATEGORY_DATA , SYNTAX_STATUS_NORMAL ),
10804
10972
FnCallTypeNew ("version_compare" , CF_DATA_TYPE_CONTEXT , VERSION_COMPARE_ARGS , & FnCallVersionCompare , "Compare two version numbers with a specified operator" ,
10805
10973
FNCALL_OPTION_NONE , FNCALL_CATEGORY_UTILS , SYNTAX_STATUS_NORMAL ),
10974
+ FnCallTypeNew ("findlocalusers" , CF_DATA_TYPE_CONTAINER , FIND_LOCAL_USERS_ARGS , & FnCallFindLocalUsers , "Find matching local users" ,
10975
+ FNCALL_OPTION_VARARG , FNCALL_CATEGORY_DATA , SYNTAX_STATUS_NORMAL ),
10806
10976
10807
10977
// Functions section following new naming convention
10808
10978
FnCallTypeNew ("string_mustache" , CF_DATA_TYPE_STRING , STRING_MUSTACHE_ARGS , & FnCallStringMustache , "Expand a Mustache template from arg1 into a string using the optional data container in arg2 or datastate()" ,
0 commit comments