Compare commits
518 Commits
dev-backup
...
dev
| Author | SHA1 | Date |
|---|---|---|
|
|
853f4a4eaf | |
|
|
8e1b8b1992 | |
|
|
57a7c1d584 | |
|
|
685ee48a5e | |
|
|
896e2d737c | |
|
|
2bc0ee4043 | |
|
|
94b5d7c7cf | |
|
|
e9540c7bc5 | |
|
|
125e97d1ca | |
|
|
f9a02eeb20 | |
|
|
8483eb6068 | |
|
|
6f6064e060 | |
|
|
269482a578 | |
|
|
7c122154fc | |
|
|
63aa5e82f6 | |
|
|
4325564119 | |
|
|
95f7ae14e4 | |
|
|
fa14d158fe | |
|
|
63486b57f2 | |
|
|
9164c02b25 | |
|
|
cc42dd74ae | |
|
|
7d8673be51 | |
|
|
ce70a05082 | |
|
|
a42381793f | |
|
|
6be3254f87 | |
|
|
08da67f884 | |
|
|
99e5730ccf | |
|
|
46a4c04b2a | |
|
|
3ccc8e7469 | |
|
|
b20cb7e892 | |
|
|
733e4f985c | |
|
|
e09eba8530 | |
|
|
df5657181d | |
|
|
52af58e631 | |
|
|
788cbf7a19 | |
|
|
1fd4001293 | |
|
|
5562ae6035 | |
|
|
1a3af6d20c | |
|
|
b82a073fec | |
|
|
bb17aa2c6c | |
|
|
79fa23f209 | |
|
|
56c05a2344 | |
|
|
987b402337 | |
|
|
ebe4be95a5 | |
|
|
e269a22a01 | |
|
|
32c0f5b9a5 | |
|
|
50b592b3fe | |
|
|
00f8a002ea | |
|
|
79e75e648b | |
|
|
ecb135e795 | |
|
|
bf415df190 | |
|
|
c865342582 | |
|
|
0b5c32315a | |
|
|
1237ebced0 | |
|
|
298a400cb7 | |
|
|
881e5a0718 | |
|
|
4a233cc203 | |
|
|
4e154ec370 | |
|
|
55da922107 | |
|
|
697f762467 | |
|
|
27411880e4 | |
|
|
e294794031 | |
|
|
879f87f192 | |
|
|
19cbf65aa7 | |
|
|
05d63e122b | |
|
|
28d196318a | |
|
|
aacdcf4247 | |
|
|
c1043cd9cb | |
|
|
95568fc7a2 | |
|
|
9dcfea4d0e | |
|
|
e1d5945591 | |
|
|
fb9741cefe | |
|
|
6c76485d4a | |
|
|
d2cfe47bdc | |
|
|
065ecd562a | |
|
|
0a05bfce2e | |
|
|
2d17669793 | |
|
|
7b7d54de70 | |
|
|
554c5d8f33 | |
|
|
f97e635d1e | |
|
|
dfe7551c97 | |
|
|
d39dba9749 | |
|
|
64cdbb0082 | |
|
|
2964ea5fec | |
|
|
88318136df | |
|
|
772d24b82a | |
|
|
3a2b80e70c | |
|
|
543068f3f7 | |
|
|
8c3b818510 | |
|
|
51a5d795b1 | |
|
|
dce0c0b948 | |
|
|
c9ad5a7144 | |
|
|
54d51b4cf7 | |
|
|
8385a1c0e4 | |
|
|
84b1f70032 | |
|
|
c3e49a53e7 | |
|
|
cc3dd7c4d7 | |
|
|
fa8b94550c | |
|
|
be165b28b9 | |
|
|
951a1ecccc | |
|
|
f90dbc74ee | |
|
|
021e02c741 | |
|
|
290074fac0 | |
|
|
5dad5e6bb4 | |
|
|
865206ced0 | |
|
|
e6590993e4 | |
|
|
0e0bbf6067 | |
|
|
13e3d0e094 | |
|
|
405eb1eb44 | |
|
|
46636f9fc3 | |
|
|
4f93123633 | |
|
|
d64f98f4fb | |
|
|
c726132db8 | |
|
|
fae6e4b741 | |
|
|
65032d39f1 | |
|
|
2c31f6d1bf | |
|
|
3dffd028cf | |
|
|
707886fa0f | |
|
|
78d134c4c3 | |
|
|
6b4bc308f4 | |
|
|
9979249eca | |
|
|
44d93d3384 | |
|
|
d44dda2d7b | |
|
|
85051786bd | |
|
|
f62c461e16 | |
|
|
ad35f50c6b | |
|
|
968667182b | |
|
|
048f80ea59 | |
|
|
4737e115c9 | |
|
|
d74bbf74b9 | |
|
|
8ec2ab8af6 | |
|
|
5d9de1a618 | |
|
|
f9c441ce54 | |
|
|
5dd1850a5a | |
|
|
90998185f4 | |
|
|
98e706006d | |
|
|
0e521cf8ee | |
|
|
f1eb38cc42 | |
|
|
1c4c6b03a4 | |
|
|
046d8d0f32 | |
|
|
6ff9601505 | |
|
|
1a42b3605a | |
|
|
7a3633f20c | |
|
|
ef490af16e | |
|
|
bf94deb0c7 | |
|
|
b2b271e5cb | |
|
|
52e14dd060 | |
|
|
a4f4b7da7c | |
|
|
c8ebfe9c27 | |
|
|
8089f36af5 | |
|
|
979f074376 | |
|
|
d673744dc4 | |
|
|
0a33c0fff0 | |
|
|
4f919d272f | |
|
|
67d367e922 | |
|
|
78f014e6b0 | |
|
|
952b3f3e15 | |
|
|
4c71a8fb14 | |
|
|
dfdab75ade | |
|
|
1c997b788b | |
|
|
c8230f5b01 | |
|
|
124a27e665 | |
|
|
ef908e4d33 | |
|
|
d3ccfd9120 | |
|
|
e75b8325ad | |
|
|
5b1bcf747e | |
|
|
bd30e99d5e | |
|
|
a9b05fc3d4 | |
|
|
5b587e9d16 | |
|
|
2ff86685ec | |
|
|
59c6abcaef | |
|
|
987f6b95ea | |
|
|
38add72517 | |
|
|
10ecac13d6 | |
|
|
a35b2a4465 | |
|
|
ef75bc9ab5 | |
|
|
d0206fcdb5 | |
|
|
76f262a843 | |
|
|
4a71f2f23a | |
|
|
d65190c6b2 | |
|
|
b4945489fa | |
|
|
28798e0839 | |
|
|
4e074f38b3 | |
|
|
43f2e18ea9 | |
|
|
8c512a80c0 | |
|
|
ded045f62c | |
|
|
353252e6c1 | |
|
|
5ef42fa20b | |
|
|
b5dc5a2da4 | |
|
|
d1d98136ef | |
|
|
eec2e9b0ec | |
|
|
fc66ce7e63 | |
|
|
d01eda562a | |
|
|
2787653b5e | |
|
|
3892835d8f | |
|
|
e532daeb2f | |
|
|
279e77946b | |
|
|
4fb4f830ef | |
|
|
ef9e6cf442 | |
|
|
b592b9899b | |
|
|
364e2b13b8 | |
|
|
153768c85b | |
|
|
df40bdcf29 | |
|
|
1ed9f06839 | |
|
|
3a30016c28 | |
|
|
08125b60fe | |
|
|
b1f0cd319c | |
|
|
5005e9093d | |
|
|
15390f67f9 | |
|
|
ea3005a02a | |
|
|
aa3e18f320 | |
|
|
ecbe01cf1a | |
|
|
f95c59b2e1 | |
|
|
9782fe69c3 | |
|
|
7cfac3a455 | |
|
|
e62cab9579 | |
|
|
2429314fb9 | |
|
|
f8a6108b77 | |
|
|
6852fb361e | |
|
|
265c696b42 | |
|
|
6c3b31ca6e | |
|
|
1d8ea965e0 | |
|
|
2057418776 | |
|
|
626320b0f1 | |
|
|
0881792b39 | |
|
|
693f775440 | |
|
|
df159f0d1a | |
|
|
777eff7b20 | |
|
|
3555762a4f | |
|
|
c6a18b70e7 | |
|
|
0c7fe54429 | |
|
|
206c0ed94a | |
|
|
5b9f5de379 | |
|
|
a377954b4a | |
|
|
b9eb7fd6c2 | |
|
|
5671fbd562 | |
|
|
78e72429c2 | |
|
|
ced6915dc6 | |
|
|
4a05400709 | |
|
|
2b8100330f | |
|
|
39456b2790 | |
|
|
ee6e84cf78 | |
|
|
7e843ab9f5 | |
|
|
215f6c880f | |
|
|
786645cf10 | |
|
|
d36fa9172d | |
|
|
718c3edb74 | |
|
|
0092b60672 | |
|
|
f7a4c80feb | |
|
|
4cf7dca700 | |
|
|
29110ab56b | |
|
|
30f1a3707a | |
|
|
3329f1a1ba | |
|
|
9e04a31af5 | |
|
|
868180af63 | |
|
|
2f881b023e | |
|
|
fd8ca0583b | |
|
|
1e52a044b4 | |
|
|
9926e9408a | |
|
|
c1feb11966 | |
|
|
ff3b1dd5ed | |
|
|
cb6ee55138 | |
|
|
ee4f591eea | |
|
|
133dc5f59c | |
|
|
5f3ea0bc08 | |
|
|
d1f969489f | |
|
|
2c1ef3134e | |
|
|
743f7a45de | |
|
|
e42bd2c499 | |
|
|
1f1e3bacc0 | |
|
|
9665b30c35 | |
|
|
1d8f9add4f | |
|
|
8d63a2cdd4 | |
|
|
7ca3a1af97 | |
|
|
c3c5491c80 | |
|
|
8e72d42921 | |
|
|
ccfb46e665 | |
|
|
54bbe54320 | |
|
|
0dba02a469 | |
|
|
3996477b10 | |
|
|
64c1838854 | |
|
|
4e136236b7 | |
|
|
f07eae8d44 | |
|
|
7de9641956 | |
|
|
eaf85c24af | |
|
|
da15994542 | |
|
|
fefd725ae4 | |
|
|
9e83020ee6 | |
|
|
b598aa98cc | |
|
|
9f8a14f3b6 | |
|
|
71922cffb7 | |
|
|
f3449902c8 | |
|
|
5aca075659 | |
|
|
feb46aa051 | |
|
|
874e27aa36 | |
|
|
5d49da13d4 | |
|
|
e8987c5322 | |
|
|
5fea097260 | |
|
|
cf6b8a5c15 | |
|
|
7a059566f0 | |
|
|
d90a336cc0 | |
|
|
3a21079c3e | |
|
|
e34ffeab2d | |
|
|
016adb558d | |
|
|
89046bb133 | |
|
|
3f5492e744 | |
|
|
aed2646a68 | |
|
|
5997d65a93 | |
|
|
1690a091df | |
|
|
fd58fbe6b0 | |
|
|
20184dec07 | |
|
|
4e75375630 | |
|
|
d1eea16d42 | |
|
|
30432db146 | |
|
|
d0ad976bfa | |
|
|
79eeaa7225 | |
|
|
baf8e9f547 | |
|
|
5b9247f559 | |
|
|
cfeabf7b1a | |
|
|
62c7df4ba2 | |
|
|
61c7f1a6a2 | |
|
|
193cf5a167 | |
|
|
b4212b17c3 | |
|
|
79bd1634f6 | |
|
|
ce1e7c05d8 | |
|
|
65d34ffe18 | |
|
|
70fe9bfa1d | |
|
|
fe0ab72b28 | |
|
|
a23c304f9a | |
|
|
6c2d640c12 | |
|
|
ad552eef2e | |
|
|
aca7330934 | |
|
|
2d997101ef | |
|
|
28026d4baa | |
|
|
4cb6d261ab | |
|
|
e47f2d7942 | |
|
|
da49db662e | |
|
|
7fe473d23a | |
|
|
a98f24fd94 | |
|
|
9365dc55f6 | |
|
|
a29daeb1f9 | |
|
|
10471dedf3 | |
|
|
d595c3162d | |
|
|
c51e34e194 | |
|
|
0b2abdc4c1 | |
|
|
3918e1aca8 | |
|
|
f3ccfd79f3 | |
|
|
8bbfea2898 | |
|
|
cbaa21e0f7 | |
|
|
b88fa83569 | |
|
|
dd1aa1d67c | |
|
|
2525b884cf | |
|
|
3be3b9e891 | |
|
|
bc4fe51953 | |
|
|
d314082ea2 | |
|
|
18ddf7ca2d | |
|
|
0fa909ffe2 | |
|
|
6a4159f854 | |
|
|
fa1dd4b72b | |
|
|
76591322f8 | |
|
|
194b8dece2 | |
|
|
c85f7a5b03 | |
|
|
c506f9b720 | |
|
|
2ee61edc8f | |
|
|
b2f62ec643 | |
|
|
f5e512fa1d | |
|
|
65e76bd48e | |
|
|
5aa2d80f17 | |
|
|
8e9717c386 | |
|
|
4107b4565d | |
|
|
2a9971501a | |
|
|
7d02b27829 | |
|
|
84fba9d11f | |
|
|
6ad30cce8f | |
|
|
29b60ead98 | |
|
|
233964e840 | |
|
|
7f52aadbe8 | |
|
|
f0b0dd3d23 | |
|
|
c4c5d19973 | |
|
|
9437866fb6 | |
|
|
fc455d7f63 | |
|
|
27e2a797fe | |
|
|
04234d13c4 | |
|
|
94776541f8 | |
|
|
d513962094 | |
|
|
5bc8836f55 | |
|
|
a84cc78f1b | |
|
|
a355b6f179 | |
|
|
8b8357488a | |
|
|
dfb462d575 | |
|
|
4cc4ad0156 | |
|
|
9e34662f09 | |
|
|
2ffec76e26 | |
|
|
2e4a8d2ec8 | |
|
|
88c0c58f1b | |
|
|
8ae5fc0a28 | |
|
|
d23d4c2181 | |
|
|
59ee692609 | |
|
|
12228a795e | |
|
|
78b9a93c8c | |
|
|
1cb2eedeec | |
|
|
29adc9f712 | |
|
|
9086b92476 | |
|
|
7e17929fa8 | |
|
|
3385064812 | |
|
|
5c3333f52d | |
|
|
77438a7a9e | |
|
|
ffdad21b43 | |
|
|
594a0d8b22 | |
|
|
bd88fcc78e | |
|
|
e167f8095f | |
|
|
f7598806b2 | |
|
|
aaa988d819 | |
|
|
b13d3cfbcb | |
|
|
bc636611cb | |
|
|
364294db35 | |
|
|
89ebbe8e07 | |
|
|
1efbe2cf60 | |
|
|
b0086c4ae5 | |
|
|
be004f6c5d | |
|
|
badd896984 | |
|
|
ced088b369 | |
|
|
e6a3d01f00 | |
|
|
4f91367b4d | |
|
|
5e1fd8a11b | |
|
|
ffd16229d2 | |
|
|
ef4ffe4f96 | |
|
|
77ab9d17db | |
|
|
7d7deeb715 | |
|
|
2e66694de7 | |
|
|
2fbf99d180 | |
|
|
ed375428c4 | |
|
|
8b14d62f5f | |
|
|
ff59dfeafc | |
|
|
13f91bea41 | |
|
|
c94d4f5181 | |
|
|
85a530d26d | |
|
|
0f1a53e60c | |
|
|
bf40f2510b | |
|
|
3422cec908 | |
|
|
3118e9b294 | |
|
|
bd246bdc0d | |
|
|
c905cb1b4f | |
|
|
47a40e331d | |
|
|
9b5e791570 | |
|
|
c211b3b15a | |
|
|
4dbe85549e | |
|
|
1b9a11da40 | |
|
|
d3797cfabd | |
|
|
82de79d493 | |
|
|
34b0f44034 | |
|
|
3e914e6e2a | |
|
|
1d54c884db | |
|
|
cdaea847e8 | |
|
|
06fba030be | |
|
|
10df626d92 | |
|
|
8b89e1f7bb | |
|
|
6f6ec08dc4 | |
|
|
10acc2daba | |
|
|
db03739812 | |
|
|
afe9d3f4f6 | |
|
|
46a3f495f9 | |
|
|
91c62221bc | |
|
|
a17f736147 | |
|
|
8223542902 | |
|
|
66c297b876 | |
|
|
68a80d0127 | |
|
|
3caf40c282 | |
|
|
50e4da682f | |
|
|
d1363211cb | |
|
|
2c5088ae8a | |
|
|
37c8e9c1c5 | |
|
|
1647bfc8a9 | |
|
|
77bb7fdab2 | |
|
|
a15093a8f3 | |
|
|
d1f8aded61 | |
|
|
485cddb775 | |
|
|
fe85b3093d | |
|
|
1657be0b0b | |
|
|
bb549899de | |
|
|
51e87f8814 | |
|
|
95016a64be | |
|
|
5abf3fbd06 | |
|
|
72175872a6 | |
|
|
06b4f9cf2b | |
|
|
b73a2d4415 | |
|
|
2b678c98b8 | |
|
|
2c25bf4a3d | |
|
|
4931343027 | |
|
|
80f1be6f9d | |
|
|
db07f833c8 | |
|
|
98bcbc1d86 | |
|
|
5d2a718561 | |
|
|
e8a0c61ef1 | |
|
|
58903b5e63 | |
|
|
b7ac203e11 | |
|
|
559bde43f4 | |
|
|
cd608192b1 | |
|
|
08f29383cf | |
|
|
a852c0674b | |
|
|
45da7d136f | |
|
|
f96243d24c | |
|
|
5232a31c2f | |
|
|
2c5dcd4f2e | |
|
|
ef4c1e1290 | |
|
|
aa504418bf | |
|
|
e704ed1a91 | |
|
|
a114fc9a46 | |
|
|
abcd2dfb31 | |
|
|
0c6b0c9c77 | |
|
|
b2dae7ff11 | |
|
|
5ac214d796 | |
|
|
4d4effabc3 | |
|
|
bcb76bc90f | |
|
|
940efd9624 | |
|
|
18b3d4c2a5 | |
|
|
ecc6db4d2a | |
|
|
39027d10aa |
|
|
@ -0,0 +1,40 @@
|
|||
name: 缺陷反馈 | Bug
|
||||
description: 当您发现了一个缺陷,需要向社区反馈时,请使用此模板。
|
||||
title: "[BUG] <标题>"
|
||||
labels: [👀 needs triage, "Type: Bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢您对 openvela 社区的支持与关注,欢迎反馈缺陷。
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "重现问题的步骤"
|
||||
description: "简洁地描述错误是什么,为什么您认为它是一个错误,以及如何重现它的步骤"
|
||||
placeholder: |
|
||||
重现问题的步骤,可能包括日志和截图。
|
||||
1. 步骤 1
|
||||
2. 步骤 2
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: architecture
|
||||
attributes:
|
||||
label: Issue Architecture
|
||||
multiple: true
|
||||
options:
|
||||
- "Arch: arm"
|
||||
- "Arch: arm64"
|
||||
- "Arch: x86_64"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
提交前请确认您已遵循以下步骤:
|
||||
- 确认问题在 [**dev**](../) 上可重现。
|
||||
- 遇到构建问题时运行 `make distclean`。
|
||||
- 搜索 [现有问题](./)
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
name: 新需求 | Feature
|
||||
description: 当您需要反馈或实现一个新需求时,使用此模板。
|
||||
title: "[FEATURE] <标题>"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢您对 openvela 社区的支持与关注。
|
||||
|
||||
- type: textarea
|
||||
id: question-description
|
||||
attributes:
|
||||
label: 您的需求是否和问题相关?
|
||||
description: 请简单描述问题,并提供issue链接。
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: 请描述您想要的解决方案
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: 替代方案
|
||||
attributes:
|
||||
label: 请描述您考虑过的替代解决方案
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
提交前请搜索 [现有功能需求](./)
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
name: 问题咨询
|
||||
title: "[问题咨询]"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢您对 openvela 社区的支持与关注。
|
||||
|
||||
- type: textarea
|
||||
id: question-description
|
||||
attributes:
|
||||
label: 描述
|
||||
description: 请解释您的问题的背景或上下文,这有助于其他人更好地理解您的问题或疑问。
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
提交前请确认您已遵循以下步骤:
|
||||
- 我已搜索 [openvela 文档](../),但未找到问题的答案。
|
||||
- 已搜索 [现有问题](./)
|
||||
|
|
@ -0,0 +1 @@
|
|||
blank_issues_enabled: false
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
## 概要
|
||||
|
||||
*在此部分更新信息,说明更改的必要性、具体做了什么以及如何实现的,如果有新功能出现,请提供参考资料(依赖关系、类似问题和解决方案等)。*
|
||||
|
||||
## 影响
|
||||
|
||||
*在此部分更新信息(如适用),说明更改如何影响用户、构建过程、硬件、文档、安全性、兼容性等。*
|
||||
|
||||
## 测试
|
||||
|
||||
*在此部分更新信息,详细说明如何验证更改,使用什么主机进行构建(操作系统、CPU、编译器等),使用什么目标进行验证(架构、板子:配置等)。提供更改前后的构建和运行日志将非常有帮助。*
|
||||
|
|
@ -0,0 +1 @@
|
|||
* @xiaoxiang781216 @GUIDINGLI @huangcaihua-xm
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
name: Bug report
|
||||
description: Report a bug to improve openvela stability
|
||||
title: "[BUG] <title>"
|
||||
labels: [👀 needs triage, "Type: Bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Hello openvela Community member! Please keep things tidy by putting your post in the proper place:
|
||||
|
||||
Reporting a bug: use this form.
|
||||
Asking a question or getting help: use the [General Help](./new?assignees=&labels=Community%3A+Question&projects=&template=003_help.yml&title=%5BHELP%5D+%3Ctitle%3E) form.
|
||||
Requesting a new feature: use the [Feature request](./new?assignees=&labels=Type%3A+Enhancement&projects=&template=002_feature_request.yml&title=%5BFEATURE%5D+%3Ctitle%3E) form.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Description / Steps to reproduce the issue"
|
||||
description: "A clear and concise description of what the bug is, and why you consider it to be a bug, and steps for how to reproduce it"
|
||||
placeholder: |
|
||||
A description with steps to reproduce the issue.
|
||||
May include logs, images, or videos.
|
||||
1. Step 1
|
||||
2. Step 2
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: architecture
|
||||
attributes:
|
||||
label: Issue Architecture
|
||||
description: What architecture(s) are you seeing the problem on?
|
||||
multiple: true
|
||||
options:
|
||||
- "[Arch: arm]"
|
||||
- "[Arch: arm64]"
|
||||
- "[Arch: x86_64]"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### Before You Submit
|
||||
|
||||
Please verify that you've followed these steps:
|
||||
- Confirm the problem is reproducible on [**dev**](../).
|
||||
- Run `make distclean` when encountering build issues.
|
||||
- Search [existing issues](./) (including [closed](./?q=is%3Aissue+is%3Aclosed))
|
||||
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
name: Feature request
|
||||
description: Request an enhancement for openvela
|
||||
title: "[FEATURE] <title>"
|
||||
labels: ["Type: Enhancement"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Hello openvela Community member! Please keep things tidy by putting your post in the proper place:
|
||||
|
||||
Requesting a new feature: use this form.
|
||||
Asking a question or getting help: use the [General Help](./new?assignees=&labels=Community%3A+Question&projects=&template=003_help.yml&title=%5BHELP%5D+%3Ctitle%3E) form.
|
||||
Reporting a bug: use the [Bug report](./new?assignees=&labels=%F0%9F%91%80+needs+triage%2CType%3A+Bug&projects=&template=001_bug_report.yml&title=%5BBUG%5D+%3Ctitle%3E) form.
|
||||
|
||||
- type: textarea
|
||||
id: question-description
|
||||
attributes:
|
||||
label: Is your feature request related to a problem? Please describe.
|
||||
description: Please provide a clear and concise description of what the problem is. Add relevant issue link.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: Describe the solution you'd like
|
||||
description: Please provide a clear and concise description of what you want to happen.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Describe alternatives you've considered
|
||||
description: Please provide a clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### Before You Submit
|
||||
|
||||
Please verify that you've followed these steps:
|
||||
- Search [existing feature requests](./) (including [closed](./?q=is%3Aissue+is%3Aclosed))
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
name: General Help
|
||||
description: Get general support regarding openvela
|
||||
title: "[HELP] <title>"
|
||||
labels: ["Community: Question"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Hello openvela Community member! Please keep things tidy by putting your post in the proper place:
|
||||
|
||||
Asking a question or getting help: use this form.
|
||||
Reporting a bug: use the [Bug report](./new?assignees=&labels=%F0%9F%91%80+needs+triage%2CType%3A+Bug&projects=&template=001_bug_report.yml&title=%5BBUG%5D+%3Ctitle%3E) form.
|
||||
Requesting a new feature: use the [Feature request](./new?assignees=&labels=Type%3A+Enhancement&projects=&template=002_feature_request.yml&title=%5BFEATURE%5D+%3Ctitle%3E) form
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### Whether you're a beginner or an experienced developer, openvela Help is here to assist you with all your openvela questions and concerns.
|
||||
|
||||
- type: textarea
|
||||
id: question-description
|
||||
attributes:
|
||||
label: Description
|
||||
description: Explain the background or context of your question. This helps others understand your problem or inquiry better.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### Before You Submit
|
||||
|
||||
Please verify that you've followed these steps:
|
||||
- I have searched [openvela Documentation](../) and didn't find an answer to my question.
|
||||
- Search [existing issues](./) (including [closed](./?q=is%3Aissue+is%3Aclosed))
|
||||
|
|
@ -0,0 +1 @@
|
|||
blank_issues_enabled: false
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
*Note: Please adhere to [Contributing Guidelines](https://github.com/open-vela/docs/blob/dev/CONTRIBUTING.md).*
|
||||
|
||||
## Summary
|
||||
|
||||
*Update this section with information on why change is necessary,
|
||||
what it exactly does and how, if new feature shows up, provide
|
||||
references (dependencies, similar problems and solutions), etc.*
|
||||
|
||||
## Impact
|
||||
|
||||
*Update this section, where applicable, on how change affects users,
|
||||
build process, hardware, documentation, security, compatibility, etc.*
|
||||
|
||||
## Testing
|
||||
|
||||
*Update this section with details on how did you verify the change,
|
||||
what Host was used for build (OS, CPU, compiler, ..), what Target was
|
||||
used for verification (arch, board:config, ..), etc. Providing build
|
||||
and runtime logs from before and after change is highly appreciated.*
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: checkpatch
|
||||
|
||||
# Controls when the workflow will run
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
checkpatch:
|
||||
uses: open-vela/public-actions/.github/workflows/checkpatch.yml@dev
|
||||
secrets: inherit
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: CI
|
||||
|
||||
# Controls when the workflow will run
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, reopened, synchronize]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
ci:
|
||||
uses: open-vela/public-actions/.github/workflows/ci.yml@dev
|
||||
secrets: inherit
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: clang-format
|
||||
|
||||
# Controls when the workflow will run
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
clang-format:
|
||||
uses: open-vela/public-actions/.github/workflows/clang-format.yml@dev
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
name: 'Close stale issues and PR'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
workflow_dispatch: # 允许手动触发
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
uses: open-vela/public-actions/.github/workflows/stale.yml@dev
|
||||
secrets: inherit
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
/uv
|
||||
*.o
|
||||
Make.dep
|
||||
.depend
|
||||
.built
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
cc_defaults {
|
||||
name: "framework_utils_defaults",
|
||||
|
||||
static_libs: [
|
||||
"libframework_utils",
|
||||
],
|
||||
|
||||
header_libs: [
|
||||
"kernel_modules_headers",
|
||||
],
|
||||
}
|
||||
|
||||
soong_config_module_type {
|
||||
name: "framework_utils_cc_defaults",
|
||||
module_type: "cc_defaults",
|
||||
config_namespace: "vela",
|
||||
|
||||
variables: [
|
||||
"kvdb_server",
|
||||
],
|
||||
properties: [
|
||||
"cflags",
|
||||
],
|
||||
}
|
||||
|
||||
soong_config_string_variable {
|
||||
name: "kvdb_server",
|
||||
values: [
|
||||
"cp",
|
||||
"sensor",
|
||||
"audio",
|
||||
],
|
||||
}
|
||||
|
||||
framework_utils_cc_defaults {
|
||||
name: "frameworks_utils_cflags_defaults",
|
||||
|
||||
cflags: [
|
||||
"-fvisibility=hidden",
|
||||
"-DCONFIG_NET_RPMSG",
|
||||
],
|
||||
|
||||
soong_config_variables: {
|
||||
kvdb_server: {
|
||||
cp: {
|
||||
cflags: ["-DCONFIG_KVDB_SERVER_CPUNAME=\"cp\""],
|
||||
},
|
||||
sensor: {
|
||||
cflags: ["-DCONFIG_KVDB_SERVER_CPUNAME=\"sensor\""],
|
||||
},
|
||||
audio: {
|
||||
cflags: ["-DCONFIG_KVDB_SERVER_CPUNAME=\"audio\""],
|
||||
},
|
||||
conditions_default: {
|
||||
cflags: ["-DCONFIG_KVDB_SERVER_CPUNAME=\"ap\""],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc_library_static {
|
||||
name: "libframework_utils",
|
||||
export_include_dirs: ["include"],
|
||||
|
||||
defaults: ["frameworks_utils_cflags_defaults"],
|
||||
|
||||
srcs: [
|
||||
"kvdb/common.c",
|
||||
"kvdb/system_properties.c",
|
||||
"kvdb/client.c",
|
||||
],
|
||||
|
||||
header_libs: [
|
||||
"kernel_modules_headers",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library_headers {
|
||||
name: "libprop_headers",
|
||||
export_include_dirs: ["include/cutils"],
|
||||
vendor_available: true,
|
||||
}
|
||||
|
||||
cc_library_headers {
|
||||
name: "libframework_utils_headers",
|
||||
export_include_dirs: ["include"],
|
||||
vendor_available: true,
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "kvset",
|
||||
srcs: ["kvdb/setprop.c"],
|
||||
defaults: ["framework_utils_defaults"],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "kvget",
|
||||
srcs: ["kvdb/getprop.c"],
|
||||
defaults: ["framework_utils_defaults"],
|
||||
}
|
||||
|
||||
cc_library_static {
|
||||
name: "libkvdb_vendor",
|
||||
export_include_dirs: ["include"],
|
||||
|
||||
defaults: ["frameworks_utils_cflags_defaults"],
|
||||
|
||||
srcs: [
|
||||
"kvdb/common.c",
|
||||
"kvdb/system_properties.c",
|
||||
"kvdb/client.c",
|
||||
],
|
||||
|
||||
header_libs: [
|
||||
"kernel_modules_headers",
|
||||
],
|
||||
|
||||
proprietary: true
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
#
|
||||
# Copyright (C) 2021 Xiaomi Corporation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy of
|
||||
# the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations under
|
||||
# the License.
|
||||
#
|
||||
if(CONFIG_SCHED_INSTRUMENTATION_DUMP
|
||||
OR CONFIG_ANDROID_LIBBASE
|
||||
OR CONFIG_ATRACE
|
||||
OR CONFIG_LIB_DBUS
|
||||
OR CONFIG_KVDB)
|
||||
|
||||
set_property(
|
||||
TARGET nuttx
|
||||
APPEND
|
||||
PROPERTY NUTTX_INCLUDE_DIRECTORIES
|
||||
${NUTTX_APPS_DIR}/frameworks/system/utils/include)
|
||||
|
||||
set(CSRCS)
|
||||
set(CFLAGS)
|
||||
set(INCDIR)
|
||||
|
||||
nuttx_add_library(framework_utils STATIC)
|
||||
|
||||
list(APPEND CSRCS trace/trace.c)
|
||||
if(CONFIG_ANDROID_LIBBASE)
|
||||
list(APPEND CSRCS log/log_write.c log/set_abort_message.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_ATRACE)
|
||||
nuttx_add_application(NAME atrace SRCS trace/atrace.c DEPENDS
|
||||
framework_utils)
|
||||
endif()
|
||||
|
||||
if(CONFIG_GDBUS)
|
||||
file(GLOB GDBUS_CSRCS gdbus/*.c)
|
||||
list(APPEND CSRCS ${GDBUS_CSRCS})
|
||||
list(APPEND CFLAGS -DDBUS_COMPILATION -DVERSION="1.15.1")
|
||||
list(APPEND INCDIR ${NUTTX_APPS_DIR}/external/dbus/dbus)
|
||||
endif()
|
||||
|
||||
if(CONFIG_KVDB)
|
||||
if(CONFIG_KVDB_DIRECT)
|
||||
list(APPEND CSRCS kvdb/direct.c)
|
||||
else()
|
||||
list(APPEND CSRCS kvdb/client.c)
|
||||
nuttx_add_application(
|
||||
MODULE
|
||||
${CONFIG_KVDB}
|
||||
NAME
|
||||
exitprop
|
||||
STACKSIZE
|
||||
${CONFIG_KVDB_STACKSIZE}
|
||||
PRIORITY
|
||||
${CONFIG_KVDB_PRIORITY}
|
||||
SRCS
|
||||
kvdb/exitprop.c
|
||||
INCLUDE_DIRECTORIES
|
||||
${INCDIR}
|
||||
DEPENDS
|
||||
framework_utils)
|
||||
endif()
|
||||
list(APPEND CSRCS kvdb/common.c kvdb/system_properties.c kvdb/backend.c)
|
||||
|
||||
if(CONFIG_KVDB_NVS)
|
||||
list(APPEND CSRCS kvdb/nvs.c)
|
||||
elseif(CONFIG_KVDB_UNQLITE)
|
||||
list(APPEND INCDIR ${NUTTX_APPS_DIR}/external/unqlite/unqlite)
|
||||
list(APPEND CSRCS kvdb/unqlite.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_KVDB_FILE OR CONFIG_KVDB_TEMPORARY_STORAGE)
|
||||
list(APPEND CSRCS kvdb/file.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_KVDB_SERVER)
|
||||
nuttx_add_application(
|
||||
MODULE
|
||||
${CONFIG_KVDB}
|
||||
NAME
|
||||
kvdbd
|
||||
STACKSIZE
|
||||
${CONFIG_KVDB_STACKSIZE}
|
||||
PRIORITY
|
||||
${CONFIG_KVDB_PRIORITY}
|
||||
SRCS
|
||||
kvdb/server.c
|
||||
INCLUDE_DIRECTORIES
|
||||
${INCDIR}
|
||||
DEPENDS
|
||||
framework_utils)
|
||||
endif()
|
||||
|
||||
nuttx_add_application(
|
||||
MODULE
|
||||
${CONFIG_KVDB}
|
||||
NAME
|
||||
setprop
|
||||
STACKSIZE
|
||||
${CONFIG_KVDB_STACKSIZE}
|
||||
PRIORITY
|
||||
${CONFIG_KVDB_PRIORITY}
|
||||
SRCS
|
||||
kvdb/setprop.c
|
||||
INCLUDE_DIRECTORIES
|
||||
${INCDIR}
|
||||
DEPENDS
|
||||
framework_utils)
|
||||
|
||||
nuttx_add_application(
|
||||
MODULE
|
||||
${CONFIG_KVDB}
|
||||
NAME
|
||||
getprop
|
||||
STACKSIZE
|
||||
${CONFIG_KVDB_STACKSIZE}
|
||||
PRIORITY
|
||||
${CONFIG_KVDB_PRIORITY}
|
||||
SRCS
|
||||
kvdb/getprop.c
|
||||
INCLUDE_DIRECTORIES
|
||||
${INCDIR}
|
||||
DEPENDS
|
||||
framework_utils)
|
||||
|
||||
if(CONFIG_KVDB_QEMU_PROPERTIES)
|
||||
nuttx_add_application(
|
||||
MODULE
|
||||
${CONFIG_KVDB}
|
||||
NAME
|
||||
qemuprop
|
||||
STACKSIZE
|
||||
${CONFIG_KVDB_STACKSIZE}
|
||||
PRIORITY
|
||||
${CONFIG_KVDB_PRIORITY}
|
||||
SRCS
|
||||
kvdb/qemu_properties.c
|
||||
INCLUDE_DIRECTORIES
|
||||
${INCDIR}
|
||||
DEPENDS
|
||||
framework_utils)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
target_sources(framework_utils PRIVATE ${CSRCS})
|
||||
target_include_directories(framework_utils PRIVATE ${INCDIR})
|
||||
target_compile_options(framework_utils PRIVATE ${CFLAGS})
|
||||
|
||||
endif()
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
#
|
||||
# Copyright (C) 2020 Xiaomi Corporation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
config ALOG
|
||||
bool "Enable Android Log"
|
||||
default n
|
||||
select LIBC_PRINT_EXTENSION
|
||||
|
||||
config ALOG_LEVEL
|
||||
int "Android Log level"
|
||||
default 0 if NDEBUG
|
||||
default 3 if !NDEBUG
|
||||
depends on ALOG
|
||||
range 0 6
|
||||
---help---
|
||||
ALOG_LEVEL is used to control the logging level for printing.
|
||||
0:LOG_ALWAYS_FATAL
|
||||
1:LOG_FATAL
|
||||
2:LOG_ERROR
|
||||
3:LOG_WARN
|
||||
4:LOG_INFO
|
||||
5:LOG_DEBUG
|
||||
6:LOG_VERBOSE
|
||||
|
||||
config ATRACE
|
||||
bool "Android Trace"
|
||||
default n
|
||||
---help---
|
||||
Enable Android Trace.
|
||||
|
||||
config GDBUS
|
||||
bool "Enable Vela GDBus"
|
||||
depends on LIB_DBUS && LIBUV_EXTENSION
|
||||
default y
|
||||
---help---
|
||||
Enable Vela GDBus
|
||||
|
||||
config KVDB
|
||||
tristate "key-value database"
|
||||
default n
|
||||
|
||||
if KVDB
|
||||
|
||||
choice
|
||||
prompt "KVDB log level"
|
||||
default KVDB_LOG_SLIENT
|
||||
|
||||
config KVDB_LOG_INFO
|
||||
bool "KVDB_LOG_INFO"
|
||||
|
||||
config KVDB_LOG_WARN
|
||||
bool "KVDB_LOG_WARN"
|
||||
|
||||
config KVDB_LOG_ERR
|
||||
bool "KVDB_LOG_ERR"
|
||||
|
||||
config KVDB_LOG_SLIENT
|
||||
bool "KVDB_LOG_SLIENT"
|
||||
|
||||
endchoice
|
||||
|
||||
config KVDB_PRIORITY
|
||||
int "task priority"
|
||||
default 100
|
||||
|
||||
config KVDB_STACKSIZE
|
||||
int "stack size"
|
||||
default 4096
|
||||
|
||||
config KVDB_DUMPLIST
|
||||
bool "KVDB dump list"
|
||||
default y
|
||||
---help---
|
||||
Kvd will dump all key-value when use getprop without key
|
||||
|
||||
config KVDB_SERVER
|
||||
bool "KVDB server"
|
||||
default n
|
||||
---help---
|
||||
Build kvdb in server mode. Operate database in a unified manner through the kvdb server
|
||||
|
||||
config KVDB_DIRECT
|
||||
bool "Access KVDB directly"
|
||||
depends on !KVDB_SERVER
|
||||
---help---
|
||||
Operate the database directly. Applicable to scenarios where cross-core communication is not required
|
||||
|
||||
if !KVDB_DIRECT
|
||||
|
||||
config KVDB_SERVER_CPUNAME
|
||||
string "which cpu kvdb server runs on"
|
||||
depends on NET_RPMSG
|
||||
default "ap"
|
||||
|
||||
config KVDB_TIMEOUT_INTERVAL
|
||||
int "transaction timeout interval(usec)"
|
||||
default 5000000
|
||||
depends on NET_SOCKOPTS
|
||||
|
||||
endif
|
||||
|
||||
config KVDB_COMMIT_INTERVAL
|
||||
int "commit time interval(sec)"
|
||||
depends on KVDB_SERVER
|
||||
default 5
|
||||
|
||||
config KVDB_BACKLOG_CONNS
|
||||
int "backlog connections"
|
||||
depends on KVDB_SERVER
|
||||
default 256
|
||||
|
||||
if KVDB_DIRECT || KVDB_SERVER
|
||||
|
||||
config KVDB_SOURCE_PATH
|
||||
string "database default value source path"
|
||||
default "/etc/build.prop"
|
||||
|
||||
config KVDB_TEMPORARY_STORAGE
|
||||
bool "enable non-persistent Key-value storage"
|
||||
default !DEFAULT_SMALL
|
||||
depends on FS_TMPFS
|
||||
|
||||
choice
|
||||
prompt "the persistent storage method of Key-value"
|
||||
default KVDB_UNQLITE
|
||||
|
||||
config KVDB_UNQLITE
|
||||
bool "UNQLITE"
|
||||
depends on UNQLITE
|
||||
---help---
|
||||
Configure the unqlite database to store Key-value.
|
||||
|
||||
config KVDB_NVS
|
||||
bool "NVS"
|
||||
depends on MTD_CONFIG_NAMED
|
||||
---help---
|
||||
Configure using Non-Volatile Storage to store Key-value
|
||||
|
||||
config KVDB_FILE
|
||||
bool "FILE"
|
||||
---help---
|
||||
Configure using file to store Key-value
|
||||
|
||||
endchoice
|
||||
|
||||
config KVDB_PERSIST_PATH
|
||||
string "persistent database path"
|
||||
default "/data/persist.db" if KVDB_UNQLITE
|
||||
default "/dev/config" if KVDB_NVS
|
||||
|
||||
config KVDB_TEMPORARY_PATH
|
||||
string "non-persistent database directory path"
|
||||
default "/tmp/db"
|
||||
depends on KVDB_TEMPORARY_STORAGE
|
||||
|
||||
endif # KVDB_DIRECT || KVDB_SERVER
|
||||
|
||||
config KVDB_QEMU_PROPERTIES
|
||||
tristate "Goldfish boot-properties service"
|
||||
default n
|
||||
depends on GOLDFISH_PIPE
|
||||
---help---
|
||||
Enable the Goldfish boot-properties service
|
||||
|
||||
endif # KVDB
|
||||
|
||||
osource "$APPSDIR/frameworks/system/utils/uv/Kconfig"
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
Copyright (C) 2024 Xiaomi Corporation
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#
|
||||
# Copyright (C) 2021 Xiaomi Corporation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
ifneq ($(CONFIG_SCHED_INSTRUMENTATION_DUMP)\
|
||||
${CONFIG_ANDROID_LIBBASE}\
|
||||
${CONFIG_ATRACE}\
|
||||
${CONFIG_LIB_DBUS}\
|
||||
${CONFIG_KVDB},)
|
||||
CONFIGURED_APPS += $(APPDIR)/frameworks/system/utils
|
||||
CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/frameworks/system/utils/include
|
||||
CXXFLAGS += ${INCDIR_PREFIX}$(APPDIR)/frameworks/system/utils/include
|
||||
endif
|
||||
|
||||
-include $(APPDIR)/frameworks/system/utils/uv/Make.defs
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
#
|
||||
# Copyright (C) 2021 Xiaomi Corporation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
include $(APPDIR)/Make.defs
|
||||
|
||||
CSRCS += trace/trace.c
|
||||
|
||||
ifneq ($(CONFIG_ANDROID_LIBBASE),)
|
||||
CSRCS += log/log_write.c log/set_abort_message.c
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_ATRACE),)
|
||||
MAINSRC += trace/atrace.c
|
||||
PROGNAME += atrace
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_GDBUS),)
|
||||
CSRCS += $(wildcard gdbus/*.c)
|
||||
CFLAGS += -DDBUS_COMPILATION -DVERSION="1.15.1"
|
||||
CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/external/dbus/dbus
|
||||
endif # CONFIG_LIB_DBUS
|
||||
|
||||
ifneq ($(CONFIG_KVDB),)
|
||||
ifneq ($(CONFIG_KVDB_DIRECT),)
|
||||
CSRCS += kvdb/direct.c
|
||||
else
|
||||
CSRCS += kvdb/client.c
|
||||
MAINSRC += kvdb/exitprop.c
|
||||
PROGNAME += exitprop
|
||||
endif # CONFIG_KVDB_DIRECT
|
||||
|
||||
CSRCS += kvdb/common.c kvdb/system_properties.c kvdb/backend.c
|
||||
MAINSRC += kvdb/setprop.c kvdb/getprop.c
|
||||
PROGNAME += setprop getprop
|
||||
|
||||
ifneq ($(CONFIG_KVDB_SERVER),)
|
||||
MAINSRC += kvdb/server.c
|
||||
PROGNAME += kvdbd
|
||||
endif # CONFIG_KVDB_SERVER
|
||||
|
||||
ifneq ($(CONFIG_KVDB_NVS),)
|
||||
CSRCS += kvdb/nvs.c
|
||||
else ifneq ($(CONFIG_KVDB_UNQLITE),)
|
||||
CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/external/unqlite/unqlite
|
||||
CSRCS += kvdb/unqlite.c
|
||||
endif # CONFIG_KVDB_NVS
|
||||
|
||||
ifneq ($(CONFIG_KVDB_FILE)$(CONFIG_KVDB_TEMPORARY_STORAGE),)
|
||||
CSRCS += kvdb/file.c
|
||||
endif # CONFIG_KVDB_FILE or CONFIG_KVDB_TEMPORARY_STORAGE
|
||||
|
||||
ifneq ($(CONFIG_KVDB_QEMU_PROPERTIES),)
|
||||
MAINSRC += kvdb/qemu_properties.c
|
||||
PROGNAME += qemuprop
|
||||
endif # CONFIG_KVDB_QEMU_PROPERTIES
|
||||
|
||||
PRIORITY = $(CONFIG_KVDB_PRIORITY)
|
||||
STACKSIZE = $(CONFIG_KVDB_STACKSIZE)
|
||||
MODULE = $(CONFIG_KVDB)
|
||||
endif # CONFIG_KVDB
|
||||
|
||||
EXPORT_FILES := gdbus/gdbus.h include
|
||||
|
||||
include $(APPDIR)/Application.mk
|
||||
|
|
@ -0,0 +1,254 @@
|
|||
# Utils
|
||||
|
||||
[[English](./README.md) | [中文](./README_zh-cn.md)]
|
||||
|
||||
## Project Overview
|
||||
|
||||
The current directory mainly contains some common tool implementations provided by the framework.
|
||||
| Tool | Brief description of tool |
|
||||
| -- | -- |
|
||||
| `gdbus` | Encapsulation of the `D-Bus` interface for convenient operation of `D-Bus`. |
|
||||
| `kvdb` | `Key-value pair` data access interface based on local database. |
|
||||
| `log` | Provides an `Log API` interface compatible with the `Android` platform.<br>Used to directly use the `Android` LOG API in `Vela`. |
|
||||
| `trace` | Provides a dotting tool for user-space programs. |
|
||||
|
||||
## Project Description
|
||||
|
||||
### 1. gdbus
|
||||
|
||||
The `gdbus` module is an API module that further encapsulates the `D-Bus` interface. Since the `D-Bus` interface is relatively complex to use, when actually using modules, by encapsulating the `D-Bus` interface again, it will be more convenient to use. `gdbus` provides an API interface with easier operation to facilitate our operation of `D-Bus`.
|
||||
|
||||
### 2. kvdb
|
||||
|
||||
#### 2.1 Introduction
|
||||
|
||||
`kvdb` provides a set of read and write interfaces for local databases. The `API` design refers to the `Android` `properties` access specification. At the same time, a command-line tool is provided to facilitate local rapid debugging.
|
||||
The `kvdb` in `Vela` supports local permanent storage and cross-core calls (requiring Unix domain socket and rpmsg socket support respectively). The key-value that needs to be permanently stored in a file needs to start with `"persist."`.
|
||||
The underlying implementation of `kvdb` contains three mechanisms:
|
||||
1. Based on the open source `UnQLite` database, it depends on the database;
|
||||
2. Another one is based on `MTD CONFIG` (currently only used for nor flash);
|
||||
3. The last one is based on `file` files.
|
||||
|
||||
> General interface description of `kvdb`: [frameworks/utils/include/kvdb.h](include/kvdb.h)
|
||||
|
||||
#### 2.2 Typical configuration of `kvdb`
|
||||
|
||||
| kvdb Configuration | Description |
|
||||
| -- | -- |
|
||||
| CONFIG_KVDB_PRIORITY | KVDB task priority, default is the system default value. |
|
||||
| CONFIG_KVDB_STACKSIZE | KVDB stack space allocation, default is the system default value. |
|
||||
| CONFIG_KVDB_SERVER | KVDB SERVER mode. Indicates whether the current CPU is the main CPU for reading and writing files. If it is 'n', only the KVDB on other CPUs will be called. |
|
||||
| CONFIG_KVDB_DIRECT | KVDB DIRECT mode: In scenarios without rpmsg socket (no need for cross-core), this mode can be used.<br>Only one of CONFIG_KVDB_DIRECT and CONFIG_KVDB_SERVER modes can be selected. |
|
||||
| CONFIG_KVDB_COMMIT_INTERVAL | KVDB commit interval (in seconds), default is 5.<br>KVDB has an internal cache and only after committing is the data truly written to the file. If power is off before the `CONFIG_KVDB_COMMIT_INTERVAL` time after submitting a persist type kv, the data will not be truly written to the `persist.db` file. The shorter the `CONFIG_KVDB_COMMIT_INTERVAL` time is set, the more frequently kvdb writes the internal cache to the file, which will affect system performance to a certain extent. |
|
||||
| CONFIG_KVDB_SOURCE_PATH | KVDB default value loading path, default is `"/etc/build.prop"`, supports multiple paths, just separate them with `;`. At each startup, KV values will be automatically loaded from this file. |
|
||||
| CONFIG_KVDB_UNQLITE | Configure to use unqlite database to store kv. |
|
||||
| CONFIG_KVDB_NVS | Configure to use nvs to store kv. |
|
||||
| CONFIG_KVDB_FILE | Configure to use file to store kv. |
|
||||
|
||||
> Only one of the three backends for data storage, `CONFIG_KVDB_UNQLITE`, `CONFIG_KVDB_NVS`, and `CONFIG_KVDB_FILE`, can be selected.
|
||||
|
||||
### 3. log
|
||||
|
||||
The log module itself is a wrapper layer that encapsulates the Vela log system, making the API consistent with the log API in Android. When we port Android applications or frameworks to Vela, we don't need to provide our own log integration anymore, we can use this module directly. Here is the structure of the log module:
|
||||
|
||||
```log
|
||||
android log api
|
||||
|
|
||||
\|/
|
||||
log wrapper
|
||||
|
|
||||
\|/
|
||||
vela log impl
|
||||
```
|
||||
|
||||
### 4. trace
|
||||
|
||||
This module mainly contains instrumentation tools for user-space programs. We can achieve instrumentation analysis by manually instrumenting in user programs. The atrace tool provided in trace is mainly used in conjunction with the instrumentation tools provided by the Vela system.
|
||||
|
||||
## Usage Guide
|
||||
|
||||
### 1. gdbus
|
||||
|
||||
1. To enable the `CONFIG_LIB_DBUS` build option
|
||||
|
||||
2. Instantiation
|
||||
|
||||
GDBus is an interface module and requires the caller to instantiate it and pass the instantiated object when calling the GDBus interface.
|
||||
The caller needs to create a structure. The members should at least include `gdbus Connection` and `client` instance objects, similar to the following:
|
||||
|
||||
```cpp
|
||||
typedef struct {
|
||||
DBusConnection* connection;
|
||||
GDBusClient* client;
|
||||
GDBusProxy* dbus_proxy[USER_SUPPORT_PROXY_MAX];
|
||||
bool client_ready;
|
||||
} dbus_context;
|
||||
|
||||
dbus_context* ctx = malloc(sizeof(dbus_context));
|
||||
|
||||
// Create a DbusConnection instance
|
||||
ctx->connection = g_dbus_setup_private(DBUS_BUS_SYSTEM, NULL, NULL);
|
||||
|
||||
// Set the disconnection function for the Connection. Otherwise, when the dbus Connection exits, the current process will exit synchronously. After adding this function, the current process will not exit.
|
||||
g_dbus_set_disconnect_function(ctx->connection, system_dbus_disconnected, callback, NULL);
|
||||
|
||||
// Set the client name for the current dbus Connection
|
||||
dbus_request_name(ctx->connection, client_name, &err);
|
||||
ctx->client = g_dbus_client_new(ctx->connection, OFONO_SERVICE, OFONO_MANAGER_PATH);
|
||||
|
||||
// Set the property filter function for the proxy.
|
||||
// ofono_interface_proxy_added listens for proxy addition and caches the required proxy instances on demand for subsequent operations on the properties and methods of this proxy.
|
||||
// ofono_interface_proxy_removed listens for proxy removal and synchronously clears the cached proxy instances.
|
||||
// In the object_filter function, set which proxies do not need to read property attributes.
|
||||
// ofono_property_changed listens for changes in properties and caches the property values of the corresponding proxies on demand.
|
||||
g_dbus_client_set_proxy_handlers(dbus_client, ofono_interface_proxy_added,
|
||||
ofono_interface_proxy_removed,
|
||||
object_filter,
|
||||
ofono_property_changed, tele);
|
||||
|
||||
// Set the callback function for successful dbus client connection on_dbus_client_ready
|
||||
g_dbus_client_set_ready_watch(ctx->client, on_dbus_client_ready, cbd)
|
||||
```
|
||||
|
||||
3. Exit and release
|
||||
|
||||
When the caller process exits, it needs to release the `dbus Connection` instance. Execute the following code:
|
||||
```cpp
|
||||
g_dbus_client_unref(ctx->client);
|
||||
dbus_connection_close(ctx->connection);
|
||||
dbus_connection_unref(ctx->connection);
|
||||
```
|
||||
|
||||
4. Call the interface
|
||||
|
||||
Call the interface, pass in the instance of `voice manager proxy`, and initiate a dial request.
|
||||
|
||||
```cpp
|
||||
g_dbus_proxy_method_call(ctx->dbus_proxy[VOICE_MANAGER], "Dial", dial_setup,
|
||||
dial_reply, param, dial_destory)
|
||||
```
|
||||
|
||||
### 2. kvdb
|
||||
|
||||
`kvdb` itself has multiple usage forms. We can integrate it directly in the code or use it as a command-line program in `nsh`.
|
||||
|
||||
#### 2.1 Example of direct integration in code
|
||||
|
||||
The following is a demo of the interface provided in `kvdb` for monitoring key/value changes. For simple and complex scenarios, two sets of APIs are provided.
|
||||
|
||||
1. Simple scenario: Only one `key` can be monitored.
|
||||
|
||||
```cpp
|
||||
int main(void)
|
||||
{
|
||||
char newkey[PROPERTY_KEY_MAX];
|
||||
char newvalue[PROPERTY_VALUE_MAX];
|
||||
int ret = property_wait("tsetkey", newkey, newvalue, -1);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("property_wait failed, ret=%d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
printf("the new key: %s\n", newkey);
|
||||
printf("the new value: %s\n", newvalue);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
2. Complex scenario: Supports polling. Users can freely monitor multiple `keys`.
|
||||
|
||||
```cpp
|
||||
int main(void)
|
||||
{
|
||||
struct pollfd fds[2];
|
||||
char newkey[PROPERTY_KEY_MAX];
|
||||
char newvalue[PROPERTY_VALUE_MAX];
|
||||
int fd1 = property_monitor_open("monitorkey*");
|
||||
int fd2 = property_monitor_open("testkey");
|
||||
|
||||
fds[0].fd = fd1;
|
||||
fds[0].events = POLLIN;
|
||||
fds[1].fd = fd2;
|
||||
fds[1].events = POLLIN;
|
||||
int ret= poll(fds, 2, -1);
|
||||
if (ret <= 0)
|
||||
goto out;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if ((fds[i].revents & POLLIN) == 0)
|
||||
continue;
|
||||
|
||||
ret = property_monitor_read(fds[i].fd, newkey, newvalue);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
printf("the new key: %s\n", newkey);
|
||||
printf("the new value: %s\n", newvalue);
|
||||
}
|
||||
|
||||
out:
|
||||
property_monitor_close(fd1);
|
||||
property_monitor_close(fd2);
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. log
|
||||
|
||||
1. To enable the `CONFIG_ANDROID_LIBBASE` build option
|
||||
2. Then we can directly use the standard Android log API in the program to collect and print logs.
|
||||
|
||||
```c
|
||||
#include <log/log.h>
|
||||
|
||||
// the tag for the ALOGI
|
||||
#define LOG_TAG "MyAppTag"
|
||||
|
||||
int main() {
|
||||
// print log with custom priority level
|
||||
__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "Formatted number: %d", 42);
|
||||
|
||||
// Using ALOGI macro to print info level log
|
||||
ALOGI("ALOGI: A log message from my app.");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 4. trace
|
||||
|
||||
1. To enable the `CONFIG_SCHED_INSTRUMENTATION_DUMP` and `CONFIG_ATRACE` build options
|
||||
|
||||
2. Add the instrumentation point in the program:
|
||||
|
||||
```cpp
|
||||
// define the tag for tracing
|
||||
#define ATRACE_TAG ATRACE_TAG_ALWAYS
|
||||
#include <cutils/trace.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// instrument the current function
|
||||
ATRACE_BEGIN("hello_main");
|
||||
sleep(1);
|
||||
ATRACE_INSTANT("printf");
|
||||
printf("hello world!");
|
||||
// end instrumentation
|
||||
ATRACE_END();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
3. Show the instrumentation result with `trace dump` tool:
|
||||
|
||||
```log
|
||||
hello-7 [0] 3.187400000: sched_wakeup_new: comm=hello pid=7 target_cpu=0
|
||||
hello-7 [0] 3.187400000: tracing_mark_write: B|7|hello_main
|
||||
hello-7 [0] 4.197700000: tracing_mark_write: I|7|printf
|
||||
hello-7 [0] 4.187700000: tracing_mark_write: E|7|hello_main
|
||||
```
|
||||
|
||||
In addition, the output result of atrace can also directly use the [perfetto](https://ui.perfetto.dev/) tool to view the timing diagram of the trace in a visual form.
|
||||
|
|
@ -0,0 +1,281 @@
|
|||
# Utils
|
||||
|
||||
[[English](./README.md) | [中文](./README_zh-cn.md)]
|
||||
|
||||
## 项目概览
|
||||
|
||||
当前目录下包含的主要是framework当中提供的一些常用工具实现.
|
||||
| 工具 | 工具简要描述 |
|
||||
| -- | -- |
|
||||
| `gdbus` | 对`D-Bus`接口封装, 方便操作`D-Bus` |
|
||||
| `kvdb` | 基于本地数据库的`键值对`数据存取接口 |
|
||||
| `log` | 提供和`Android`平台兼容的`Log API`接口<br>用于在`Vela`当中直接使用`Android` LOG API |
|
||||
| `trace` | 提供用于用户空间程序的打点工具 |
|
||||
|
||||
## 项目描述
|
||||
|
||||
### 1. gdbus
|
||||
|
||||
`gdbus`模块是对`D-Bus`接口的进一步封装的API模块. 由于`D-Bus`接口使用比较复杂, 在实际模块使用时, 通过将`D-Bus`接口再次封装, 会方便使用. `gdbus`通过提供更简易操作的API接口,方便我们去操作`D-Bus`.
|
||||
|
||||
### 2. kvdb
|
||||
|
||||
#### 2.1 简要介绍
|
||||
|
||||
`kvdb`提供了一套本地数据库的读写接口, `API`设计参考`Android`的`properties`存取规范, 同时提供了命令行工具以方便本地快速调试.
|
||||
`Vela`中的`kvdb`支持本地永久化存储以及跨核调用(分别需要Unix domain socket和rpmsg socket支持), 需要永久存储到文件的键值需要以`"persist."`开头.
|
||||
`kvdb`底层实现包含了三种机制:
|
||||
1. 基于开源的`UnQLite`数据库, 依赖于数据库;
|
||||
2. 另外一种是基于`MTD CONFIG` (目前仅用于nor flash);
|
||||
3. 最后一种基于`file`文件;
|
||||
|
||||
> `kvdb`一般接口说明: [frameworks/utils/include/kvdb.h](include/kvdb.h)
|
||||
|
||||
#### 2.2 `kvdb`的常见配置说明
|
||||
|
||||
| kvdb配置 | 说明 |
|
||||
| -- | -- |
|
||||
| CONFIG_KVDB_PRIORITY | KVDB任务优先级, 默认为系统默认值 |
|
||||
| CONFIG_KVDB_STACKSIZE | KVDB栈空间分配,默认为系统默认值 |
|
||||
| CONFIG_KVDB_SERVER | KVDB SERVER模式. 表示当前CPU是否为读写文件的主CPU, 为n则只调用其他CPU上的KVDB |
|
||||
| CONFIG_KVDB_DIRECT | KVDB DIRECT模式: 在无需rpmsg socket的场景(无需跨核),可使用此模式<br>CONFIG_KVDB_DIRECT 与 CONFIG_KVDB_SERVER 两种模式只能二选一 |
|
||||
| CONFIG_KVDB_COMMIT_INTERVAL | KVDB提交间隔 (秒), 默认为5<br>KVDB有内部缓存, 提交后才真正写入文件, 如果提交persist类型的kv后, `CONFIG_KVDB_COMMIT_INTERVAL`时间前就下电, 数据不会真正写入到`persist.db`文件中. `CONFIG_KVDB_COMMIT_INTERVAL`时间设置的越短, `kvdb`将内部缓存写入文件越频繁, 会一定程度上影响系统性能 |
|
||||
| CONFIG_KVDB_SOURCE_PATH | KVDB默认值加载路径,默认为`"/etc/build.prop"`, 支持多个路径, 用`;`分隔即可. 每次开机启动会自动从该文件加载KV值 |
|
||||
| CONFIG_KVDB_UNQLITE | 配置使用 unqlite database 存储 kv |
|
||||
| CONFIG_KVDB_NVS | 配置使用 nvs 存储 kv |
|
||||
| CONFIG_KVDB_FILE | 配置使用 file 存储 kv |
|
||||
|
||||
> `CONFIG_KVDB_UNQLITE`,`CONFIG_KVDB_NVS`, `CONFIG_KVDB_FILE` 三种数据存储的backend只能三选一
|
||||
|
||||
### 3. log
|
||||
|
||||
log模块本身是一个wrapper层,底层将Vela log系统进行了封装,封装成的API是和Android当中的log API一致, 当我们将Android应用或者框架移植到Vela当中, 不需要再提供自己的log对接,直接使用当前模块就可以了.
|
||||
以下是log模块的结构:
|
||||
|
||||
```log
|
||||
android log api
|
||||
|
|
||||
\|/
|
||||
log wrapper
|
||||
|
|
||||
\|/
|
||||
vela log impl
|
||||
```
|
||||
|
||||
### 4. trace
|
||||
|
||||
这个模块当中包含的主要是用于用户空间程序的打点工具. 我们可以通过在用户程序当中手动插桩来实现打点分析.
|
||||
trace当中提供的atrace工具主要是配合Vela系统提供的打点工具来使用的.
|
||||
|
||||
## 使用指南
|
||||
|
||||
### 1. gdbus
|
||||
|
||||
1. 打开`CONFIG_LIB_DBUS`选项
|
||||
|
||||
2. 实例化
|
||||
|
||||
gdbus为接口模块, 需要调用者实例化, 并在调用gdbus接口时, 传入实例化对象.
|
||||
调用者需要创建一个结构体, 成员至少有`gdbus Connection`和`client`实例对象, 类似如下:
|
||||
|
||||
```cpp
|
||||
typedef struct {
|
||||
DBusConnection* connection;
|
||||
GDBusClient* client;
|
||||
GDBusProxy* dbus_proxy[USER_SUPPORT_PROXY_MAX];
|
||||
bool client_ready;
|
||||
} dbus_context;
|
||||
|
||||
dbus_context* ctx = malloc(sizeof(dbus_context));
|
||||
|
||||
//创建DbusConnection实例
|
||||
ctx->connection = g_dbus_setup_private(DBUS_BUS_SYSTEM, NULL, NULL);
|
||||
|
||||
//设置Connection退出函数,否则dbus Connection退出时,会同步退出当前进程,添加后,则不会退出当前进程
|
||||
g_dbus_set_disconnect_function(ctx->connection, system_dbus_disconnected, callback, NULL);
|
||||
|
||||
//设置当前dbus Connection的client名字
|
||||
dbus_request_name(ctx->connection, client_name, &err);
|
||||
ctx->client = g_dbus_client_new(ctx->connection, OFONO_SERVICE, OFONO_MANAGER_PATH);
|
||||
|
||||
//设置proxy的property的filter函数,
|
||||
//ofono_interface_proxy_added 监听proxy添加,按需缓存需要使用的proxy实例,供后续操作这个proxy的属性和method
|
||||
//ofono_interface_proxy_removed 监听proxy删除,同步清除缓存的proxy实例
|
||||
//在object_filter函数中设置哪些proxy不需要读取property属性
|
||||
//ofono_property_changed 监听proper属性的变化,按需缓存对应proxy的proper值。
|
||||
g_dbus_client_set_proxy_handlers(dbus_client, ofono_interface_proxy_added,
|
||||
ofono_interface_proxy_removed,
|
||||
object_filter,
|
||||
ofono_property_changed, tele);
|
||||
|
||||
//设置dbus client连接成功的回调函数on_dbus_client_ready
|
||||
g_dbus_client_set_ready_watch(ctx->client, on_dbus_client_ready, cbd)
|
||||
```
|
||||
|
||||
3. 退出释放
|
||||
|
||||
调用者进程退出时, 需要释放`dbus Connection`实例, 执行如下代码:
|
||||
|
||||
```cpp
|
||||
g_dbus_client_unref(ctx->client);
|
||||
dbus_connection_close(ctx->connection);
|
||||
dbus_connection_unref(ctx->connection);
|
||||
```
|
||||
|
||||
4. 调用接口
|
||||
|
||||
调用接口, 传入`voice manager proxy`实例, 发起dial请求:
|
||||
|
||||
```cpp
|
||||
g_dbus_proxy_method_call(ctx->dbus_proxy[VOICE_MANAGER], "Dial", dial_setup,
|
||||
dial_reply, param, dial_destory)
|
||||
```
|
||||
|
||||
### 2. kvdb
|
||||
|
||||
`kvdb`本身有多种使用形式,我们可以直接在代码当中集成,也可以直接在`nsh`当中以命令行程序的方式来使用.
|
||||
|
||||
#### 2.1 直接在代码当中集成的示例
|
||||
|
||||
以下是针对`kvdb`当中提供的监控key/value变化的接口的demo, 针对简单和复杂场景,提供了2套API:
|
||||
|
||||
1. 简单场景: 只能监控一个`key`
|
||||
|
||||
```cpp
|
||||
int main(void)
|
||||
{
|
||||
char newkey[PROPERTY_KEY_MAX];
|
||||
char newvalue[PROPERTY_VALUE_MAX];
|
||||
int ret = property_wait("tsetkey", newkey, newvalue, -1);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("property_wait failed, ret=%d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
printf("the new key: %s\n", newkey);
|
||||
printf("the new value: %s\n", newvalue);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
2. 复杂场景: 支持poll,用户自由监控多个key
|
||||
|
||||
```cpp
|
||||
int main(void)
|
||||
{
|
||||
struct pollfd fds[2];
|
||||
char newkey[PROPERTY_KEY_MAX];
|
||||
char newvalue[PROPERTY_VALUE_MAX];
|
||||
int fd1 = property_monitor_open("monitorkey*");
|
||||
int fd2 = property_monitor_open("testkey");
|
||||
|
||||
fds[0].fd = fd1;
|
||||
fds[0].events = POLLIN;
|
||||
fds[1].fd = fd2;
|
||||
fds[1].events = POLLIN;
|
||||
int ret= poll(fds, 2, -1);
|
||||
if (ret <= 0)
|
||||
goto out;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if ((fds[i].revents & POLLIN) == 0)
|
||||
continue;
|
||||
|
||||
ret = property_monitor_read(fds[i].fd, newkey, newvalue);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
printf("the new key: %s\n", newkey);
|
||||
printf("the new value: %s\n", newvalue);
|
||||
}
|
||||
|
||||
out:
|
||||
property_monitor_close(fd1);
|
||||
property_monitor_close(fd2);
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2 在nsh当中以命令行的形式来使用
|
||||
|
||||
KVDB提供了getprop和setprop两个命令行程序供用户使用,用户可以使用getprop和setprop方便地查看已存在的KV或是设置新的KV。
|
||||
这两个命令行在使能KVDB后默认开启
|
||||
getprop:打印出设置的property
|
||||
- nsh> getprop:列出当前所有props
|
||||
- nsh> getprop <key>: 打印出<key>对应的prop
|
||||
|
||||
setprop:设置或者删除property
|
||||
- nsh> setprop <key> : 删除<key>对应的prop
|
||||
- nsh> setprop <key> <value> : 保存<key>:<value>到数据库
|
||||
|
||||
下面是具体的使用示例:
|
||||
|
||||
```log
|
||||
nsh> setprop name peter #添加名为name值为peter的键值对, 掉电消失
|
||||
nsh> setprop persist.name1 peter1 #添加名为name1值为peter1的键值对, 掉电不消失
|
||||
|
||||
nsh> getprop #查看所有键值对
|
||||
name: peter
|
||||
|
||||
nsh> setprop name #删除名为name的键值对
|
||||
nsh> getprop name
|
||||
```
|
||||
|
||||
### 3. log
|
||||
|
||||
1. 打开`CONFIG_ANDROID_LIBBASE`
|
||||
2. 然后我们可以在程序当中直接使用标准的android log api来收集打印日志.
|
||||
|
||||
```cpp
|
||||
#include <log/log.h>
|
||||
|
||||
#define LOG_TAG "MyAppTag"
|
||||
|
||||
int main() {
|
||||
// 自定义优先级打印 log
|
||||
__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "Formatted number: %d", 42);
|
||||
|
||||
// 使用宏打印 INFO 级别 log
|
||||
ALOGI("ALOGI: A log message from my app.");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 4. trace
|
||||
|
||||
1. 打开`CONFIG_SCHED_INSTRUMENTATION_DUMP`和`CONFIG_ATRACE`选项
|
||||
|
||||
2. 在程序当中需要跟踪的地方添加上打点信息:
|
||||
|
||||
```cpp
|
||||
// 使用是添加头文件,必须添加TAG
|
||||
#define ATRACE_TAG ATRACE_TAG_ALWAYS
|
||||
#include <cutils/trace.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 对当前函数进行插桩
|
||||
ATRACE_BEGIN("hello_main");
|
||||
sleep(1);
|
||||
ATRACE_INSTANT("printf");
|
||||
printf("hello world!");
|
||||
// 结束插桩
|
||||
ATRACE_END();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
3. 使用trace dump工具查看打点输出的结果:
|
||||
|
||||
```log
|
||||
hello-7 [0] 3.187400000: sched_wakeup_new: comm=hello pid=7 target_cpu=0
|
||||
hello-7 [0] 3.187400000: tracing_mark_write: B|7|hello_main
|
||||
hello-7 [0] 4.197700000: tracing_mark_write: I|7|printf
|
||||
hello-7 [0] 4.187700000: tracing_mark_write: E|7|hello_main
|
||||
```
|
||||
|
||||
另外就是 atrace 的输出结果也是可以直接使用 [perfetto](https://ui.perfetto.dev/) 工具以可视化的形式来查看 trace 的时序图的.
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Xiaomi Technologies Co., Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This file is part of the Xiaomi project.
|
||||
*
|
||||
* This source code is licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <uv_ext.h>
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
#include <dbus/dbus-hash.h>
|
||||
|
||||
#include "syslog.h"
|
||||
|
||||
#include "gdbus.h"
|
||||
|
||||
#define error(fmt, ...) \
|
||||
do { \
|
||||
syslog(LOG_ERR, fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define warning(fmt, ...) \
|
||||
do { \
|
||||
syslog(LOG_WARNING, fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define info(fmt, ...) \
|
||||
do { \
|
||||
syslog(LOG_INFO, fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define debug(fmt, ...) \
|
||||
do { \
|
||||
syslog(LOG_DEBUG, fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
struct GDBusClient {
|
||||
int ref_count;
|
||||
DBusConnection* dbus_conn;
|
||||
char* service_name;
|
||||
char* base_path;
|
||||
char* root_path;
|
||||
guint watch;
|
||||
guint added_watch;
|
||||
guint removed_watch;
|
||||
struct ptr_array* match_rules;
|
||||
DBusPendingCall* pending_call;
|
||||
DBusPendingCall* get_objects_call;
|
||||
GDBusWatchFunction connect_func;
|
||||
void* connect_data;
|
||||
GDBusWatchFunction disconn_func;
|
||||
gboolean connected;
|
||||
void* disconn_data;
|
||||
GDBusMessageFunction signal_func;
|
||||
void* signal_data;
|
||||
GDBusProxyFunction proxy_added;
|
||||
GDBusProxyFunction proxy_removed;
|
||||
GDBusProxyPropertyFilterFunction proxy_property_filter;
|
||||
GDBusProxyFilterFunction proxy_filter;
|
||||
GDBusClientFunction ready;
|
||||
void* ready_data;
|
||||
gboolean ready_called;
|
||||
GDBusPropertyFunction property_changed;
|
||||
void* user_data;
|
||||
DBusList* proxy_list;
|
||||
gboolean standard;
|
||||
gboolean getting_object_call;
|
||||
uv_async_queue_t async_queue;
|
||||
uv_thread_t main_thread;
|
||||
GDBusWatch *watcher;
|
||||
};
|
||||
|
||||
struct GDBusProxy {
|
||||
int ref_count;
|
||||
GDBusClient* client;
|
||||
char* obj_path;
|
||||
char* interface;
|
||||
DBusHashTable* prop_list;
|
||||
guint watch;
|
||||
guint watch_non_standard;
|
||||
GDBusPropertyFunction prop_func;
|
||||
void* prop_data;
|
||||
GDBusProxyFunction removed_func;
|
||||
void* removed_data;
|
||||
DBusPendingCall* get_all_call;
|
||||
gboolean pending;
|
||||
gboolean filter_first;
|
||||
gboolean getting_all_prop;
|
||||
};
|
||||
|
||||
GDBusWatch* new_dbus_watch(DBusConnection* connection);
|
||||
void free_dbus_watch(GDBusWatch* watcher);
|
||||
void dbus_watch_set_connection_state(GDBusWatch* watcher, gboolean closed);
|
||||
|
||||
int dbus_polkit_check_authorization(DBusConnection* conn,
|
||||
const char* action, gboolean allow_interaction,
|
||||
void (*callback)(dbus_bool_t, void*),
|
||||
void* user_data, int timeout_ms);
|
||||
|
|
@ -0,0 +1,964 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Xiaomi Technologies Co., Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This file is part of the Xiaomi project.
|
||||
*
|
||||
* This source code is licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __GDBUS_H
|
||||
#define __GDBUS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
typedef void* gpointer;
|
||||
typedef int gboolean;
|
||||
typedef unsigned int guint;
|
||||
typedef uint32_t guint32;
|
||||
|
||||
#define g_dbus_setup_bus dbus_setup_bus
|
||||
#define g_dbus_setup_private dbus_setup_private
|
||||
#define g_dbus_request_name dbus_request_name
|
||||
#define g_dbus_set_flags dbus_set_flags
|
||||
#define g_dbus_get_flags dbus_get_flags
|
||||
#define g_dbus_register_interface dbus_register_interface
|
||||
#define g_dbus_unregister_interface dbus_unregister_interface
|
||||
#define g_dbus_register_security dbus_register_security
|
||||
#define g_dbus_unregister_security dbus_unregister_security
|
||||
#define g_dbus_pending_success dbus_pending_success
|
||||
#define g_dbus_pending_error dbus_pending_error
|
||||
#define g_dbus_pending_error_valist dbus_pending_error_valist
|
||||
#define g_dbus_create_error dbus_create_error
|
||||
#define g_dbus_create_error_valist dbus_create_error_valist
|
||||
#define g_dbus_create_reply dbus_create_reply
|
||||
#define g_dbus_create_reply_valist dbus_create_reply_valist
|
||||
#define g_dbus_send_message dbus_send_message
|
||||
#define g_dbus_send_message_with_reply dbus_send_message_with_reply
|
||||
#define g_dbus_send_error dbus_send_error
|
||||
#define g_dbus_send_error_valist dbus_send_error_valist
|
||||
#define g_dbus_send_reply dbus_send_reply
|
||||
#define g_dbus_send_reply_valist dbus_send_reply_valist
|
||||
#define g_dbus_emit_signal dbus_emit_signal
|
||||
#define g_dbus_emit_signal_valist dbus_emit_signal_valist
|
||||
#define g_dbus_pending_property_success dbus_pending_property_success
|
||||
#define g_dbus_pending_property_error_valist dbus_pending_property_error_valist
|
||||
#define g_dbus_pending_property_error dbus_pending_property_error
|
||||
#define g_dbus_emit_property_changed dbus_emit_property_changed
|
||||
#define g_dbus_emit_property_changed_full dbus_emit_property_changed_full
|
||||
#define g_dbus_get_properties dbus_get_properties
|
||||
#define g_dbus_attach_object_manager dbus_attach_object_manager
|
||||
#define g_dbus_detach_object_manager dbus_detach_object_manager
|
||||
#define g_dbus_proxy_new dbus_proxy_new
|
||||
#define g_dbus_proxy_ref dbus_proxy_ref
|
||||
#define g_dbus_proxy_unref dbus_proxy_unref
|
||||
#define g_dbus_proxy_get_path dbus_proxy_get_path
|
||||
#define g_dbus_proxy_get_interface dbus_proxy_get_interface
|
||||
#define g_dbus_proxy_get_property dbus_proxy_get_property
|
||||
#define g_dbus_proxy_get_property_basic dbus_proxy_get_property_basic
|
||||
#define g_dbus_proxy_get_property_iter_cb dbus_proxy_get_property_iter_cb
|
||||
#define g_dbus_proxy_lookup dbus_proxy_lookup
|
||||
#define g_dbus_proxy_path_lookup dbus_proxy_path_lookup
|
||||
#define g_dbus_proxy_refresh_property dbus_proxy_refresh_property
|
||||
#define g_dbus_proxy_set_property_basic dbus_proxy_set_property_basic
|
||||
#define g_dbus_proxy_set_property_array dbus_proxy_set_property_array
|
||||
#define g_dbus_dict_append_entry dbus_dict_append_entry
|
||||
#define g_dbus_dict_append_basic_array dbus_dict_append_basic_array
|
||||
#define g_dbus_dict_append_array dbus_dict_append_array
|
||||
#define g_dbus_proxy_method_call dbus_proxy_method_call
|
||||
#define g_dbus_proxy_set_property_watch dbus_proxy_set_property_watch
|
||||
#define g_dbus_proxy_remove_property_watch dbus_proxy_remove_property_watch
|
||||
#define g_dbus_proxy_set_removed_watch dbus_proxy_set_removed_watch
|
||||
#define g_dbus_client_new dbus_client_new
|
||||
#define g_dbus_client_new_full dbus_client_new_full
|
||||
#define g_dbus_client_ref dbus_client_ref
|
||||
#define g_dbus_client_unref dbus_client_unref
|
||||
#define g_dbus_client_set_connect_watch dbus_client_set_connect_watch
|
||||
#define g_dbus_client_set_disconnect_watch dbus_client_set_disconnect_watch
|
||||
#define g_dbus_client_set_signal_watch dbus_client_set_signal_watch
|
||||
#define g_dbus_client_set_ready_watch dbus_client_set_ready_watch
|
||||
#define g_dbus_client_set_proxy_handlers dbus_client_set_proxy_handlers
|
||||
#define g_dbus_client_set_proxy_filter dbus_client_set_proxy_filter
|
||||
|
||||
typedef struct GDBusArgInfo GDBusArgInfo;
|
||||
typedef struct GDBusMethodTable GDBusMethodTable;
|
||||
typedef struct GDBusSignalTable GDBusSignalTable;
|
||||
typedef struct GDBusPropertyTable GDBusPropertyTable;
|
||||
typedef struct GDBusSecurityTable GDBusSecurityTable;
|
||||
|
||||
typedef void (*GDBusWatchFunction)(DBusConnection* connection,
|
||||
void* user_data);
|
||||
|
||||
typedef void (*GDBusMessageFunction)(DBusConnection* connection,
|
||||
DBusMessage* message, void* user_data);
|
||||
|
||||
typedef gboolean (*GDBusSignalFunction)(DBusConnection* connection,
|
||||
DBusMessage* message, void* user_data);
|
||||
|
||||
typedef void (*GDBusPropIterFunction)(DBusMessageIter* iter, void* value);
|
||||
|
||||
/**
|
||||
* @brief Set and connect to the specified DBus bus type
|
||||
*
|
||||
* @param type the type of DBus bus, which can be DBUS_BUS_SESSION or DBUS_BUS_SYSTEM
|
||||
* @param name The name of the DBus bus to connect to
|
||||
* @param error A DBusError structure used to store error information
|
||||
*
|
||||
* @return If successful, returns a pointer to DBusConnection; if failed, returns NULL
|
||||
*/
|
||||
DBusConnection* dbus_setup_bus(DBusBusType type, const char* name, DBusError* error);
|
||||
|
||||
/**
|
||||
* @brief Set up a private DBus connection
|
||||
*
|
||||
* This function is used to set up a private DBus connection. It accepts a DBusBusType
|
||||
* parameter that specifies the type of connection (session or system), a string parameter
|
||||
* that is the name of the DBus you want to connect to, and a DBusError pointer to store
|
||||
* any error information that may occur.
|
||||
* If the function succeeds, it returns a pointer to a DBusConnection, otherwise it returns NULL.
|
||||
*
|
||||
* @param type The type of DBus connection, which can be DBUS_BUS_SESSION or DBUS_BUS_SYSTEM
|
||||
* @param name The name of the DBus you want to connect to
|
||||
* @param error A DBusError structure used to store error information
|
||||
* @return Returns a DBusConnection pointer on success, or NULL on failure
|
||||
*/
|
||||
DBusConnection* dbus_setup_private(DBusBusType type, const char* name, DBusError* error);
|
||||
|
||||
/**
|
||||
* @brief Requests a specific D-Bus name on the specified DBus connection.
|
||||
*
|
||||
* @param connection Pointer to a DBusConnection object representing the connection for
|
||||
* which the name is to be requested.
|
||||
* @param name String of the D-Bus name to be requested.
|
||||
* @param error Pointer to a DBusError object for storing possible errors that may occur
|
||||
* during the name request.
|
||||
*
|
||||
* @return Returns a gboolean value of TRUE if the request succeeds or a gboolean value
|
||||
* of FALSE if the request fails.
|
||||
*/
|
||||
gboolean dbus_request_name(DBusConnection* connection, const char* name, DBusError* error);
|
||||
|
||||
typedef void (*GDBusDestroyFunction)(void* user_data);
|
||||
|
||||
typedef DBusMessage* (*GDBusMethodFunction)(DBusConnection* connection,
|
||||
DBusMessage* message, void* user_data);
|
||||
|
||||
typedef gboolean (*GDBusPropertyGetter)(const GDBusPropertyTable* property,
|
||||
DBusMessageIter* iter, void* data);
|
||||
|
||||
typedef guint32 GDBusPendingPropertySet;
|
||||
|
||||
typedef void (*GDBusPropertySetter)(const GDBusPropertyTable* property,
|
||||
DBusMessageIter* value, GDBusPendingPropertySet id,
|
||||
void* data);
|
||||
|
||||
typedef gboolean (*GDBusPropertyExists)(const GDBusPropertyTable* property,
|
||||
void* data);
|
||||
|
||||
typedef guint32 GDBusPendingReply;
|
||||
|
||||
typedef void (*GDBusSecurityFunction)(DBusConnection* connection,
|
||||
const char* action,
|
||||
gboolean interaction,
|
||||
GDBusPendingReply pending);
|
||||
|
||||
enum GDBusFlags {
|
||||
G_DBUS_FLAG_ENABLE_EXPERIMENTAL = (1 << 0),
|
||||
};
|
||||
|
||||
enum GDBusMethodFlags {
|
||||
G_DBUS_METHOD_FLAG_DEPRECATED = (1 << 0),
|
||||
G_DBUS_METHOD_FLAG_NOREPLY = (1 << 1),
|
||||
G_DBUS_METHOD_FLAG_ASYNC = (1 << 2),
|
||||
G_DBUS_METHOD_FLAG_EXPERIMENTAL = (1 << 3),
|
||||
};
|
||||
|
||||
enum GDBusSignalFlags {
|
||||
G_DBUS_SIGNAL_FLAG_DEPRECATED = (1 << 0),
|
||||
G_DBUS_SIGNAL_FLAG_EXPERIMENTAL = (1 << 1),
|
||||
};
|
||||
|
||||
enum GDBusPropertyFlags {
|
||||
G_DBUS_PROPERTY_FLAG_DEPRECATED = (1 << 0),
|
||||
G_DBUS_PROPERTY_FLAG_EXPERIMENTAL = (1 << 1),
|
||||
};
|
||||
|
||||
enum GDBusSecurityFlags {
|
||||
G_DBUS_SECURITY_FLAG_DEPRECATED = (1 << 0),
|
||||
G_DBUS_SECURITY_FLAG_BUILTIN = (1 << 1),
|
||||
G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION = (1 << 2),
|
||||
};
|
||||
|
||||
enum GDbusPropertyChangedFlags {
|
||||
G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH = (1 << 0),
|
||||
};
|
||||
|
||||
typedef enum GDBusMethodFlags GDBusMethodFlags;
|
||||
typedef enum GDBusSignalFlags GDBusSignalFlags;
|
||||
typedef enum GDBusPropertyFlags GDBusPropertyFlags;
|
||||
typedef enum GDBusSecurityFlags GDBusSecurityFlags;
|
||||
typedef enum GDbusPropertyChangedFlags GDbusPropertyChangedFlags;
|
||||
|
||||
struct GDBusArgInfo {
|
||||
const char* name;
|
||||
const char* signature;
|
||||
};
|
||||
|
||||
struct GDBusMethodTable {
|
||||
const char* name;
|
||||
GDBusMethodFunction function;
|
||||
GDBusMethodFlags flags;
|
||||
unsigned int privilege;
|
||||
const GDBusArgInfo* in_args;
|
||||
const GDBusArgInfo* out_args;
|
||||
};
|
||||
|
||||
struct GDBusSignalTable {
|
||||
const char* name;
|
||||
GDBusSignalFlags flags;
|
||||
const GDBusArgInfo* args;
|
||||
};
|
||||
|
||||
struct GDBusPropertyTable {
|
||||
const char* name;
|
||||
const char* type;
|
||||
GDBusPropertyGetter get;
|
||||
GDBusPropertySetter set;
|
||||
GDBusPropertyExists exists;
|
||||
GDBusPropertyFlags flags;
|
||||
};
|
||||
|
||||
struct GDBusSecurityTable {
|
||||
unsigned int privilege;
|
||||
const char* action;
|
||||
GDBusSecurityFlags flags;
|
||||
GDBusSecurityFunction function;
|
||||
};
|
||||
|
||||
#define GDBUS_ARGS(args...) \
|
||||
(const GDBusArgInfo[]) \
|
||||
{ \
|
||||
args, { } \
|
||||
}
|
||||
|
||||
#define GDBUS_METHOD(_name, _in_args, _out_args, _function) \
|
||||
.name = _name, \
|
||||
.in_args = _in_args, \
|
||||
.out_args = _out_args, \
|
||||
.function = _function
|
||||
|
||||
#define GDBUS_ASYNC_METHOD(_name, _in_args, _out_args, _function) \
|
||||
.name = _name, \
|
||||
.in_args = _in_args, \
|
||||
.out_args = _out_args, \
|
||||
.function = _function, \
|
||||
.flags = G_DBUS_METHOD_FLAG_ASYNC
|
||||
|
||||
#define GDBUS_DEPRECATED_METHOD(_name, _in_args, _out_args, _function) \
|
||||
.name = _name, \
|
||||
.in_args = _in_args, \
|
||||
.out_args = _out_args, \
|
||||
.function = _function, \
|
||||
.flags = G_DBUS_METHOD_FLAG_DEPRECATED
|
||||
|
||||
#define GDBUS_DEPRECATED_ASYNC_METHOD(_name, _in_args, _out_args, _function) \
|
||||
.name = _name, \
|
||||
.in_args = _in_args, \
|
||||
.out_args = _out_args, \
|
||||
.function = _function, \
|
||||
.flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_DEPRECATED
|
||||
|
||||
#define GDBUS_EXPERIMENTAL_METHOD(_name, _in_args, _out_args, _function) \
|
||||
.name = _name, \
|
||||
.in_args = _in_args, \
|
||||
.out_args = _out_args, \
|
||||
.function = _function, \
|
||||
.flags = G_DBUS_METHOD_FLAG_EXPERIMENTAL
|
||||
|
||||
#define GDBUS_EXPERIMENTAL_ASYNC_METHOD(_name, _in_args, _out_args, _function) \
|
||||
.name = _name, \
|
||||
.in_args = _in_args, \
|
||||
.out_args = _out_args, \
|
||||
.function = _function, \
|
||||
.flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_EXPERIMENTAL
|
||||
|
||||
#define GDBUS_NOREPLY_METHOD(_name, _in_args, _out_args, _function) \
|
||||
.name = _name, \
|
||||
.in_args = _in_args, \
|
||||
.out_args = _out_args, \
|
||||
.function = _function, \
|
||||
.flags = G_DBUS_METHOD_FLAG_NOREPLY
|
||||
|
||||
#define GDBUS_SIGNAL(_name, _args) \
|
||||
.name = _name, \
|
||||
.args = _args
|
||||
|
||||
#define GDBUS_DEPRECATED_SIGNAL(_name, _args) \
|
||||
.name = _name, \
|
||||
.args = _args, \
|
||||
.flags = G_DBUS_SIGNAL_FLAG_DEPRECATED
|
||||
|
||||
#define GDBUS_EXPERIMENTAL_SIGNAL(_name, _args) \
|
||||
.name = _name, \
|
||||
.args = _args, \
|
||||
.flags = G_DBUS_SIGNAL_FLAG_EXPERIMENTAL
|
||||
|
||||
void dbus_set_flags(int flags);
|
||||
int dbus_get_flags(void);
|
||||
|
||||
/**
|
||||
* @brief Register an interface on the specified DBus connection
|
||||
*
|
||||
* This function is used to register an interface on the specified DBus connection.
|
||||
* It requires the following parameters:
|
||||
* - connection: DBus connection object
|
||||
* - path: path of DBus interface
|
||||
* - name: name of DBus interface
|
||||
* - methods: a GDBusMethodTable object containing methods of interface
|
||||
* - signals: a GDBusSignalTable object containing signals of interface
|
||||
* - properties: a GDBusPropertyTable object containing properties of interface
|
||||
* - user_data: user data, which can be passed to callback function
|
||||
* - destroy: a GDBusDestroyFunction, which is called when the interface is deregistered
|
||||
* to clean up user data
|
||||
*
|
||||
* @param connection DBus connection object
|
||||
* @param path path of DBus interface
|
||||
* @param name name of DBus interface
|
||||
* @param methods GDBusMethodTable object containing interface methods
|
||||
* @param signals GDBusSignalTable object containing interface signals
|
||||
* @param properties GDBusPropertyTable object containing interface properties
|
||||
* @param user_data user data
|
||||
* @param destroy GDBusDestroyFunction for cleaning up user data
|
||||
* @return gboolean Returns TRUE if registration is successful, and FALSE if fails
|
||||
*/
|
||||
gboolean dbus_register_interface(DBusConnection* connection,
|
||||
const char* path, const char* name,
|
||||
const GDBusMethodTable* methods,
|
||||
const GDBusSignalTable* signals,
|
||||
const GDBusPropertyTable* properties,
|
||||
void* user_data,
|
||||
GDBusDestroyFunction destroy);
|
||||
|
||||
/**
|
||||
* @brief Unregister the specified interface from the specified DBus connection
|
||||
*
|
||||
* @param connection Pointer to DBusConnection, indicating the DBus connection of the
|
||||
* interface to be unregistered
|
||||
* @param path Path of the interface
|
||||
* @param name Name of the interface
|
||||
* @return If the interface is successfully unregistered, return gboolean TRUE, otherwise
|
||||
* return FALSE
|
||||
*/
|
||||
gboolean dbus_unregister_interface(DBusConnection* connection,
|
||||
const char* path, const char* name);
|
||||
|
||||
gboolean dbus_register_security(const GDBusSecurityTable* security);
|
||||
gboolean dbus_unregister_security(const GDBusSecurityTable* security);
|
||||
|
||||
void dbus_pending_success(DBusConnection* connection,
|
||||
GDBusPendingReply pending);
|
||||
void dbus_pending_error(DBusConnection* connection,
|
||||
GDBusPendingReply pending,
|
||||
const char* name, const char* format, ...)
|
||||
__attribute__((format(printf, 4, 5)));
|
||||
void dbus_pending_error_valist(DBusConnection* connection,
|
||||
GDBusPendingReply pending, const char* name,
|
||||
const char* format, va_list args);
|
||||
|
||||
DBusMessage* dbus_create_error(DBusMessage* message, const char* name,
|
||||
const char* format, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
DBusMessage* dbus_create_error_valist(DBusMessage* message, const char* name,
|
||||
const char* format, va_list args);
|
||||
DBusMessage* dbus_create_reply(DBusMessage* message, int type, ...);
|
||||
DBusMessage* dbus_create_reply_valist(DBusMessage* message,
|
||||
int type, va_list args);
|
||||
|
||||
/**
|
||||
* @brief Send a message to the specified DBus connection
|
||||
*
|
||||
* @param connection Pointer to the DBusConnection object, indicating the DBus connection
|
||||
* to send the message
|
||||
* @param message Pointer to the DBusMessage object, indicating the message to send
|
||||
*
|
||||
* @return If the sending is successful, return the gboolean type with a value of TRUE;
|
||||
* if the sending fails, return the gboolean type with a value of FALSE
|
||||
*/
|
||||
gboolean dbus_send_message(DBusConnection* connection, DBusMessage* message);
|
||||
|
||||
/**
|
||||
* @brief Send a message to the specified DBus connection and wait for a reply,
|
||||
* non-blocking call
|
||||
*
|
||||
* @param connection Pointer to the DBusConnection object, indicating the DBus connection
|
||||
* @param message Pointer to the DBusMessage object, indicating the message to be sent
|
||||
* @param call Pointer to the pointer to the DBusPendingCall object, used to store the
|
||||
* handle of the asynchronous call
|
||||
* @param timeout Timeout for waiting for a reply after sending a message, in milliseconds
|
||||
*
|
||||
* @return If the message is successfully sent and the DBusPendingCall object instance is
|
||||
* replied, the return value of the gboolean type is TRUE; otherwise, it returns FALSE
|
||||
*/
|
||||
gboolean dbus_send_message_with_reply(DBusConnection* connection, DBusMessage* message,
|
||||
DBusPendingCall** call, int timeout);
|
||||
|
||||
gboolean dbus_send_error(DBusConnection* connection, DBusMessage* message,
|
||||
const char* name, const char* format, ...)
|
||||
__attribute__((format(printf, 4, 5)));
|
||||
gboolean dbus_send_error_valist(DBusConnection* connection,
|
||||
DBusMessage* message, const char* name,
|
||||
const char* format, va_list args);
|
||||
gboolean dbus_send_reply(DBusConnection* connection,
|
||||
DBusMessage* message, int type, ...);
|
||||
gboolean dbus_send_reply_valist(DBusConnection* connection,
|
||||
DBusMessage* message, int type, va_list args);
|
||||
|
||||
gboolean dbus_emit_signal(DBusConnection* connection,
|
||||
const char* path, const char* interface,
|
||||
const char* name, int type, ...);
|
||||
gboolean dbus_emit_signal_valist(DBusConnection* connection,
|
||||
const char* path, const char* interface,
|
||||
const char* name, int type, va_list args);
|
||||
|
||||
typedef struct GDBusWatch GDBusWatch;
|
||||
|
||||
/**
|
||||
* Adds a service watch to monitor the specified D-Bus service name
|
||||
*
|
||||
* @param watcher The GDBusWatch instance to add the watch to
|
||||
* @param name The D-Bus service name to watch (e.g. "org.freedesktop.DBus")
|
||||
* @param connect Callback function when service connects
|
||||
* @param disconnect Callback function when service disconnects
|
||||
* @param user_data User data passed to callbacks
|
||||
* @param destroy Destroy notification callback for user_data
|
||||
* @return Watch ID that can be used to remove the watch
|
||||
*/
|
||||
guint dbus_add_service_watch(GDBusWatch* watcher, const char* name,
|
||||
GDBusWatchFunction connect, GDBusWatchFunction disconnect,
|
||||
void* user_data, GDBusDestroyFunction destroy);
|
||||
|
||||
/**
|
||||
* Adds a disconnect watch for the specified D-Bus name
|
||||
*
|
||||
* @param watcher The GDBusWatch instance to add the watch to
|
||||
* @param name The D-Bus name to monitor for disconnection
|
||||
* @param function Callback function when name disconnects
|
||||
* @param user_data User data passed to callback
|
||||
* @param destroy Destroy notification callback for user_data
|
||||
* @return Watch ID that can be used to remove the watch
|
||||
*/
|
||||
guint dbus_add_service_disconnect_watch(GDBusWatch* watcher, const char* name,
|
||||
GDBusWatchFunction function, void* user_data, GDBusDestroyFunction destroy);
|
||||
|
||||
/**
|
||||
* @brief Add a signal monitor to the DBus connection
|
||||
*
|
||||
* @param connection: Pointer to DBusConnection, indicating the DBus connection to which
|
||||
* the signal monitor is to be added
|
||||
* @param sender: The name of the DBus client that sends the signal. If it is NULL, the
|
||||
* signals of all clients are monitored
|
||||
* @param path: The path of the signal. If it is NULL, the signals of all paths are
|
||||
* monitored
|
||||
* @param interface: The interface of the signal. If it is NULL, the signals of all
|
||||
* interfaces are monitored
|
||||
* @param member: The member of the signal, that is, the specific name of the signal.
|
||||
* If it is NULL, the signals of all members are monitored
|
||||
* @param function: The function called when the signal is received receives the
|
||||
* following parameters:
|
||||
* - DBusConnection*: DBus connection
|
||||
* - const char*: The sender of the signal
|
||||
* - const char*: The path of the signal
|
||||
* - const char*: The interface of the signal
|
||||
* - const char*: The member of the signal
|
||||
* - void*: User data
|
||||
* @param user_data: User data, which will be passed to the function when calling the
|
||||
* function
|
||||
* @param destroy: The function called when the signal monitor is removed to destroy
|
||||
* user_data. If it is NULL, no action is performed.
|
||||
* @return: Returns an unsigned integer representing the ID of the newly created signal
|
||||
* monitor.
|
||||
*/
|
||||
guint dbus_add_signal_watch(GDBusWatch* watcher,
|
||||
const char* sender, const char* path,
|
||||
const char* interface, const char* member,
|
||||
GDBusSignalFunction function, void* user_data,
|
||||
GDBusDestroyFunction destroy);
|
||||
|
||||
guint dbus_add_properties_watch(GDBusWatch* watcher,
|
||||
const char* sender, const char* path,
|
||||
const char* interface,
|
||||
GDBusSignalFunction function, void* user_data,
|
||||
GDBusDestroyFunction destroy);
|
||||
|
||||
/**
|
||||
* @brief Remove a specific watch from the specified DBus connection
|
||||
*
|
||||
* @param connection Pointer to DBusConnection, indicating the connection from which the
|
||||
* watch is to be removed
|
||||
* @param tag Indicates the unique identifier of the watch to be removed
|
||||
*
|
||||
* @return gboolean The function returns TRUE if executed successfully, otherwise returns
|
||||
* FALSE
|
||||
*/
|
||||
gboolean dbus_remove_watch(GDBusWatch* watcher, guint tag);
|
||||
|
||||
void dbus_remove_all_watches(GDBusWatch* watcher);
|
||||
|
||||
void dbus_pending_property_success(GDBusPendingPropertySet id);
|
||||
void dbus_pending_property_error_valist(GDBusPendingReply id,
|
||||
const char* name, const char* format, va_list args);
|
||||
void dbus_pending_property_error(GDBusPendingReply id, const char* name,
|
||||
const char* format, ...);
|
||||
|
||||
/*
|
||||
* Note that when multiple properties for a given object path are changed
|
||||
* in the same mainloop iteration, they will be grouped with the last
|
||||
* property changed. If this behaviour is undesired, use
|
||||
* dbus_emit_property_changed_full() with the
|
||||
* G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH flag, causing the signal to ignore
|
||||
* any grouping.
|
||||
*/
|
||||
void dbus_emit_property_changed(DBusConnection* connection,
|
||||
const char* path, const char* interface,
|
||||
const char* name);
|
||||
void dbus_emit_property_changed_full(DBusConnection* connection,
|
||||
const char* path, const char* interface,
|
||||
const char* name,
|
||||
GDbusPropertyChangedFlags flags);
|
||||
gboolean dbus_get_properties(DBusConnection* connection, const char* path,
|
||||
const char* interface, DBusMessageIter* iter);
|
||||
|
||||
gboolean dbus_attach_object_manager(DBusConnection* connection);
|
||||
gboolean dbus_detach_object_manager(DBusConnection* connection);
|
||||
|
||||
typedef struct GDBusClient GDBusClient;
|
||||
typedef struct GDBusProxy GDBusProxy;
|
||||
|
||||
/**
|
||||
* @brief Create a new D-Bus proxy object
|
||||
*
|
||||
* This function is used to create a new D-Bus proxy object.
|
||||
*
|
||||
* @param client Pointer to the GDBusClient object, which is an instance of the
|
||||
* D-Bus client.
|
||||
* @param path The path of the D-Bus proxy.
|
||||
* @param interface The name of the interface to be implemented by the D-Bus proxy.
|
||||
*
|
||||
* @return Returns a pointer to the GDBusProxy object if it was created successfully,
|
||||
* otherwise returns NULL.
|
||||
*/
|
||||
GDBusProxy* dbus_proxy_new(GDBusClient* client, const char* path,
|
||||
const char* interface);
|
||||
|
||||
/**
|
||||
* @brief Increase the reference count of a D-Bus proxy
|
||||
*
|
||||
* This function increases the reference count of a given D-Bus proxy.
|
||||
*
|
||||
* @param proxy Pointer to the D-Bus proxy whose reference count is to be increased.
|
||||
* @return Returns the pointer to the D-Bus proxy after the reference count is increased.
|
||||
*/
|
||||
GDBusProxy* dbus_proxy_ref(GDBusProxy* proxy);
|
||||
|
||||
/**
|
||||
* @brief Decrement the reference count of a D-Bus proxy
|
||||
*
|
||||
* This function decrements the reference count of a given D-Bus proxy. If the reference
|
||||
* count reaches 0, the proxy will be released.
|
||||
*
|
||||
* @param proxy Pointer to the D-Bus proxy whose reference count is to be decremented.
|
||||
*/
|
||||
void dbus_proxy_unref(GDBusProxy* proxy);
|
||||
|
||||
/**
|
||||
* @brief Get the path of the D-Bus proxy
|
||||
*
|
||||
* @param proxy Pointer to GDBusProxy
|
||||
* @return Return the path of the proxy
|
||||
*/
|
||||
const char* dbus_proxy_get_path(const GDBusProxy* proxy);
|
||||
|
||||
/**
|
||||
* @brief Get the interface of the D-Bus proxy
|
||||
*
|
||||
* @param proxy Pointer to GDBusProxy
|
||||
* @return Return the interface of the proxy
|
||||
*/
|
||||
const char* dbus_proxy_get_interface(GDBusProxy* proxy);
|
||||
|
||||
/**
|
||||
* @brief Gets a property value from a D-Bus proxy object, should run
|
||||
* in uv default loop.
|
||||
*
|
||||
* @param proxy The D-Bus proxy object to query
|
||||
* @param name The name of the property to get
|
||||
* @param iter Pointer to a DBusMessageIter to store the property value
|
||||
* @return gboolean TRUE if successful, FALSE otherwise
|
||||
*
|
||||
* This function synchronously gets a property value from a D-Bus proxy object
|
||||
* and stores it in the provided DBusMessageIter. The caller is responsible
|
||||
* for properly handling the DBusMessageIter contents.
|
||||
*/
|
||||
gboolean dbus_proxy_get_property(GDBusProxy* proxy, const char* name,
|
||||
DBusMessageIter* iter);
|
||||
|
||||
/**
|
||||
* @brief Gets a basic property value from a D-Bus proxy object (thread-safe)
|
||||
*
|
||||
* @param proxy The D-Bus proxy object to query
|
||||
* @param name The name of the property to get
|
||||
* @param value Pointer to store the property value (must match property type)
|
||||
* @return gboolean TRUE if successful, FALSE otherwise
|
||||
*
|
||||
* This function synchronously gets a basic type property value from a D-Bus
|
||||
* proxy object in a thread-safe manner. For complex struct properties,
|
||||
* please use dbus_proxy_get_property_iter_cb instead. The value parameter
|
||||
* must point to storage of the correct type for the property being retrieved.
|
||||
*/
|
||||
gboolean dbus_proxy_get_property_basic(GDBusProxy* proxy, const char* name,
|
||||
void* value);
|
||||
|
||||
/**
|
||||
* Asynchronously gets a D-Bus proxy property with thread safety
|
||||
*
|
||||
* @param proxy The D-Bus proxy object to query
|
||||
* @param name Name of the property to retrieve
|
||||
* @param value Pointer to store the property value
|
||||
* @param iter_cb Callback function to handle property iteration
|
||||
* @return TRUE if property was successfully retrieved, FALSE on error
|
||||
*
|
||||
* @note This function handles thread synchronization automatically.
|
||||
* It can be called from any thread but will block if not in default loop.
|
||||
*/
|
||||
gboolean dbus_proxy_get_property_iter_cb(GDBusProxy* proxy, const char* name,
|
||||
void* value, GDBusPropIterFunction iter_cb);
|
||||
|
||||
GDBusProxy* dbus_proxy_lookup(void* list, int* index, const char* path,
|
||||
const char* interface);
|
||||
char* dbus_proxy_path_lookup(void* list, int* index, const char* path);
|
||||
|
||||
gboolean dbus_proxy_refresh_property(GDBusProxy* proxy, const char* name);
|
||||
|
||||
typedef void (*GDBusResultFunction)(const DBusError* error, void* user_data);
|
||||
|
||||
/**
|
||||
* @brief This function is used to set the properties of a D-Bus proxy.
|
||||
*
|
||||
* @param proxy Pointer to GDBusProxy, indicating the proxy to set the properties.
|
||||
* @param name The name of the property, which should be a string type.
|
||||
* @param type The type of the property, which should be an integer, indicating the data
|
||||
* type of the property.
|
||||
* @param value Pointer to the value of the property, which should be a void type,
|
||||
* indicating the value of the property.
|
||||
* @param function Pointer to GDBusResultFunction, indicating the function to be executed
|
||||
* after setting the property.
|
||||
* @param user_data Pointer to user data, which will be passed when executing
|
||||
* the @param function function.
|
||||
* @param destroy Pointer to GDBusDestroyFunction, indicating how to destroy the data
|
||||
* pointed to by value when the set property is no longer needed.
|
||||
*
|
||||
* @return Returns a true value of type gboolean if the property is successfully set;
|
||||
* otherwise, returns a false value.
|
||||
*/
|
||||
gboolean dbus_proxy_set_property_basic(GDBusProxy* proxy,
|
||||
const char* name, int type, const void* value,
|
||||
GDBusResultFunction function, void* user_data,
|
||||
GDBusDestroyFunction destroy);
|
||||
|
||||
gboolean dbus_proxy_set_property_array(GDBusProxy* proxy,
|
||||
const char* name, int type, const void* value,
|
||||
size_t size, GDBusResultFunction function,
|
||||
void* user_data, GDBusDestroyFunction destroy);
|
||||
|
||||
void dbus_dict_append_entry(DBusMessageIter* dict,
|
||||
const char* key, int type, void* val);
|
||||
void dbus_dict_append_basic_array(DBusMessageIter* dict, int key_type,
|
||||
const void* key, int type, void* val,
|
||||
int n_elements);
|
||||
void dbus_dict_append_array(DBusMessageIter* dict,
|
||||
const char* key, int type, void* val,
|
||||
int n_elements);
|
||||
|
||||
typedef void (*GDBusSetupFunction)(DBusMessageIter* iter, void* user_data);
|
||||
typedef void (*GDBusReturnFunction)(DBusMessage* message, void* user_data);
|
||||
|
||||
/**
|
||||
* @brief Calls the specified @method method on the given D-Bus proxy
|
||||
*
|
||||
* @param proxy Pointer to GDBusProxy, indicating the D-Bus proxy to be called
|
||||
* @param method The name of the method to be called
|
||||
* @param setup A GDBusSetupFunction, used to set the parameters of the method call
|
||||
* @param function A GDBusReturnFunction, called after the method call succeeds,
|
||||
* returns the return value of the method
|
||||
* @param user_data User data, passed to GDBusReturnFunction
|
||||
* @param destroy A GDBusDestroyFunction, called after the method call is completed,
|
||||
* used to clean up resources
|
||||
*
|
||||
* @return Returns gboolean if the method call succeeds, otherwise returns gboolean
|
||||
*/
|
||||
gboolean dbus_proxy_method_call(GDBusProxy* proxy, const char* method,
|
||||
GDBusSetupFunction setup,
|
||||
GDBusReturnFunction function, void* user_data,
|
||||
GDBusDestroyFunction destroy);
|
||||
|
||||
typedef void (*GDBusClientFunction)(GDBusClient* client, void* user_data);
|
||||
typedef void (*GDBusProxyFunction)(GDBusProxy* proxy, void* user_data);
|
||||
typedef gboolean (*GDBusProxyPropertyFilterFunction)(GDBusProxy* proxy, void* user_data);
|
||||
typedef void (*GDBusPropertyFunction)(GDBusProxy* proxy, const char* name,
|
||||
DBusMessageIter* iter, void* user_data);
|
||||
typedef gboolean (*GDBusProxyFilterFunction)(const char* path,
|
||||
const char* interface, void* user_data);
|
||||
|
||||
/**
|
||||
* @brief Sets a property monitor for the D-Bus proxy.
|
||||
*
|
||||
* @param proxy the GDBusProxy object which the property monitor is to be set.
|
||||
* @param function Pointer to the callback function that handles property changes.
|
||||
* @param user_data User data, which will be passed to the callback function.
|
||||
* @return Returns TRUE if the monitor was successfully set, otherwise returns FALSE.
|
||||
*/
|
||||
gboolean dbus_proxy_set_property_watch(GDBusProxy* proxy,
|
||||
GDBusPropertyFunction function, void* user_data);
|
||||
|
||||
/**
|
||||
* @brief Removes a property monitor from a D-Bus proxy.
|
||||
*
|
||||
* @param proxy the GDBusProxy object whose property monitor is to be removed.
|
||||
* @param destroy Pointer to a destruction function that handles user data.
|
||||
* @return Returns TRUE if the monitor was successfully removed, otherwise returns FALSE.
|
||||
*/
|
||||
gboolean dbus_proxy_remove_property_watch(GDBusProxy* proxy,
|
||||
GDBusDestroyFunction destroy);
|
||||
|
||||
gboolean dbus_proxy_set_removed_watch(GDBusProxy* proxy,
|
||||
GDBusProxyFunction destroy, void* user_data);
|
||||
|
||||
/**
|
||||
* @brief Create a new DBus client instance
|
||||
*
|
||||
* This function is used to create a new DBus client instance.
|
||||
*
|
||||
* @param connection Pointer to DBus connection.
|
||||
* @param service Service name, used to identify DBus service.
|
||||
* @param path Path, used to identify the path of DBus object.
|
||||
*
|
||||
* @return Returns a pointer to the newly created GDBusClient instance.
|
||||
*/
|
||||
GDBusClient* dbus_client_new(DBusConnection* connection,
|
||||
const char* service, const char* path);
|
||||
|
||||
GDBusClient* dbus_client_new_full(DBusConnection* connection,
|
||||
const char* service,
|
||||
const char* path,
|
||||
const char* root_path);
|
||||
|
||||
/**
|
||||
* @brief Increase the reference count of the GDBusClient object.
|
||||
*
|
||||
* @param client The GDBusClient object whose reference count is to be increased.
|
||||
* @return The GDBusClient object after the reference count is increased.
|
||||
*/
|
||||
GDBusClient* dbus_client_ref(GDBusClient* client);
|
||||
|
||||
/**
|
||||
* @brief Decrement the reference count of a GDBusClient object.
|
||||
*
|
||||
* @param client The GDBusClient object whose reference count is to be decremented.
|
||||
*/
|
||||
void dbus_client_unref(GDBusClient* client);
|
||||
|
||||
/**
|
||||
* @brief Set the connection monitoring function of the D-Bus client.
|
||||
*
|
||||
* @param client Pointer to the GDBusClient instance.
|
||||
* @param function Pointer to the function of type GDBusWatchFunction, which will be
|
||||
* called when there is a new D-Bus connection.
|
||||
* @param user_data caller data, which will be passed to the caller when callback
|
||||
* @return If the setting is successful, return a true value of type gboolean; otherwise,
|
||||
* return a false value.
|
||||
*/
|
||||
gboolean dbus_client_set_connect_watch(GDBusClient* client, GDBusWatchFunction function,
|
||||
void* user_data);
|
||||
|
||||
/**
|
||||
* @brief Set the disconnection monitoring function of the D-Bus client.
|
||||
*
|
||||
* @param client Pointer to the GDBusClient instance.
|
||||
* @param function Pointer to the function of type GDBusWatchFunction, which will be
|
||||
* called when the D-Bus connection is disconnected.
|
||||
* @param user_data caller data will be passed to the caller when calling the function.
|
||||
* @return If the setting is successful, return a true value of type gboolean; otherwise,
|
||||
* return a false value.
|
||||
*/
|
||||
gboolean dbus_client_set_disconnect_watch(GDBusClient* client,
|
||||
GDBusWatchFunction function, void* user_data);
|
||||
|
||||
/**
|
||||
* @brief Set the signal monitoring function of the D-Bus client
|
||||
*
|
||||
* This function is used to set the signal monitoring function of the D-Bus client.
|
||||
* When the client receives a D-Bus signal, the function will be called.
|
||||
*
|
||||
* @param client Pointer to the D-Bus client
|
||||
* @param function Points to the function that processes the D-Bus signal
|
||||
* @param user_data caller data will be passed to the caller when calling the function
|
||||
*
|
||||
* @return If the setting is successful, return gboolean as TRUE, otherwise return FALSE
|
||||
*/
|
||||
gboolean dbus_client_set_signal_watch(GDBusClient* client,
|
||||
GDBusMessageFunction function, void* user_data);
|
||||
|
||||
/**
|
||||
* @brief Set the preparation function and user data of the DBus client
|
||||
*
|
||||
* This function is used to set the preparation function and user data of the DBus client.
|
||||
* The preparation function will be called when the DBus client is ready, and the user
|
||||
* data can be passed to this function.
|
||||
*
|
||||
* @param client Pointer to the DBus client
|
||||
* @param ready Preparation function, which will be called when the DBus client is ready
|
||||
* @param user_data User data, which can be passed to the preparation function
|
||||
*
|
||||
* @return gboolean If the setting is successful, it returns TRUE, otherwise it returns FALSE
|
||||
*/
|
||||
gboolean dbus_client_set_ready_watch(GDBusClient* client,
|
||||
GDBusClientFunction ready, void* user_data);
|
||||
|
||||
/**
|
||||
* @brief Set the proxy handler function
|
||||
*
|
||||
* This function is used to set the proxy handler function of the D-Bus client.
|
||||
*
|
||||
* @param client Pointer to the GDBusClient instance.
|
||||
* @param proxy_added Callback function when the proxy is added.
|
||||
* @param proxy_removed Callback function when the proxy is removed.
|
||||
* @param proxy_property_filter Callback function for proxy property filtering.
|
||||
* @param property_changed Callback function when the property is changed.
|
||||
* @param user_data caller data, which will be used in the callback function.
|
||||
* @return If the setting is successful, the return gboolean is TRUE, otherwise
|
||||
* it returns FALSE.
|
||||
*/
|
||||
gboolean dbus_client_set_proxy_handlers(GDBusClient* client,
|
||||
GDBusProxyFunction proxy_added,
|
||||
GDBusProxyFunction proxy_removed,
|
||||
GDBusProxyPropertyFilterFunction proxy_property_filter,
|
||||
GDBusPropertyFunction property_changed,
|
||||
void* user_data);
|
||||
|
||||
/**
|
||||
* @brief Set the proxy filter function
|
||||
*
|
||||
* This function is used to set the proxy filter function of the D-Bus client.
|
||||
*
|
||||
* @param client Pointer to the GDBusClient instance.
|
||||
* @param proxy_filter Callback function of the proxy filter.
|
||||
* @param user_data caller data, which will be used in the callback function.
|
||||
* @return If the setting is successful, the return gboolean is TRUE, otherwise
|
||||
* it returns FALSE.
|
||||
*/
|
||||
gboolean dbus_client_set_proxy_filter(GDBusClient* client,
|
||||
GDBusProxyFilterFunction proxy_filter,
|
||||
void* user_data);
|
||||
|
||||
/**
|
||||
* Adds a service watch for the specified name.
|
||||
* @param client The DBus client instance
|
||||
* @param name The service name to watch
|
||||
* @param connect Callback when service connects
|
||||
* @param disconnect Callback when service disconnects
|
||||
* @param user_data User data passed to callbacks
|
||||
* @param destroy Destroy notification callback
|
||||
* @return Watch ID
|
||||
*/
|
||||
guint dbus_client_add_service_watch(GDBusClient* client, const char* name,
|
||||
GDBusWatchFunction connect, GDBusWatchFunction disconnect,
|
||||
void* user_data, GDBusDestroyFunction destroy);
|
||||
|
||||
/**
|
||||
* Adds a watch for service disconnection only.
|
||||
* @param client The DBus client instance
|
||||
* @param name The service name to watch
|
||||
* @param function Callback when service disconnects
|
||||
* @param user_data User data passed to callback
|
||||
* @param destroy Destroy notification callback
|
||||
* @return Watch ID
|
||||
*/
|
||||
guint dbus_client_add_service_disconnect_watch(GDBusClient* client, const char* name,
|
||||
GDBusWatchFunction function, void* user_data, GDBusDestroyFunction destroy);
|
||||
|
||||
/**
|
||||
* Adds a signal watch with detailed matching criteria.
|
||||
* @param client The DBus client instance
|
||||
* @param sender The sender name to match
|
||||
* @param path The object path to match
|
||||
* @param interface The interface name to match
|
||||
* @param member The member name to match
|
||||
* @param function Callback when signal is received
|
||||
* @param user_data User data passed to callback
|
||||
* @param destroy Destroy notification callback
|
||||
* @return Watch ID
|
||||
*/
|
||||
guint dbus_client_add_signal_watch(GDBusClient* client,const char* sender,
|
||||
const char* path, const char* interface, const char* member,
|
||||
GDBusSignalFunction function, void* user_data, GDBusDestroyFunction destroy);
|
||||
|
||||
/**
|
||||
* Adds a properties change watch.
|
||||
* @param client The DBus client instance
|
||||
* @param sender The sender name to match
|
||||
* @param path The object path to match
|
||||
* @param interface The interface name to match
|
||||
* @param function Callback when properties change
|
||||
* @param user_data User data passed to callback
|
||||
* @param destroy Destroy notification callback
|
||||
* @return Watch ID
|
||||
*/
|
||||
guint dbus_client_add_properties_watch(GDBusClient* client,
|
||||
const char* sender, const char* path, const char* interface,
|
||||
GDBusSignalFunction function, void* user_data, GDBusDestroyFunction destroy);
|
||||
|
||||
/**
|
||||
* Removes a previously added watch.
|
||||
* @param client The DBus client instance
|
||||
* @param tag The watch ID to remove
|
||||
* @return TRUE if watch was found and removed
|
||||
*/
|
||||
gboolean dbus_client_remove_watch(GDBusClient* client, guint tag);
|
||||
|
||||
/**
|
||||
* Removes all watches from the client.
|
||||
* @param client The DBus client instance
|
||||
*/
|
||||
void dbus_client_remove_all_watches(GDBusClient* client);
|
||||
|
||||
/**
|
||||
* Adds a watch for client disconnection.
|
||||
* @param client The DBus client instance
|
||||
* @param function Callback when client disconnects
|
||||
* @param user_data User data passed to callback
|
||||
* @param destroy Destroy notification callback
|
||||
* @return Watch ID
|
||||
*/
|
||||
gboolean dbus_client_add_disconnect_watch(GDBusClient* client,
|
||||
GDBusWatchFunction function, void* user_data, DBusFreeFunction destroy);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __GDBUS_H */
|
||||
|
|
@ -0,0 +1,512 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Xiaomi Technologies Co., Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This file is part of the Xiaomi project.
|
||||
*
|
||||
* This source code is licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
#include <poll.h>
|
||||
#include <stdlib.h>
|
||||
#include <uv.h>
|
||||
|
||||
#include "gdbus-internal.h"
|
||||
|
||||
struct idle_handler {
|
||||
uv_idle_t handle;
|
||||
DBusConnection* conn;
|
||||
};
|
||||
|
||||
struct timeout_handler {
|
||||
uv_timer_t handle;
|
||||
DBusTimeout* timeout;
|
||||
};
|
||||
|
||||
struct dbus_watch_info {
|
||||
uv_poll_t* handle;
|
||||
DBusWatch* read_watch;
|
||||
DBusWatch* write_watch;
|
||||
DBusConnection* conn;
|
||||
};
|
||||
|
||||
static void close_cb(uv_handle_t* handle)
|
||||
{
|
||||
free(handle->data);
|
||||
}
|
||||
|
||||
static void message_dispatch(uv_idle_t* handle)
|
||||
{
|
||||
struct idle_handler* handler = handle->data;
|
||||
DBusConnection* conn = handler->conn;
|
||||
|
||||
/* Dispatch messages */
|
||||
while (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS)
|
||||
;
|
||||
|
||||
dbus_connection_unref(conn);
|
||||
|
||||
uv_close((uv_handle_t*)handle, close_cb);
|
||||
}
|
||||
|
||||
static inline void queue_dispatch(DBusConnection* conn,
|
||||
DBusDispatchStatus status)
|
||||
{
|
||||
if (status == DBUS_DISPATCH_DATA_REMAINS) {
|
||||
struct idle_handler* handler;
|
||||
|
||||
handler = calloc(1, sizeof(struct idle_handler));
|
||||
if (handler == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (uv_idle_init(uv_default_loop(), &handler->handle) != 0) {
|
||||
free(handler);
|
||||
return;
|
||||
}
|
||||
|
||||
handler->conn = dbus_connection_ref(conn);
|
||||
handler->handle.data = handler;
|
||||
if (uv_idle_start(&handler->handle, message_dispatch) != 0) {
|
||||
dbus_connection_unref(conn);
|
||||
uv_close((uv_handle_t*)&handler->handle, close_cb);
|
||||
free(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void watch_func(uv_poll_t* handle, int state, int events)
|
||||
{
|
||||
struct dbus_watch_info* info = handle->data;
|
||||
unsigned int flags = 0;
|
||||
DBusDispatchStatus status;
|
||||
DBusConnection* conn;
|
||||
|
||||
if (events & UV_READABLE)
|
||||
flags |= DBUS_WATCH_READABLE;
|
||||
if (events & UV_WRITABLE)
|
||||
flags |= DBUS_WATCH_WRITABLE;
|
||||
if (events & UV_DISCONNECT)
|
||||
flags |= DBUS_WATCH_HANGUP;
|
||||
if (events & POLLERR)
|
||||
flags |= DBUS_WATCH_ERROR;
|
||||
|
||||
/* Protect connection from being destroyed by dbus_watch_handle */
|
||||
conn = dbus_connection_ref(info->conn);
|
||||
|
||||
if (flags & DBUS_WATCH_READABLE && info->read_watch != NULL)
|
||||
dbus_watch_handle(info->read_watch, flags);
|
||||
if (flags & DBUS_WATCH_WRITABLE && info->write_watch != NULL)
|
||||
dbus_watch_handle(info->write_watch, flags);
|
||||
|
||||
status = dbus_connection_get_dispatch_status(conn);
|
||||
queue_dispatch(conn, status);
|
||||
|
||||
dbus_connection_unref(conn);
|
||||
}
|
||||
|
||||
static void close_watch_info_handler_cb(uv_handle_t* handle)
|
||||
{
|
||||
struct dbus_watch_info* info = handle->data;
|
||||
|
||||
if (info == NULL)
|
||||
return;
|
||||
|
||||
if (info->read_watch) {
|
||||
dbus_watch_set_data(info->read_watch, NULL, NULL);
|
||||
info->read_watch = NULL;
|
||||
}
|
||||
if (info->write_watch) {
|
||||
dbus_watch_set_data(info->write_watch, NULL, NULL);
|
||||
info->write_watch = NULL;
|
||||
}
|
||||
|
||||
free(info->handle);
|
||||
info->handle = NULL;
|
||||
}
|
||||
|
||||
static void watch_info_free_read(void* data)
|
||||
{
|
||||
struct dbus_watch_info* info = data;
|
||||
|
||||
if (info != NULL && info->read_watch != NULL) {
|
||||
info->read_watch = NULL;
|
||||
|
||||
/**
|
||||
* libdbus maybe call read watch free cb, not from remove watch.
|
||||
* need close uv hander when read and watch both null.
|
||||
*/
|
||||
if (info->write_watch == NULL && info->handle != NULL)
|
||||
uv_close((uv_handle_t*)info->handle, close_watch_info_handler_cb);
|
||||
}
|
||||
}
|
||||
|
||||
static void watch_info_free_write(void* data)
|
||||
{
|
||||
struct dbus_watch_info* info = data;
|
||||
|
||||
if (info != NULL && info->write_watch != NULL) {
|
||||
info->write_watch = NULL;
|
||||
|
||||
/**
|
||||
* libdbus maybe call write watch free cb, not from remove watch.
|
||||
* need close uv hander when read and watch both null.
|
||||
*/
|
||||
if (info->read_watch == NULL && info->handle != NULL)
|
||||
uv_close((uv_handle_t*)info->handle, close_watch_info_handler_cb);
|
||||
}
|
||||
}
|
||||
|
||||
static dbus_bool_t add_watch(DBusWatch* watch, void* data)
|
||||
{
|
||||
struct dbus_watch_info* watch_info = data;
|
||||
int cond = UV_DISCONNECT;
|
||||
int flags = 0;
|
||||
|
||||
if (!dbus_watch_get_enabled(watch))
|
||||
return TRUE;
|
||||
|
||||
flags = dbus_watch_get_flags(watch);
|
||||
if (flags & DBUS_WATCH_READABLE) {
|
||||
dbus_watch_set_data(watch, watch_info, watch_info_free_read);
|
||||
watch_info->read_watch = watch;
|
||||
}
|
||||
if (flags & DBUS_WATCH_WRITABLE) {
|
||||
dbus_watch_set_data(watch, watch_info, watch_info_free_write);
|
||||
watch_info->write_watch = watch;
|
||||
}
|
||||
|
||||
if (watch_info->read_watch != NULL && watch != watch_info->read_watch)
|
||||
flags |= dbus_watch_get_flags(watch_info->read_watch);
|
||||
if (watch_info->write_watch != NULL && watch != watch_info->write_watch)
|
||||
flags |= dbus_watch_get_flags(watch_info->write_watch);
|
||||
|
||||
if (flags & DBUS_WATCH_READABLE)
|
||||
cond |= UV_READABLE;
|
||||
if (flags & DBUS_WATCH_WRITABLE)
|
||||
cond |= UV_WRITABLE;
|
||||
|
||||
if (!watch_info->handle) {
|
||||
watch_info->handle = calloc(1, sizeof(uv_poll_t));
|
||||
if (!watch_info->handle)
|
||||
return FALSE;
|
||||
|
||||
int fd = dbus_watch_get_unix_fd(watch);
|
||||
if (uv_poll_init(uv_default_loop(), watch_info->handle, fd) != 0) {
|
||||
free(watch_info->handle);
|
||||
watch_info->handle = NULL;
|
||||
dbus_watch_set_data(watch, NULL, NULL);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
watch_info->handle->data = watch_info;
|
||||
}
|
||||
|
||||
if (uv_poll_start(watch_info->handle, cond, watch_func) != 0) {
|
||||
uv_close((uv_handle_t*)watch_info->handle, close_watch_info_handler_cb);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void remove_watch(DBusWatch* watch, void* data)
|
||||
{
|
||||
int flags = 0;
|
||||
int cond = 0;
|
||||
struct dbus_watch_info* info = data;
|
||||
|
||||
/* If the watch is still enabled, we treat this as a toggle */
|
||||
if (dbus_watch_get_enabled(watch))
|
||||
return;
|
||||
|
||||
if (info->read_watch == watch && info->write_watch != NULL) {
|
||||
/* remove watch is read, keep write flag if write watch is valid */
|
||||
flags = dbus_watch_get_flags(info->write_watch);
|
||||
} else if (info->write_watch == watch && info->read_watch != NULL) {
|
||||
/* remove watch is write, keep read flag if read watch is valid */
|
||||
flags = dbus_watch_get_flags(info->read_watch);
|
||||
}
|
||||
|
||||
if (flags & DBUS_WATCH_READABLE)
|
||||
cond |= UV_READABLE;
|
||||
if (flags & DBUS_WATCH_WRITABLE)
|
||||
cond |= UV_WRITABLE;
|
||||
|
||||
if (cond != 0) {
|
||||
cond |= UV_DISCONNECT;
|
||||
if (uv_poll_start(info->handle, cond, watch_func) != 0) {
|
||||
uv_close((uv_handle_t*)info->handle, close_watch_info_handler_cb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* will trigger watch_info_free_read/write() */
|
||||
dbus_watch_set_data(watch, NULL, NULL);
|
||||
}
|
||||
|
||||
static void watch_toggled(DBusWatch* watch, void* data)
|
||||
{
|
||||
/* Because we just exit on OOM, enable/disable is
|
||||
* no different from add/remove */
|
||||
if (dbus_watch_get_enabled(watch))
|
||||
add_watch(watch, data);
|
||||
else
|
||||
remove_watch(watch, data);
|
||||
}
|
||||
|
||||
static void timeout_handler_dispatch(uv_timer_t* handle)
|
||||
{
|
||||
struct timeout_handler* handler = handle->data;
|
||||
|
||||
/* if not enabled should not be polled by the main loop */
|
||||
if (dbus_timeout_get_enabled(handler->timeout))
|
||||
dbus_timeout_handle(handler->timeout);
|
||||
}
|
||||
|
||||
static void timeout_handler_free(void* data)
|
||||
{
|
||||
struct timeout_handler* handler = data;
|
||||
|
||||
uv_close((uv_handle_t*)&handler->handle, close_cb);
|
||||
}
|
||||
|
||||
static dbus_bool_t add_timeout(DBusTimeout* timeout, void* data)
|
||||
{
|
||||
int interval = dbus_timeout_get_interval(timeout);
|
||||
struct timeout_handler* handler;
|
||||
|
||||
if (!dbus_timeout_get_enabled(timeout))
|
||||
return TRUE;
|
||||
|
||||
handler = calloc(1, sizeof(struct timeout_handler));
|
||||
if (handler == NULL)
|
||||
return FALSE;
|
||||
|
||||
handler->timeout = timeout;
|
||||
|
||||
dbus_timeout_set_data(timeout, handler, timeout_handler_free);
|
||||
|
||||
if (uv_timer_init(uv_default_loop(), &handler->handle) != 0) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
handler->handle.data = handler;
|
||||
if (uv_timer_start(&handler->handle, timeout_handler_dispatch, interval, 0) != 0) {
|
||||
uv_close((uv_handle_t*)&handler->handle, close_cb);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
errout:
|
||||
free(handler);
|
||||
dbus_timeout_set_data(timeout, NULL, NULL);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void remove_timeout(DBusTimeout* timeout, void* data)
|
||||
{
|
||||
/* will trigger timeout_handler_free() */
|
||||
dbus_timeout_set_data(timeout, NULL, NULL);
|
||||
}
|
||||
|
||||
static void timeout_toggled(DBusTimeout* timeout, void* data)
|
||||
{
|
||||
if (dbus_timeout_get_enabled(timeout))
|
||||
add_timeout(timeout, data);
|
||||
else
|
||||
remove_timeout(timeout, data);
|
||||
}
|
||||
|
||||
static void dispatch_status(DBusConnection* conn,
|
||||
DBusDispatchStatus status, void* data)
|
||||
{
|
||||
if (!dbus_connection_get_is_connected(conn))
|
||||
return;
|
||||
|
||||
queue_dispatch(conn, status);
|
||||
}
|
||||
|
||||
static void dbus_watch_info_free(void* data)
|
||||
{
|
||||
struct dbus_watch_info* info = data;
|
||||
|
||||
if (info != NULL) {
|
||||
dbus_connection_unref(info->conn);
|
||||
free(info);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void setup_dbus_with_main_loop(DBusConnection* conn)
|
||||
{
|
||||
struct dbus_watch_info* info = calloc(1, sizeof(struct dbus_watch_info));
|
||||
if (info == NULL)
|
||||
return;
|
||||
info->conn = dbus_connection_ref(conn);
|
||||
|
||||
dbus_connection_set_watch_functions(conn, add_watch, remove_watch,
|
||||
watch_toggled, info, dbus_watch_info_free);
|
||||
|
||||
dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout,
|
||||
timeout_toggled, NULL, NULL);
|
||||
|
||||
dbus_connection_set_dispatch_status_function(conn, dispatch_status,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
static gboolean setup_bus(DBusConnection* conn, const char* name,
|
||||
DBusError* error)
|
||||
{
|
||||
gboolean result;
|
||||
DBusDispatchStatus status;
|
||||
|
||||
if (name != NULL) {
|
||||
result = dbus_request_name(conn, name, error);
|
||||
|
||||
if (error != NULL) {
|
||||
if (dbus_error_is_set(error) == TRUE)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (result == FALSE)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
setup_dbus_with_main_loop(conn);
|
||||
|
||||
status = dbus_connection_get_dispatch_status(conn);
|
||||
queue_dispatch(conn, status);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
DBusConnection* dbus_setup_bus(DBusBusType type, const char* name,
|
||||
DBusError* error)
|
||||
{
|
||||
DBusConnection* conn;
|
||||
|
||||
conn = dbus_bus_get(type, error);
|
||||
|
||||
if (error != NULL) {
|
||||
if (dbus_error_is_set(error) == TRUE)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (conn == NULL)
|
||||
return NULL;
|
||||
|
||||
if (setup_bus(conn, name, error) == FALSE) {
|
||||
dbus_connection_unref(conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
DBusConnection* dbus_setup_private(DBusBusType type, const char* name,
|
||||
DBusError* error)
|
||||
{
|
||||
DBusConnection* conn;
|
||||
|
||||
conn = dbus_bus_get_private(type, error);
|
||||
|
||||
if (error != NULL) {
|
||||
if (dbus_error_is_set(error) == TRUE)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (conn == NULL)
|
||||
return NULL;
|
||||
|
||||
if (setup_bus(conn, name, error) == FALSE) {
|
||||
dbus_connection_close(conn);
|
||||
dbus_connection_unref(conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
gboolean dbus_request_name(DBusConnection* connection, const char* name,
|
||||
DBusError* error)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = dbus_bus_request_name(connection, name,
|
||||
DBUS_NAME_FLAG_DO_NOT_QUEUE, error);
|
||||
|
||||
if (error != NULL) {
|
||||
if (dbus_error_is_set(error) == TRUE)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
|
||||
if (error != NULL)
|
||||
dbus_set_error(error, name, "Name already in use");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
struct disconnect_data {
|
||||
GDBusWatchFunction function;
|
||||
void* user_data;
|
||||
GDBusWatch* watcher;
|
||||
};
|
||||
|
||||
static gboolean disconnected_signal(DBusConnection* conn,
|
||||
DBusMessage* msg, void* data)
|
||||
{
|
||||
struct disconnect_data* dc_data = data;
|
||||
|
||||
info("Got disconnected from the system message bus");
|
||||
|
||||
dc_data->function(conn, dc_data->user_data);
|
||||
|
||||
dbus_connection_unref(conn);
|
||||
|
||||
dbus_watch_set_connection_state(dc_data->watcher, TRUE);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean dbus_client_add_disconnect_watch(GDBusClient* client,
|
||||
GDBusWatchFunction function, void* user_data, DBusFreeFunction destroy)
|
||||
{
|
||||
struct disconnect_data* dc_data;
|
||||
|
||||
dbus_connection_set_exit_on_disconnect(client->dbus_conn, FALSE);
|
||||
|
||||
dc_data = calloc(1, sizeof(struct disconnect_data));
|
||||
if (dc_data == NULL)
|
||||
return FALSE;
|
||||
|
||||
dc_data->function = function;
|
||||
dc_data->user_data = user_data;
|
||||
dc_data->watcher = client->watcher;
|
||||
|
||||
if (dbus_add_signal_watch(client->watcher, NULL, NULL,
|
||||
DBUS_INTERFACE_LOCAL, "Disconnected",
|
||||
disconnected_signal, dc_data, free)
|
||||
== 0) {
|
||||
error("Failed to add watch for D-Bus Disconnected signal");
|
||||
free(dc_data);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
dbus_connection_ref(client->dbus_conn);
|
||||
return TRUE;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,226 @@
|
|||
/*
|
||||
* Copyright (C) 2025 Xiaomi Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
#include "gdbus-internal.h"
|
||||
|
||||
#define POLICY_KIT_DBUS_NAME "org.freedesktop.PolicyKit1"
|
||||
#define POLICY_KIT_INTERFACE "org.freedesktop.PolicyKit1.Authority"
|
||||
#define POLICY_KIT_PATH "/org/freedesktop/PolicyKit1/Authority"
|
||||
#define POLICY_KIT_ACTION "org.freedesktop.policykit.exec"
|
||||
|
||||
typedef enum polkit_interaction_flag {
|
||||
POLKIT_FLAG_NONE = 0x00000000,
|
||||
POLKIT_FLAG_ALLOW = 0x00000001
|
||||
} polkit_interaction_flag;
|
||||
|
||||
typedef struct authorization_context {
|
||||
void (*callback)(dbus_bool_t authorized, void* user_data);
|
||||
void* user_data;
|
||||
} authorization_context;
|
||||
|
||||
typedef struct dict_entry_builder {
|
||||
DBusMessageIter iter;
|
||||
const char* key;
|
||||
const char* value;
|
||||
} dict_entry_builder;
|
||||
|
||||
typedef enum contained_sig_type {
|
||||
SIG_TYPE_EMPTY,
|
||||
SIG_TYPE_VARIANT,
|
||||
} contained_sig_type;
|
||||
|
||||
static const char* init_contained_signature_type(contained_sig_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case SIG_TYPE_EMPTY:
|
||||
return DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
|
||||
DBUS_TYPE_STRING_AS_STRING
|
||||
DBUS_TYPE_STRING_AS_STRING
|
||||
DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
|
||||
case SIG_TYPE_VARIANT:
|
||||
return DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
|
||||
DBUS_TYPE_STRING_AS_STRING
|
||||
DBUS_TYPE_VARIANT_AS_STRING
|
||||
DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void build_dict_entry(dict_entry_builder* builder)
|
||||
{
|
||||
DBusMessageIter dict, entry, variant;
|
||||
|
||||
dbus_message_iter_open_container(&builder->iter, DBUS_TYPE_ARRAY,
|
||||
init_contained_signature_type(SIG_TYPE_VARIANT), &dict);
|
||||
|
||||
dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
|
||||
dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &builder->key);
|
||||
|
||||
dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
|
||||
DBUS_TYPE_STRING_AS_STRING, &variant);
|
||||
dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING, &builder->value);
|
||||
|
||||
dbus_message_iter_close_container(&entry, &variant);
|
||||
dbus_message_iter_close_container(&dict, &entry);
|
||||
dbus_message_iter_close_container(&builder->iter, &dict);
|
||||
}
|
||||
|
||||
static void build_empty_dict(DBusMessageIter* iter)
|
||||
{
|
||||
DBusMessageIter dict;
|
||||
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
|
||||
init_contained_signature_type(SIG_TYPE_EMPTY), &dict);
|
||||
dbus_message_iter_close_container(iter, &dict);
|
||||
}
|
||||
|
||||
static void build_authorization_arguments(DBusConnection* conn, DBusMessageIter* iter,
|
||||
const char* action, polkit_interaction_flag flags)
|
||||
{
|
||||
const char* bus_name = dbus_bus_get_unique_name(conn);
|
||||
const char* subject_kind = "system-bus-name";
|
||||
const char* cancellation_id = "";
|
||||
DBusMessageIter subject;
|
||||
|
||||
// build main subject
|
||||
dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &subject);
|
||||
dbus_message_iter_append_basic(&subject, DBUS_TYPE_STRING, &subject_kind);
|
||||
|
||||
dict_entry_builder builder = {
|
||||
.iter = subject,
|
||||
.key = "name",
|
||||
.value = bus_name
|
||||
};
|
||||
build_dict_entry(&builder);
|
||||
dbus_message_iter_close_container(iter, &subject);
|
||||
|
||||
// add less parameters
|
||||
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
|
||||
action ? action : POLICY_KIT_ACTION);
|
||||
build_empty_dict(iter);
|
||||
dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &flags);
|
||||
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cancellation_id);
|
||||
}
|
||||
|
||||
static dbus_bool_t parse_authorization_result(DBusMessageIter* iter)
|
||||
{
|
||||
DBusMessageIter recurse_iter;
|
||||
dbus_bool_t auth = FALSE;
|
||||
|
||||
if (!iter)
|
||||
return FALSE;
|
||||
|
||||
dbus_message_iter_recurse(iter, &recurse_iter);
|
||||
dbus_message_iter_get_basic(&recurse_iter, &auth);
|
||||
|
||||
return auth;
|
||||
}
|
||||
|
||||
static void handle_authorization_reply(DBusPendingCall* call, void* user_data)
|
||||
{
|
||||
authorization_context* context = user_data;
|
||||
DBusMessage* reply = NULL;
|
||||
DBusMessageIter iter;
|
||||
dbus_bool_t authorized = FALSE;
|
||||
|
||||
if (!call || !context)
|
||||
goto cleanup;
|
||||
|
||||
reply = dbus_pending_call_steal_reply(call);
|
||||
if (!reply)
|
||||
goto cleanup;
|
||||
|
||||
if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
|
||||
error("Authorization error: %s\n", dbus_message_get_error_name(reply));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!dbus_message_has_signature(reply, "(bba{ss})")) {
|
||||
error("Invalid reply signature\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!dbus_message_iter_init(reply, &iter))
|
||||
goto cleanup;
|
||||
|
||||
authorized = parse_authorization_result(&iter);
|
||||
|
||||
cleanup:
|
||||
if (context != NULL && context->callback) {
|
||||
context->callback(authorized, context->user_data);
|
||||
}
|
||||
|
||||
if (reply)
|
||||
dbus_message_unref(reply);
|
||||
|
||||
dbus_pending_call_unref(call);
|
||||
}
|
||||
|
||||
int dbus_polkit_check_authorization(DBusConnection* conn,
|
||||
const char* action, gboolean allow_interaction,
|
||||
void (*callback)(dbus_bool_t, void*),
|
||||
void* user_data, int timeout_ms)
|
||||
{
|
||||
DBusMessage* msg;
|
||||
DBusMessageIter iter;
|
||||
DBusPendingCall* pending_call = NULL;
|
||||
polkit_interaction_flag flags;
|
||||
|
||||
if (!conn)
|
||||
return -EINVAL;
|
||||
|
||||
authorization_context* context = calloc(1, sizeof(authorization_context));
|
||||
if (!context)
|
||||
return -ENOMEM;
|
||||
|
||||
msg = dbus_message_new_method_call(POLICY_KIT_DBUS_NAME, POLICY_KIT_PATH,
|
||||
POLICY_KIT_INTERFACE, "CheckAuthorization");
|
||||
if (!msg) {
|
||||
free(context);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
flags = allow_interaction ? POLKIT_FLAG_ALLOW : POLKIT_FLAG_NONE;
|
||||
|
||||
dbus_message_iter_init_append(msg, &iter);
|
||||
build_authorization_arguments(conn, &iter, action, flags);
|
||||
|
||||
if (!dbus_connection_send_with_reply(conn, msg, &pending_call, timeout_ms)) {
|
||||
dbus_message_unref(msg);
|
||||
free(context);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!pending_call) {
|
||||
dbus_message_unref(msg);
|
||||
free(context);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
context->callback = callback;
|
||||
context->user_data = user_data;
|
||||
|
||||
dbus_pending_call_set_notify(pending_call, handle_authorization_reply,
|
||||
context, free);
|
||||
dbus_message_unref(msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,995 @@
|
|||
/*
|
||||
* Copyright (C) 2025 Xiaomi Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <execinfo.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <dbus/dbus-list.h>
|
||||
#include <dbus/dbus.h>
|
||||
#include <uv.h>
|
||||
|
||||
#include "gdbus-internal.h"
|
||||
|
||||
static DBusHandlerResult message_filter(DBusConnection* connection,
|
||||
DBusMessage* message, void* user_data);
|
||||
|
||||
struct GDBusWatch {
|
||||
DBusConnection* conn;
|
||||
guint serial;
|
||||
DBusList* list;
|
||||
gboolean conn_closed;
|
||||
};
|
||||
|
||||
struct service_data {
|
||||
DBusConnection* conn;
|
||||
DBusPendingCall* call;
|
||||
char* name;
|
||||
const char* owner;
|
||||
struct filter_callback* callback;
|
||||
uv_idle_t handle;
|
||||
};
|
||||
|
||||
struct filter_callback {
|
||||
GDBusWatchFunction conn_func;
|
||||
GDBusWatchFunction disc_func;
|
||||
GDBusSignalFunction signal_func;
|
||||
GDBusDestroyFunction destroy_func;
|
||||
struct service_data* data;
|
||||
void* user_data;
|
||||
guint id;
|
||||
};
|
||||
|
||||
struct filter_data {
|
||||
DBusConnection* connection;
|
||||
DBusHandleMessageFunction handle_func;
|
||||
char* name;
|
||||
char* owner;
|
||||
char* path;
|
||||
char* interface;
|
||||
char* member;
|
||||
char* argument;
|
||||
DBusList* callbacks;
|
||||
DBusList* processed;
|
||||
guint name_watch;
|
||||
gboolean lock;
|
||||
gboolean registered;
|
||||
GDBusWatch* watcher;
|
||||
};
|
||||
|
||||
static int strcmp0(const char* str1, const char* str2)
|
||||
{
|
||||
if (str1 == NULL)
|
||||
return -(str1 != str2);
|
||||
|
||||
if (str2 == NULL)
|
||||
return -(str1 != str2);
|
||||
|
||||
return strcmp(str1, str2);
|
||||
}
|
||||
|
||||
static char* strdup0(const char* str)
|
||||
{
|
||||
if (str)
|
||||
return strdup(str);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int get_watch_serial(GDBusWatch* watcher)
|
||||
{
|
||||
return ++watcher->serial;
|
||||
}
|
||||
|
||||
static struct filter_data* filter_data_find_match(DBusConnection* connection,
|
||||
DBusList* listener_list, const char* name, const char* owner,
|
||||
const char* path, const char* interface, const char* member, const char* argument)
|
||||
{
|
||||
DBusList* current;
|
||||
|
||||
for (current = _dbus_list_get_first_link(&listener_list); current != NULL;
|
||||
current = _dbus_list_get_next_link(&listener_list, current)) {
|
||||
struct filter_data* data = current->data;
|
||||
|
||||
if (connection != data->connection)
|
||||
continue;
|
||||
|
||||
if (strcmp0(name, data->name) != 0)
|
||||
continue;
|
||||
|
||||
if (strcmp0(owner, data->owner) != 0)
|
||||
continue;
|
||||
|
||||
if (strcmp0(path, data->path) != 0)
|
||||
continue;
|
||||
|
||||
if (strcmp0(interface, data->interface) != 0)
|
||||
continue;
|
||||
|
||||
if (strcmp0(member, data->member) != 0)
|
||||
continue;
|
||||
|
||||
if (strcmp0(argument, data->argument) != 0)
|
||||
continue;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct filter_data* filter_data_find(DBusConnection* connection, DBusList* listener_list)
|
||||
{
|
||||
DBusList* current;
|
||||
|
||||
for (current = _dbus_list_get_first_link(&listener_list); current != NULL;
|
||||
current = _dbus_list_get_next_link(&listener_list, current)) {
|
||||
struct filter_data* data = current->data;
|
||||
|
||||
if (connection != data->connection)
|
||||
continue;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void format_rule(struct filter_data* data, char* rule, size_t size)
|
||||
{
|
||||
const char* sender;
|
||||
int offset;
|
||||
|
||||
offset = snprintf(rule, size, "type='signal'");
|
||||
sender = data->name ?: data->owner;
|
||||
|
||||
if (sender)
|
||||
offset += snprintf(rule + offset, size - offset,
|
||||
",sender='%s'", sender);
|
||||
if (data->path)
|
||||
offset += snprintf(rule + offset, size - offset,
|
||||
",path='%s'", data->path);
|
||||
if (data->interface)
|
||||
offset += snprintf(rule + offset, size - offset,
|
||||
",interface='%s'", data->interface);
|
||||
if (data->member)
|
||||
offset += snprintf(rule + offset, size - offset,
|
||||
",member='%s'", data->member);
|
||||
if (data->argument)
|
||||
snprintf(rule + offset, size - offset,
|
||||
",arg0='%s'", data->argument);
|
||||
}
|
||||
|
||||
static gboolean add_match(struct filter_data* data,
|
||||
DBusHandleMessageFunction filter)
|
||||
{
|
||||
DBusError err;
|
||||
char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
|
||||
|
||||
format_rule(data, rule, sizeof(rule));
|
||||
dbus_error_init(&err);
|
||||
|
||||
dbus_bus_add_match(data->connection, rule, &err);
|
||||
if (dbus_error_is_set(&err)) {
|
||||
error("Adding match rule \"%s\" failed: %s", rule,
|
||||
err.message);
|
||||
dbus_error_free(&err);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
data->handle_func = filter;
|
||||
data->registered = TRUE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean remove_match(struct filter_data* data)
|
||||
{
|
||||
DBusError err;
|
||||
char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
|
||||
|
||||
if (data->watcher->conn_closed) {
|
||||
/* If the connection is disconnected, we don't need to remove the match */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
format_rule(data, rule, sizeof(rule));
|
||||
|
||||
dbus_error_init(&err);
|
||||
|
||||
dbus_bus_remove_match(data->connection, rule, &err);
|
||||
if (dbus_error_is_set(&err)) {
|
||||
error("GDBUS Removing owner match rule for %s failed: %s",
|
||||
rule, err.message);
|
||||
dump_stack();
|
||||
dbus_error_free(&err);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void filter_data_free(struct filter_data* data, DBusList* listener_list)
|
||||
{
|
||||
DBusList* l;
|
||||
|
||||
/* Remove filter if there are no listeners left for the connection */
|
||||
if (filter_data_find(data->connection, listener_list) == NULL) {
|
||||
dbus_connection_remove_filter(data->connection, message_filter,
|
||||
data->watcher);
|
||||
}
|
||||
|
||||
for (l = _dbus_list_get_first_link(&data->callbacks); l != NULL;
|
||||
l = _dbus_list_get_next_link(&data->callbacks, l))
|
||||
free(l->data);
|
||||
|
||||
_dbus_list_clear(&data->callbacks);
|
||||
dbus_remove_watch(data->watcher, data->name_watch);
|
||||
free(data->name);
|
||||
free(data->owner);
|
||||
free(data->path);
|
||||
free(data->interface);
|
||||
free(data->member);
|
||||
free(data->argument);
|
||||
|
||||
/* no listeners left for the connection */
|
||||
if (_dbus_list_get_last(&listener_list) == NULL) {
|
||||
free_dbus_watch(data->watcher);
|
||||
}
|
||||
dbus_connection_unref(data->connection);
|
||||
free(data);
|
||||
}
|
||||
|
||||
static struct filter_data* filter_data_get(GDBusWatch* watcher,
|
||||
DBusHandleMessageFunction filter,
|
||||
const char* sender, const char* path, const char* interface,
|
||||
const char* member, const char* argument)
|
||||
{
|
||||
struct filter_data* data;
|
||||
const char *name = NULL, *owner = NULL;
|
||||
|
||||
if (filter_data_find(watcher->conn, watcher->list) == NULL) {
|
||||
if (!dbus_connection_add_filter(watcher->conn,
|
||||
message_filter, watcher, NULL)) {
|
||||
error("dbus_connection_add_filter() failed");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (sender != NULL) {
|
||||
if (sender[0] == ':')
|
||||
owner = sender;
|
||||
else
|
||||
name = sender;
|
||||
}
|
||||
|
||||
data = filter_data_find_match(watcher->conn, watcher->list, name, owner, path,
|
||||
interface, member, argument);
|
||||
if (data)
|
||||
return data;
|
||||
|
||||
data = calloc(1, sizeof(struct filter_data));
|
||||
|
||||
data->connection = dbus_connection_ref(watcher->conn);
|
||||
data->name = strdup0(name);
|
||||
data->owner = strdup0(owner);
|
||||
data->path = strdup0(path);
|
||||
data->interface = strdup0(interface);
|
||||
data->member = strdup0(member);
|
||||
data->argument = strdup0(argument);
|
||||
data->watcher = watcher;
|
||||
|
||||
if (!add_match(data, filter)) {
|
||||
dbus_connection_unref(data->connection);
|
||||
free(data->name);
|
||||
free(data->owner);
|
||||
free(data->path);
|
||||
free(data->interface);
|
||||
free(data->member);
|
||||
free(data->argument);
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_dbus_list_append(&watcher->list, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
static struct filter_callback* filter_data_find_callback(
|
||||
struct filter_data* data,
|
||||
guint id)
|
||||
{
|
||||
DBusList* l;
|
||||
|
||||
for (l = _dbus_list_get_first_link(&data->callbacks); l != NULL;
|
||||
l = _dbus_list_get_next_link(&data->callbacks, l)) {
|
||||
struct filter_callback* cb = l->data;
|
||||
if (cb->id == id)
|
||||
return cb;
|
||||
}
|
||||
for (l = _dbus_list_get_first_link(&data->processed); l != NULL;
|
||||
l = _dbus_list_get_next_link(&data->processed, l)) {
|
||||
struct filter_callback* cb = l->data;
|
||||
if (cb->id == id)
|
||||
return cb;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void filter_data_call_and_free(struct filter_data* data, DBusList* listener_list)
|
||||
{
|
||||
DBusList* l;
|
||||
|
||||
for (l = _dbus_list_get_first_link(&data->callbacks); l != NULL;
|
||||
l = _dbus_list_get_next_link(&data->callbacks, l)) {
|
||||
struct filter_callback* cb = l->data;
|
||||
if (cb->disc_func)
|
||||
cb->disc_func(data->connection, cb->user_data);
|
||||
if (cb->destroy_func)
|
||||
cb->destroy_func(cb->user_data);
|
||||
}
|
||||
|
||||
filter_data_free(data, listener_list);
|
||||
}
|
||||
|
||||
static struct filter_callback* filter_data_add_callback(
|
||||
struct filter_data* data,
|
||||
GDBusWatchFunction connect,
|
||||
GDBusWatchFunction disconnect,
|
||||
GDBusSignalFunction signal,
|
||||
GDBusDestroyFunction destroy,
|
||||
guint watch_id,
|
||||
void* user_data)
|
||||
{
|
||||
struct filter_callback* cb = NULL;
|
||||
|
||||
cb = calloc(1, sizeof(struct filter_callback));
|
||||
|
||||
cb->conn_func = connect;
|
||||
cb->disc_func = disconnect;
|
||||
cb->signal_func = signal;
|
||||
cb->destroy_func = destroy;
|
||||
cb->user_data = user_data;
|
||||
cb->id = watch_id;
|
||||
|
||||
if (data->lock)
|
||||
_dbus_list_append(&data->processed, cb);
|
||||
else
|
||||
_dbus_list_append(&data->callbacks, cb);
|
||||
|
||||
return cb;
|
||||
}
|
||||
|
||||
static void close_cb(uv_handle_t* handle)
|
||||
{
|
||||
free(handle->data);
|
||||
}
|
||||
|
||||
static void service_data_free(struct service_data* data)
|
||||
{
|
||||
struct filter_callback* callback = data->callback;
|
||||
|
||||
dbus_connection_unref(data->conn);
|
||||
|
||||
if (data->call)
|
||||
dbus_pending_call_unref(data->call);
|
||||
|
||||
free(data->name);
|
||||
callback->data = NULL;
|
||||
|
||||
uv_close((uv_handle_t*)&data->handle, close_cb);
|
||||
}
|
||||
|
||||
/* Returns TRUE if data is freed */
|
||||
static gboolean filter_data_remove_callback(struct filter_data* data,
|
||||
struct filter_callback* cb)
|
||||
{
|
||||
_dbus_list_remove(&data->callbacks, cb);
|
||||
_dbus_list_remove(&data->processed, cb);
|
||||
|
||||
/* Cancel pending operations */
|
||||
if (cb->data) {
|
||||
if (cb->data->call)
|
||||
dbus_pending_call_cancel(cb->data->call);
|
||||
service_data_free(cb->data);
|
||||
}
|
||||
|
||||
if (cb->destroy_func)
|
||||
cb->destroy_func(cb->user_data);
|
||||
|
||||
free(cb);
|
||||
|
||||
/* Don't remove the filter if other callbacks exist or data is lock
|
||||
* processing callbacks */
|
||||
if (data->callbacks || data->lock)
|
||||
return FALSE;
|
||||
|
||||
if (data->registered && !remove_match(data))
|
||||
return FALSE;
|
||||
|
||||
_dbus_list_remove(&data->watcher->list, data);
|
||||
filter_data_free(data, data->watcher->list);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static DBusHandlerResult signal_filter(DBusConnection* connection,
|
||||
DBusMessage* message, void* user_data)
|
||||
{
|
||||
struct filter_data* data = user_data;
|
||||
struct filter_callback* cb;
|
||||
|
||||
while (data->callbacks) {
|
||||
cb = data->callbacks->data;
|
||||
|
||||
if (cb->signal_func && !cb->signal_func(connection, message, cb->user_data)) {
|
||||
if (filter_data_remove_callback(data, cb))
|
||||
break;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check if the watch was removed/freed by the callback
|
||||
* function */
|
||||
if (_dbus_list_find_last(&data->callbacks, cb) == NULL)
|
||||
continue;
|
||||
|
||||
_dbus_list_remove(&data->callbacks, cb);
|
||||
_dbus_list_append(&data->processed, cb);
|
||||
}
|
||||
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
static void update_name_cache(DBusList* listener_list, const char* name, const char* owner)
|
||||
{
|
||||
DBusList* l;
|
||||
|
||||
for (l = _dbus_list_get_first_link(&listener_list); l != NULL;
|
||||
l = _dbus_list_get_next_link(&listener_list, l)) {
|
||||
struct filter_data* data = l->data;
|
||||
|
||||
if (strcmp0(data->name, name) != 0)
|
||||
continue;
|
||||
|
||||
free(data->owner);
|
||||
data->owner = strdup0(owner);
|
||||
}
|
||||
}
|
||||
|
||||
static const char* check_name_cache(DBusList* listener_list, const char* name)
|
||||
{
|
||||
DBusList* l;
|
||||
|
||||
for (l = _dbus_list_get_first_link(&listener_list); l != NULL;
|
||||
l = _dbus_list_get_next_link(&listener_list, l)) {
|
||||
struct filter_data* data = l->data;
|
||||
|
||||
if (strcmp0(data->name, name) != 0)
|
||||
continue;
|
||||
|
||||
return data->owner;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static DBusHandlerResult service_filter(DBusConnection* connection,
|
||||
DBusMessage* message, void* user_data)
|
||||
{
|
||||
struct filter_data* data = user_data;
|
||||
struct filter_callback* cb;
|
||||
char *name, *old, *new;
|
||||
|
||||
if (!dbus_message_get_args(message, NULL,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_STRING, &old,
|
||||
DBUS_TYPE_STRING, &new,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
error("Invalid arguments for NameOwnerChanged signal");
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
update_name_cache(data->watcher->list, name, new);
|
||||
|
||||
while (data->callbacks) {
|
||||
cb = data->callbacks->data;
|
||||
|
||||
if (*new == '\0') {
|
||||
if (cb->disc_func)
|
||||
cb->disc_func(connection, cb->user_data);
|
||||
} else {
|
||||
if (cb->conn_func)
|
||||
cb->conn_func(connection, cb->user_data);
|
||||
}
|
||||
|
||||
/* Check if the watch was removed/freed by the callback
|
||||
* function */
|
||||
if (_dbus_list_find_last(&data->callbacks, cb) == NULL)
|
||||
continue;
|
||||
|
||||
/* Only auto remove if it is a bus name watch */
|
||||
if (data->argument[0] == ':' && (cb->conn_func == NULL || cb->disc_func == NULL)) {
|
||||
if (filter_data_remove_callback(data, cb))
|
||||
break;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
_dbus_list_remove(&data->callbacks, cb);
|
||||
_dbus_list_append(&data->processed, cb);
|
||||
}
|
||||
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
static bool process_signal_message(DBusMessage* message,
|
||||
const char** sender, const char** path,
|
||||
const char** iface, const char** member,
|
||||
const char** arg)
|
||||
{
|
||||
if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
|
||||
return false;
|
||||
|
||||
*sender = dbus_message_get_sender(message);
|
||||
*path = dbus_message_get_path(message);
|
||||
*iface = dbus_message_get_interface(message);
|
||||
*member = dbus_message_get_member(message);
|
||||
if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, arg, DBUS_TYPE_INVALID))
|
||||
*arg = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
static int filter_data_match(const char* str1, const char* data_param)
|
||||
{
|
||||
if (data_param == NULL)
|
||||
return 0;
|
||||
|
||||
if (str1 == NULL)
|
||||
return -1;
|
||||
|
||||
return strcmp(str1, data_param);
|
||||
}
|
||||
|
||||
static gboolean match_listener_data(struct filter_data* data, DBusConnection* connection,
|
||||
const char* sender, const char* path,
|
||||
const char* iface, const char* member, const char* arg)
|
||||
{
|
||||
if (connection != data->connection)
|
||||
return FALSE;
|
||||
|
||||
if (filter_data_match(sender, data->owner) != 0)
|
||||
return FALSE;
|
||||
|
||||
if (filter_data_match(path, data->path) != 0)
|
||||
return FALSE;
|
||||
|
||||
if (filter_data_match(iface, data->interface) != 0)
|
||||
return FALSE;
|
||||
|
||||
if (filter_data_match(member, data->member) != 0)
|
||||
return FALSE;
|
||||
|
||||
if (filter_data_match(arg, data->argument) != 0)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void process_listener_callbacks(struct filter_data* data, DBusConnection* connection,
|
||||
DBusMessage* message)
|
||||
{
|
||||
|
||||
if (!data->handle_func)
|
||||
return;
|
||||
|
||||
data->lock = TRUE;
|
||||
data->handle_func(connection, message, data);
|
||||
data->callbacks = data->processed;
|
||||
data->processed = NULL;
|
||||
data->lock = FALSE;
|
||||
}
|
||||
|
||||
static void process_listeners(DBusList* listener_list, DBusConnection* connection,
|
||||
DBusMessage* message, DBusList** delete_listener, const char* sender,
|
||||
const char* path, const char* iface, const char* member, const char* arg)
|
||||
{
|
||||
DBusList* current;
|
||||
struct filter_data* data;
|
||||
|
||||
for (current = _dbus_list_get_first_link(&listener_list); current;
|
||||
current = _dbus_list_get_next_link(&listener_list, current)) {
|
||||
data = current->data;
|
||||
|
||||
if (!match_listener_data(data, connection, sender,
|
||||
path, iface, member, arg))
|
||||
continue;
|
||||
|
||||
process_listener_callbacks(data, connection, message);
|
||||
|
||||
if (!data->callbacks)
|
||||
_dbus_list_prepend(delete_listener, current);
|
||||
}
|
||||
}
|
||||
|
||||
static void cleanup_listeners(DBusList* delete_listener, GDBusWatch* watcher)
|
||||
{
|
||||
DBusList* current;
|
||||
struct filter_data* data;
|
||||
|
||||
for (current = _dbus_list_get_first_link(&delete_listener); current;
|
||||
current = _dbus_list_get_next_link(&delete_listener, current)) {
|
||||
DBusList* l = current->data;
|
||||
data = l->data;
|
||||
|
||||
/* Has any other callback added callbacks back to this data? */
|
||||
if (data->callbacks)
|
||||
continue;
|
||||
|
||||
remove_match(data);
|
||||
_dbus_list_remove_link(&watcher->list, l);
|
||||
filter_data_free(data, watcher->list);
|
||||
}
|
||||
_dbus_list_clear(&delete_listener);
|
||||
}
|
||||
|
||||
static DBusHandlerResult message_filter(DBusConnection* connection,
|
||||
DBusMessage* message, void* user_data)
|
||||
{
|
||||
const char *sender, *path, *iface, *member, *arg = NULL;
|
||||
DBusList* delete_listener = NULL;
|
||||
GDBusWatch* watcher = user_data;
|
||||
|
||||
if (!process_signal_message(message, &sender, &path,
|
||||
&iface, &member, &arg))
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
|
||||
process_listeners(watcher->list, connection, message, &delete_listener,
|
||||
sender, path, iface, member, arg);
|
||||
|
||||
if (delete_listener)
|
||||
cleanup_listeners(delete_listener, watcher);
|
||||
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
static void update_service(uv_idle_t* handle)
|
||||
{
|
||||
struct service_data* data = handle->data;
|
||||
struct filter_callback* cb = data->callback;
|
||||
DBusConnection* conn;
|
||||
|
||||
conn = dbus_connection_ref(data->conn);
|
||||
service_data_free(data);
|
||||
|
||||
if (cb->conn_func)
|
||||
cb->conn_func(conn, cb->user_data);
|
||||
|
||||
dbus_connection_unref(conn);
|
||||
}
|
||||
|
||||
static void service_reply(DBusPendingCall* call, void* user_data)
|
||||
{
|
||||
struct service_data* data = user_data;
|
||||
DBusMessage* reply;
|
||||
DBusError err;
|
||||
|
||||
reply = dbus_pending_call_steal_reply(call);
|
||||
if (reply == NULL)
|
||||
return;
|
||||
|
||||
dbus_error_init(&err);
|
||||
|
||||
if (dbus_set_error_from_message(&err, reply))
|
||||
goto fail;
|
||||
|
||||
if (dbus_message_get_args(reply, &err, DBUS_TYPE_STRING,
|
||||
&data->owner, DBUS_TYPE_INVALID)
|
||||
== FALSE)
|
||||
goto fail;
|
||||
|
||||
update_service(&data->handle);
|
||||
|
||||
goto done;
|
||||
|
||||
fail:
|
||||
error("service_reply fail: %s", err.message);
|
||||
dbus_error_free(&err);
|
||||
service_data_free(data);
|
||||
done:
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
|
||||
static struct service_data* create_service_data(DBusConnection* conn,
|
||||
const char* name,
|
||||
struct filter_callback* cb)
|
||||
{
|
||||
struct service_data* data = calloc(1, sizeof(*data));
|
||||
if (!data) {
|
||||
error("%s: malloc failed", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data->conn = dbus_connection_ref(conn);
|
||||
data->name = strdup0(name);
|
||||
data->callback = cb;
|
||||
cb->data = data;
|
||||
return data;
|
||||
}
|
||||
|
||||
static bool init_uv_handle(struct service_data* data)
|
||||
{
|
||||
if (uv_idle_init(uv_default_loop(), &data->handle) != 0) {
|
||||
error("%s: uv_idle_init failed", __func__);
|
||||
return false;
|
||||
}
|
||||
data->handle.data = data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static gboolean send_name_owner_request(struct service_data* data, const char* name)
|
||||
{
|
||||
DBusMessage* msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
|
||||
DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner");
|
||||
if (!msg) {
|
||||
error("%s: dbus_message_new_method_call failed", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID);
|
||||
if (!dbus_connection_send_with_reply(data->conn, msg, &data->call, -1)) {
|
||||
error("%s: dbus_connection_send_with_reply failed", __func__);
|
||||
dbus_message_unref(msg);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!data->call) {
|
||||
error("%s: dbus reply failed with empty pendingcall", __func__);
|
||||
dbus_message_unref(msg);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (dbus_pending_call_get_completed(data->call)) {
|
||||
service_reply(data->call, data);
|
||||
} else {
|
||||
dbus_pending_call_set_notify(data->call, service_reply, data, NULL);
|
||||
}
|
||||
|
||||
dbus_message_unref(msg);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void check_service(DBusConnection* conn, const char* name,
|
||||
struct filter_callback* cb, DBusList* listener_list)
|
||||
{
|
||||
struct service_data* data = create_service_data(conn, name, cb);
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
if (!init_uv_handle(data)) {
|
||||
dbus_connection_unref(data->conn);
|
||||
free(data->name);
|
||||
free(data);
|
||||
return;
|
||||
}
|
||||
|
||||
data->owner = check_name_cache(listener_list, name);
|
||||
if (data->owner) {
|
||||
if (uv_idle_start(&data->handle, update_service) != 0) {
|
||||
error("%s: uv_idle_start failed:", __func__);
|
||||
uv_close((uv_handle_t*)&data->handle, NULL);
|
||||
dbus_connection_unref(data->conn);
|
||||
free(data->name);
|
||||
free(data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (send_name_owner_request(data, name) == FALSE) {
|
||||
uv_close((uv_handle_t*)&data->handle, NULL);
|
||||
dbus_connection_unref(data->conn);
|
||||
free(data->name);
|
||||
free(data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
guint dbus_add_service_watch(GDBusWatch* watcher, const char* name,
|
||||
GDBusWatchFunction connect,
|
||||
GDBusWatchFunction disconnect,
|
||||
void* user_data, GDBusDestroyFunction destroy)
|
||||
{
|
||||
struct filter_data* data;
|
||||
struct filter_callback* cb;
|
||||
|
||||
if (name == NULL)
|
||||
return 0;
|
||||
|
||||
data = filter_data_get(watcher, service_filter, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
|
||||
DBUS_INTERFACE_DBUS, "NameOwnerChanged", name);
|
||||
if (data == NULL)
|
||||
return 0;
|
||||
|
||||
cb = filter_data_add_callback(data, connect, disconnect, NULL, destroy,
|
||||
get_watch_serial(watcher), user_data);
|
||||
if (cb == NULL)
|
||||
return 0;
|
||||
|
||||
if (connect)
|
||||
check_service(watcher->conn, name, cb, watcher->list);
|
||||
|
||||
return cb->id;
|
||||
}
|
||||
|
||||
guint dbus_add_service_disconnect_watch(GDBusWatch* watcher, const char* name,
|
||||
GDBusWatchFunction func, void* user_data, GDBusDestroyFunction destroy)
|
||||
{
|
||||
return dbus_add_service_watch(watcher, name, NULL, func, user_data, destroy);
|
||||
}
|
||||
|
||||
guint dbus_add_signal_watch(GDBusWatch* watcher,
|
||||
const char* sender, const char* path,
|
||||
const char* interface, const char* member,
|
||||
GDBusSignalFunction function, void* user_data,
|
||||
GDBusDestroyFunction destroy)
|
||||
{
|
||||
struct filter_data* data;
|
||||
struct filter_callback* cb;
|
||||
|
||||
data = filter_data_get(watcher, signal_filter, sender, path,
|
||||
interface, member, NULL);
|
||||
if (data == NULL)
|
||||
return 0;
|
||||
|
||||
cb = filter_data_add_callback(data, NULL, NULL, function, destroy,
|
||||
get_watch_serial(watcher), user_data);
|
||||
if (cb == NULL)
|
||||
return 0;
|
||||
|
||||
if (data->name != NULL && data->name_watch == 0)
|
||||
data->name_watch = dbus_add_service_watch(watcher,
|
||||
data->name, NULL,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
return cb->id;
|
||||
}
|
||||
|
||||
guint dbus_add_properties_watch(GDBusWatch* watcher,
|
||||
const char* sender, const char* path,
|
||||
const char* interface,
|
||||
GDBusSignalFunction function, void* user_data,
|
||||
GDBusDestroyFunction destroy)
|
||||
{
|
||||
struct filter_data* data;
|
||||
struct filter_callback* cb;
|
||||
|
||||
data = filter_data_get(watcher, signal_filter, sender, path,
|
||||
DBUS_INTERFACE_PROPERTIES, "PropertiesChanged", interface);
|
||||
if (data == NULL)
|
||||
return 0;
|
||||
|
||||
cb = filter_data_add_callback(data, NULL, NULL, function, destroy,
|
||||
get_watch_serial(watcher), user_data);
|
||||
if (cb == NULL)
|
||||
return 0;
|
||||
|
||||
if (data->name != NULL && data->name_watch == 0)
|
||||
data->name_watch = dbus_add_service_watch(watcher,
|
||||
data->name, NULL,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
return cb->id;
|
||||
}
|
||||
|
||||
gboolean dbus_remove_watch(GDBusWatch* watcher, guint id)
|
||||
{
|
||||
struct filter_data* data;
|
||||
struct filter_callback* cb;
|
||||
DBusList* ldata;
|
||||
|
||||
if (id == 0)
|
||||
return FALSE;
|
||||
|
||||
for (ldata = _dbus_list_get_first_link(&watcher->list); ldata != NULL;
|
||||
ldata = _dbus_list_get_next_link(&watcher->list, ldata)) {
|
||||
data = ldata->data;
|
||||
|
||||
cb = filter_data_find_callback(data, id);
|
||||
if (cb) {
|
||||
filter_data_remove_callback(data, cb);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void dbus_remove_all_watches(GDBusWatch* watcher)
|
||||
{
|
||||
struct filter_data* data;
|
||||
|
||||
while ((data = filter_data_find(watcher->conn, watcher->list))) {
|
||||
_dbus_list_remove(&watcher->list, data);
|
||||
filter_data_call_and_free(data, watcher->list);
|
||||
}
|
||||
}
|
||||
|
||||
GDBusWatch* new_dbus_watch(DBusConnection* connection)
|
||||
{
|
||||
GDBusWatch* watcher;
|
||||
|
||||
watcher = calloc(1, sizeof(GDBusWatch));
|
||||
if (watcher == NULL)
|
||||
return NULL;
|
||||
|
||||
watcher->conn = dbus_connection_ref(connection);
|
||||
watcher->list = NULL;
|
||||
watcher->serial = 0;
|
||||
watcher->conn_closed = FALSE;
|
||||
|
||||
return watcher;
|
||||
}
|
||||
void free_dbus_watch(GDBusWatch* watcher)
|
||||
{
|
||||
dbus_connection_unref(watcher->conn);
|
||||
free(watcher);
|
||||
}
|
||||
|
||||
guint dbus_client_add_service_watch(GDBusClient* client, const char* name,
|
||||
GDBusWatchFunction connect, GDBusWatchFunction disconnect,
|
||||
void* user_data, GDBusDestroyFunction destroy)
|
||||
{
|
||||
return dbus_add_service_watch(client->watcher, name, connect, disconnect,
|
||||
user_data, destroy);
|
||||
}
|
||||
|
||||
guint dbus_client_add_service_disconnect_watch(GDBusClient* client, const char* name,
|
||||
GDBusWatchFunction function, void* user_data, GDBusDestroyFunction destroy)
|
||||
{
|
||||
return dbus_add_service_disconnect_watch(client->watcher, name, function, user_data, destroy);
|
||||
}
|
||||
|
||||
guint dbus_client_add_signal_watch(GDBusClient* client, const char* sender,
|
||||
const char* path, const char* interface, const char* member,
|
||||
GDBusSignalFunction function, void* user_data, GDBusDestroyFunction destroy)
|
||||
{
|
||||
return dbus_add_signal_watch(client->watcher, sender, path, interface,
|
||||
member, function, user_data, destroy);
|
||||
}
|
||||
|
||||
guint dbus_client_add_properties_watch(GDBusClient* client,
|
||||
const char* sender, const char* path, const char* interface,
|
||||
GDBusSignalFunction function, void* user_data, GDBusDestroyFunction destroy)
|
||||
{
|
||||
return dbus_add_properties_watch(client->watcher, sender, path, interface,
|
||||
function, user_data, destroy);
|
||||
}
|
||||
|
||||
gboolean dbus_client_remove_watch(GDBusClient* client, guint tag)
|
||||
{
|
||||
return dbus_remove_watch(client->watcher, tag);
|
||||
}
|
||||
|
||||
void dbus_client_remove_all_watches(GDBusClient* client)
|
||||
{
|
||||
dbus_remove_all_watches(client->watcher);
|
||||
}
|
||||
|
||||
void dbus_watch_set_connection_state(GDBusWatch* watcher, gboolean closed)
|
||||
{
|
||||
watcher->conn_closed = closed;
|
||||
}
|
||||
|
|
@ -0,0 +1,437 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @addtogroup Logging
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Support routines to send messages to the Android log buffer,
|
||||
* which can later be accessed through the `logcat` utility.
|
||||
*
|
||||
* Each log message must have
|
||||
* - a priority
|
||||
* - a log tag
|
||||
* - some text
|
||||
*
|
||||
* The tag normally corresponds to the component that emits the log message,
|
||||
* and should be reasonably small.
|
||||
*
|
||||
* Log message text may be truncated to less than an implementation-specific
|
||||
* limit (1023 bytes).
|
||||
*
|
||||
* Note that a newline character ("\n") will be appended automatically to your
|
||||
* log message, if not already there. It is not possible to send several
|
||||
* messages and have them appear on a single line in logcat.
|
||||
*
|
||||
* Please use logging in moderation:
|
||||
*
|
||||
* - Sending log messages eats CPU and slow down your application and the
|
||||
* system.
|
||||
*
|
||||
* - The circular log buffer is pretty small, so sending many messages
|
||||
* will hide other important log messages.
|
||||
*
|
||||
* - In release builds, only send log messages to account for exceptional
|
||||
* conditions.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if !defined(__BIONIC__) && !defined(__INTRODUCED_IN)
|
||||
#define __INTRODUCED_IN(x)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* clang-format off */
|
||||
/**
|
||||
* @brief Android log priority values, in increasing order of priority.
|
||||
*
|
||||
* In Vela, we could using the CONFIG_ALOG to control the default log filter level
|
||||
*/
|
||||
typedef enum android_LogPriority {
|
||||
/**
|
||||
* @brief For internal use only.
|
||||
*/
|
||||
ANDROID_LOG_UNKNOWN = 0,
|
||||
/**
|
||||
* @brief The default priority, for internal use only.
|
||||
* only for SetMinPriority()
|
||||
*/
|
||||
ANDROID_LOG_DEFAULT,
|
||||
/**
|
||||
* @brief Verbose logging. Should typically be disabled for a release apk.
|
||||
*/
|
||||
ANDROID_LOG_VERBOSE,
|
||||
/**
|
||||
* @brief Debug logging. Should typically be disabled for a release apk.
|
||||
*/
|
||||
ANDROID_LOG_DEBUG,
|
||||
/**
|
||||
* @brief Informational logging. Should typically be disabled for a release apk.
|
||||
*/
|
||||
ANDROID_LOG_INFO,
|
||||
/**
|
||||
* @brief Warning logging. For use with recoverable failures.
|
||||
*/
|
||||
ANDROID_LOG_WARN,
|
||||
/**
|
||||
* @brief Error logging. For use with unrecoverable failures.
|
||||
*/
|
||||
ANDROID_LOG_ERROR,
|
||||
/**
|
||||
* @brief Fatal logging. For use when aborting.
|
||||
*/
|
||||
ANDROID_LOG_FATAL,
|
||||
/**
|
||||
* @brief For internal use only.
|
||||
* only for SetMinPriority(); must be last
|
||||
*/
|
||||
ANDROID_LOG_SILENT,
|
||||
} android_LogPriority;
|
||||
|
||||
/**
|
||||
* @brief Writes the constant string `text` to the log, with priority `prio` and tag
|
||||
* `tag`.
|
||||
* @param[in] prio the log filter level
|
||||
* @param[in] tag the tag using to trace and filter log info
|
||||
* @param[in] text the detailed log content to print out or record
|
||||
* @return return 0 on success, non-zero on failure
|
||||
*/
|
||||
int __android_log_write(int prio, const char* tag, const char* text);
|
||||
|
||||
/**
|
||||
* @brief Writes a formatted string to the log, with priority `prio` and tag `tag`.
|
||||
* The details of formatting are the same as for
|
||||
* [printf(3)](http://man7.org/linux/man-pages/man3/printf.3.html).
|
||||
*
|
||||
* @param[in] prio the log filter level
|
||||
* @param[in] tag the tag using to trace and filter log info
|
||||
* @param[in] fmt the formatted string
|
||||
* @return return 0 on success, non-zero on failure
|
||||
*/
|
||||
int __android_log_print(int prio, const char* tag, const char* fmt, ...)
|
||||
__attribute__((__format__(printf, 3, 4)));
|
||||
|
||||
/**
|
||||
* @brief Equivalent to `__android_log_print`, but taking a `va_list`.
|
||||
* (If `__android_log_print` is like `printf`, this is like `vprintf`.)
|
||||
*
|
||||
* @param[in] prio the log filter level
|
||||
* @param[in] tag the tag using to trace and filter log info
|
||||
* @param[in] fmt the formatted string
|
||||
* @param[in] ap variadic list of all parameters
|
||||
* @return return 0 on success, non-zero on failure
|
||||
*/
|
||||
int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap)
|
||||
__attribute__((__format__(printf, 3, 0)));
|
||||
|
||||
/**
|
||||
* @brief Writes an assertion failure to the log (as `ANDROID_LOG_FATAL`) and to
|
||||
* stderr, before calling
|
||||
* [abort(3)](http://man7.org/linux/man-pages/man3/abort.3.html).
|
||||
*
|
||||
* If `fmt` is non-null, `cond` is unused. If `fmt` is null, the string
|
||||
* `Assertion failed: %s` is used with `cond` as the string argument.
|
||||
* If both `fmt` and `cond` are null, a default string is provided.
|
||||
*
|
||||
* Most callers should use
|
||||
* [assert(3)](http://man7.org/linux/man-pages/man3/assert.3.html) from
|
||||
* `<assert.h>` instead, or the `__assert` and `__assert2` functions
|
||||
* provided by bionic if more control is needed. They support automatically
|
||||
* including the source filename and line number more conveniently than this
|
||||
* function.
|
||||
*
|
||||
* @param[in] cond the string representation of assertion condition
|
||||
* @param[in] tag the tag using to trace and filter log info
|
||||
* @param[in] fmt the formatted string
|
||||
* @return return 0 on success, non-zero on failure
|
||||
*/
|
||||
void __android_log_assert(const char* cond, const char* tag, const char* fmt, ...)
|
||||
__attribute__((__noreturn__)) __attribute__((__format__(printf, 3, 4)));
|
||||
|
||||
/**
|
||||
* @brief Identifies a specific log buffer for __android_log_buf_write()
|
||||
* and __android_log_buf_print().
|
||||
*/
|
||||
typedef enum log_id {
|
||||
LOG_ID_MIN = 0,
|
||||
|
||||
/**
|
||||
* @brief The main log buffer. This is the only log buffer available to apps.
|
||||
*/
|
||||
LOG_ID_MAIN = 0,
|
||||
/**
|
||||
* @brief The radio log buffer.
|
||||
*/
|
||||
LOG_ID_RADIO = 1,
|
||||
/**
|
||||
* @brief The event log buffer.
|
||||
*/
|
||||
LOG_ID_EVENTS = 2,
|
||||
/**
|
||||
* @brief The system log buffer.
|
||||
*/
|
||||
LOG_ID_SYSTEM = 3,
|
||||
/**
|
||||
* @brief The crash log buffer.
|
||||
*/
|
||||
LOG_ID_CRASH = 4,
|
||||
/**
|
||||
* @brief The statistics log buffer.
|
||||
*/
|
||||
LOG_ID_STATS = 5,
|
||||
/**
|
||||
* @brief The security log buffer.
|
||||
*/
|
||||
LOG_ID_SECURITY = 6,
|
||||
/**
|
||||
* @brief The kernel log buffer.
|
||||
*/
|
||||
LOG_ID_KERNEL = 7,
|
||||
|
||||
LOG_ID_MAX,
|
||||
|
||||
/** Let the logging function choose the best log target. */
|
||||
LOG_ID_DEFAULT = 0x7FFFFFFF
|
||||
} log_id_t;
|
||||
|
||||
/**
|
||||
* Writes the constant string `text` to the log buffer `id`,
|
||||
* with priority `prio` and tag `tag`.
|
||||
*
|
||||
* Apps should use __android_log_write() instead.
|
||||
*/
|
||||
int __android_log_buf_write(int bufID, int prio, const char* tag, const char* text);
|
||||
|
||||
/**
|
||||
* Writes a formatted string to log buffer `id`,
|
||||
* with priority `prio` and tag `tag`.
|
||||
* The details of formatting are the same as for
|
||||
* [printf(3)](http://man7.org/linux/man-pages/man3/printf.3.html).
|
||||
*
|
||||
* Apps should use __android_log_print() instead.
|
||||
*/
|
||||
int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
|
||||
__attribute__((__format__(printf, 4, 5)));
|
||||
|
||||
/**
|
||||
* Logger data struct used for writing log messages to liblog via __android_log_write_logger_data()
|
||||
* and sending log messages to user defined loggers specified in __android_log_set_logger().
|
||||
*/
|
||||
struct __android_log_message {
|
||||
/** Must be set to sizeof(__android_log_message) and is used for versioning. */
|
||||
size_t struct_size;
|
||||
|
||||
/** {@link log_id_t} values. */
|
||||
int32_t buffer_id;
|
||||
|
||||
/** {@link android_LogPriority} values. */
|
||||
int32_t priority;
|
||||
|
||||
/** The tag for the log message. */
|
||||
const char* tag;
|
||||
|
||||
/** Optional file name, may be set to nullptr. */
|
||||
const char* file;
|
||||
|
||||
/** Optional line number, ignore if file is nullptr. */
|
||||
uint32_t line;
|
||||
|
||||
/** The log message itself. */
|
||||
const char* message;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prototype for the 'logger' function that is called for every log message.
|
||||
*/
|
||||
typedef void (*__android_logger_function)(const struct __android_log_message* log_message);
|
||||
/**
|
||||
* Prototype for the 'abort' function that is called when liblog will abort due to
|
||||
* __android_log_assert() failures.
|
||||
*/
|
||||
typedef void (*__android_aborter_function)(const char* abort_message);
|
||||
|
||||
/**
|
||||
* Writes the log message specified by log_message. log_message includes additional file name and
|
||||
* line number information that a logger may use. log_message is versioned for backwards
|
||||
* compatibility.
|
||||
* This assumes that loggability has already been checked through __android_log_is_loggable().
|
||||
* Higher level logging libraries, such as libbase, first check loggability, then format their
|
||||
* buffers, then pass the message to liblog via this function, and therefore we do not want to
|
||||
* duplicate the loggability check here.
|
||||
*
|
||||
* @param log_message the log message itself, see __android_log_message.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_write_log_message(struct __android_log_message* log_message) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Sets a user defined logger function. All log messages sent to liblog will be set to the
|
||||
* function pointer specified by logger for processing. It is not expected that log messages are
|
||||
* already terminated with a new line. This function should add new lines if required for line
|
||||
* separation.
|
||||
*
|
||||
* @param logger the new function that will handle log messages.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_set_logger(__android_logger_function logger) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Writes the log message to logd. This is an __android_logger_function and can be provided to
|
||||
* __android_log_set_logger(). It is the default logger when running liblog on a device.
|
||||
*
|
||||
* @param log_message the log message to write, see __android_log_message.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_logd_logger(const struct __android_log_message* log_message) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Writes the log message to stderr. This is an __android_logger_function and can be provided to
|
||||
* __android_log_set_logger(). It is the default logger when running liblog on host.
|
||||
*
|
||||
* @param log_message the log message to write, see __android_log_message.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_stderr_logger(const struct __android_log_message* log_message)
|
||||
__INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Sets a user defined aborter function that is called for __android_log_assert() failures. This
|
||||
* user defined aborter function is highly recommended to abort and be noreturn, but is not strictly
|
||||
* required to.
|
||||
*
|
||||
* @param aborter the new aborter function, see __android_aborter_function.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_set_aborter(__android_aborter_function aborter) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Calls the stored aborter function. This allows for other logging libraries to use the same
|
||||
* aborter function by calling this function in liblog.
|
||||
*
|
||||
* @param abort_message an additional message supplied when aborting, for example this is used to
|
||||
* call android_set_abort_message() in __android_log_default_aborter().
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_call_aborter(const char* abort_message) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Sets android_set_abort_message() on device then aborts(). This is the default aborter.
|
||||
*
|
||||
* @param abort_message an additional message supplied when aborting. This functions calls
|
||||
* android_set_abort_message() with its contents.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_default_aborter(const char* abort_message) __attribute__((noreturn))
|
||||
__INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Use the per-tag properties "log.tag.<tagname>" along with the minimum priority from
|
||||
* __android_log_set_minimum_priority() to determine if a log message with a given prio and tag will
|
||||
* be printed. A non-zero result indicates yes, zero indicates false.
|
||||
*
|
||||
* If both a priority for a tag and a minimum priority are set by
|
||||
* __android_log_set_minimum_priority(), then the lowest of the two values are to determine the
|
||||
* minimum priority needed to log. If only one is set, then that value is used to determine the
|
||||
* minimum priority needed. If none are set, then default_priority is used.
|
||||
*
|
||||
* @param prio the priority to test, takes android_LogPriority values.
|
||||
* @param tag the tag to test.
|
||||
* @param default_prio the default priority to use if no properties or minimum priority are set.
|
||||
* @return an integer where 1 indicates that the message is loggable and 0 indicates that it is not.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
int __android_log_is_loggable(int prio, const char* tag, int default_prio) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Use the per-tag properties "log.tag.<tagname>" along with the minimum priority from
|
||||
* __android_log_set_minimum_priority() to determine if a log message with a given prio and tag will
|
||||
* be printed. A non-zero result indicates yes, zero indicates false.
|
||||
*
|
||||
* If both a priority for a tag and a minimum priority are set by
|
||||
* __android_log_set_minimum_priority(), then the lowest of the two values are to determine the
|
||||
* minimum priority needed to log. If only one is set, then that value is used to determine the
|
||||
* minimum priority needed. If none are set, then default_priority is used.
|
||||
*
|
||||
* @param prio the priority to test, takes android_LogPriority values.
|
||||
* @param tag the tag to test.
|
||||
* @param len the length of the tag.
|
||||
* @param default_prio the default priority to use if no properties or minimum priority are set.
|
||||
* @return an integer where 1 indicates that the message is loggable and 0 indicates that it is not.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio)
|
||||
__INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Sets the minimum priority that will be logged for this process.
|
||||
*
|
||||
* @param priority the new minimum priority to set, takes android_LogPriority values.
|
||||
* @return the previous set minimum priority as android_LogPriority values, or
|
||||
* ANDROID_LOG_DEFAULT if none was set.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
int32_t __android_log_set_minimum_priority(int32_t priority) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Gets the minimum priority that will be logged for this process. If none has been set by a
|
||||
* previous __android_log_set_minimum_priority() call, this returns ANDROID_LOG_DEFAULT.
|
||||
*
|
||||
* @return the current minimum priority as android_LogPriority values, or
|
||||
* ANDROID_LOG_DEFAULT if none is set.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
int32_t __android_log_get_minimum_priority(void) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Sets the default tag if no tag is provided when writing a log message. Defaults to
|
||||
* getprogname(). This truncates tag to the maximum log message size, though appropriate tags
|
||||
* should be much smaller.
|
||||
*
|
||||
* @param tag the new log tag.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_set_default_tag(const char* tag) __INTRODUCED_IN(30);
|
||||
|
||||
/* clang-format on */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @file android/set_abort_message.h
|
||||
* @brief The android_set_abort_message() function.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* android_set_abort_message() sets the abort message that will be shown
|
||||
* by [debuggerd](https://source.android.com/devices/tech/debug/native-crash).
|
||||
* This is meant for use by libraries that deliberately abort so that they can
|
||||
* provide an explanation. It is used within bionic to implement assert() and
|
||||
* all FORTIFY/fdsan aborts.
|
||||
*
|
||||
* Available since API level 21.
|
||||
*/
|
||||
void android_set_abort_message(const char* __msg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <sys/system_properties.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//
|
||||
// Deprecated.
|
||||
//
|
||||
// See <android-base/properties.h> for better API.
|
||||
//
|
||||
|
||||
#define PROPERTY_KEY_MAX PROP_NAME_MAX
|
||||
#define PROPERTY_VALUE_MAX PROP_VALUE_MAX
|
||||
|
||||
/* property_get: returns the length of the value which will never be
|
||||
** greater than PROPERTY_VALUE_MAX - 1 and will always be zero terminated.
|
||||
** (the length does not include the terminating zero).
|
||||
**
|
||||
** If the property read fails or returns an empty value, the default
|
||||
** value is used (if nonnull).
|
||||
*/
|
||||
int property_get(const char* key, char* value, const char* default_value);
|
||||
|
||||
/* property_get_bool: returns the value of key coerced into a
|
||||
** boolean. If the property is not set, then the default value is returned.
|
||||
**
|
||||
* The following is considered to be true (1):
|
||||
** "1", "true", "y", "yes", "on"
|
||||
**
|
||||
** The following is considered to be false (0):
|
||||
** "0", "false", "n", "no", "off"
|
||||
**
|
||||
** The conversion is whitespace-sensitive (e.g. " off" will not be false).
|
||||
**
|
||||
** If no property with this key is set (or the key is NULL) or the boolean
|
||||
** conversion fails, the default value is returned.
|
||||
**/
|
||||
int8_t property_get_bool(const char* key, int8_t default_value);
|
||||
|
||||
/* property_get_int64: returns the value of key truncated and coerced into a
|
||||
** int64_t. If the property is not set, then the default value is used.
|
||||
**
|
||||
** The numeric conversion is identical to strtoimax with the base inferred:
|
||||
** - All digits up to the first non-digit characters are read
|
||||
** - The longest consecutive prefix of digits is converted to a long
|
||||
**
|
||||
** Valid strings of digits are:
|
||||
** - An optional sign character + or -
|
||||
** - An optional prefix indicating the base (otherwise base 10 is assumed)
|
||||
** -- 0 prefix is octal
|
||||
** -- 0x / 0X prefix is hex
|
||||
**
|
||||
** Leading/trailing whitespace is ignored. Overflow/underflow will cause
|
||||
** numeric conversion to fail.
|
||||
**
|
||||
** If no property with this key is set (or the key is NULL) or the numeric
|
||||
** conversion fails, the default value is returned.
|
||||
**/
|
||||
int64_t property_get_int64(const char* key, int64_t default_value);
|
||||
|
||||
/* property_get_int32: returns the value of key truncated and coerced into an
|
||||
** int32_t. If the property is not set, then the default value is used.
|
||||
**
|
||||
** The numeric conversion is identical to strtoimax with the base inferred:
|
||||
** - All digits up to the first non-digit characters are read
|
||||
** - The longest consecutive prefix of digits is converted to a long
|
||||
**
|
||||
** Valid strings of digits are:
|
||||
** - An optional sign character + or -
|
||||
** - An optional prefix indicating the base (otherwise base 10 is assumed)
|
||||
** -- 0 prefix is octal
|
||||
** -- 0x / 0X prefix is hex
|
||||
**
|
||||
** Leading/trailing whitespace is ignored. Overflow/underflow will cause
|
||||
** numeric conversion to fail.
|
||||
**
|
||||
** If no property with this key is set (or the key is NULL) or the numeric
|
||||
** conversion fails, the default value is returned.
|
||||
**/
|
||||
int32_t property_get_int32(const char* key, int32_t default_value);
|
||||
|
||||
/* property_set: returns 0 on success, < 0 on failure
|
||||
*/
|
||||
int property_set(const char* key, const char* value);
|
||||
|
||||
int property_list(void (*propfn)(const char* key, const char* value, void* cookie), void* cookie);
|
||||
|
||||
#if defined(__BIONIC_FORTIFY)
|
||||
#define __property_get_err_str "property_get() called with too small of a buffer"
|
||||
|
||||
#if defined(__clang__)
|
||||
|
||||
/* Some projects use -Weverything; diagnose_if is clang-specific. */
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgcc-compat"
|
||||
int property_get(const char* key, char* value, const char* default_value)
|
||||
__clang_error_if(__bos(value) != __BIONIC_FORTIFY_UNKNOWN_SIZE && __bos(value) < PROPERTY_VALUE_MAX,
|
||||
__property_get_err_str);
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#else /* defined(__clang__) */
|
||||
|
||||
extern int __property_get_real(const char*, char*, const char*) __asm__(__USER_LABEL_PREFIX__ "property_get");
|
||||
__errordecl(__property_get_too_small_error, __property_get_err_str);
|
||||
|
||||
__BIONIC_FORTIFY_INLINE
|
||||
int property_get(const char* key, char* value, const char* default_value)
|
||||
{
|
||||
size_t bos = __bos(value);
|
||||
if (bos < PROPERTY_VALUE_MAX) {
|
||||
__property_get_too_small_error();
|
||||
}
|
||||
return __property_get_real(key, value, default_value);
|
||||
}
|
||||
|
||||
#endif /* defined(__clang__) */
|
||||
|
||||
#undef __property_get_err_str
|
||||
#endif /* defined(__BIONIC_FORTIFY) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,300 @@
|
|||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBS_CUTILS_TRACE_H
|
||||
#define _LIBS_CUTILS_TRACE_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
/**
|
||||
* Define ATRACE_TAG before including this header to filter tracing by one of the tags below.
|
||||
* Use ATRACE_TAG_ALWAYS for always-on tracing (debug only, as it incurs performance cost).
|
||||
* ATRACE_TAG_NEVER or undefined disables tracing.
|
||||
*
|
||||
* For hardware modules, combine ATRACE_TAG_HAL with relevant tags, e.g., for camera:
|
||||
* #define ATRACE_TAG (ATRACE_TAG_CAMERA | ATRACE_TAG_HAL)
|
||||
*
|
||||
* Sync with frameworks/base/core/java/android/os/Trace.java.
|
||||
*/
|
||||
|
||||
#define ATRACE_TAG_NEVER 0
|
||||
#define ATRACE_TAG_ALWAYS (1<<0)
|
||||
#define ATRACE_TAG_GRAPHICS (1<<1)
|
||||
#define ATRACE_TAG_INPUT (1<<2)
|
||||
#define ATRACE_TAG_VIEW (1<<3)
|
||||
#define ATRACE_TAG_WEBVIEW (1<<4)
|
||||
#define ATRACE_TAG_WINDOW_MANAGER (1<<5)
|
||||
#define ATRACE_TAG_ACTIVITY_MANAGER (1<<6)
|
||||
#define ATRACE_TAG_SYNC_MANAGER (1<<7)
|
||||
#define ATRACE_TAG_AUDIO (1<<8)
|
||||
#define ATRACE_TAG_VIDEO (1<<9)
|
||||
#define ATRACE_TAG_CAMERA (1<<10)
|
||||
#define ATRACE_TAG_HAL (1<<11)
|
||||
#define ATRACE_TAG_APP (1<<12)
|
||||
#define ATRACE_TAG_RESOURCES (1<<13)
|
||||
#define ATRACE_TAG_DALVIK (1<<14)
|
||||
#define ATRACE_TAG_RS (1<<15)
|
||||
#define ATRACE_TAG_BIONIC (1<<16)
|
||||
#define ATRACE_TAG_POWER (1<<17)
|
||||
#define ATRACE_TAG_PACKAGE_MANAGER (1<<18)
|
||||
#define ATRACE_TAG_SYSTEM_SERVER (1<<19)
|
||||
#define ATRACE_TAG_DATABASE (1<<20)
|
||||
#define ATRACE_TAG_NETWORK (1<<21)
|
||||
#define ATRACE_TAG_ADB (1<<22)
|
||||
#define ATRACE_TAG_VIBRATOR (1<<23)
|
||||
#define ATRACE_TAG_AIDL (1<<24)
|
||||
#define ATRACE_TAG_NNAPI (1<<25)
|
||||
#define ATRACE_TAG_RRO (1<<26)
|
||||
#define ATRACE_TAG_THERMAL (1 << 27)
|
||||
#define ATRACE_TAG_LAST ATRACE_TAG_THERMAL
|
||||
|
||||
/* Reserved for initialization. */
|
||||
|
||||
#define ATRACE_TAG_NOT_READY (1ULL<<63)
|
||||
|
||||
#define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST)
|
||||
|
||||
#ifndef ATRACE_TAG
|
||||
#define ATRACE_TAG ATRACE_TAG_NEVER
|
||||
#elif ATRACE_TAG_VALID_MASK < ATRACE_TAG
|
||||
#error ATRACE_TAG must be defined to be one of the tags defined in cutils/trace.h
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Opens trace file and reads initial tags from the system property.
|
||||
* This is automatically called when the first trace function is used.
|
||||
*/
|
||||
void atrace_setup(void);
|
||||
|
||||
/**
|
||||
* Updates `atrace_enabled_tags` from the system property `debug.atrace.tags.enableflags`.
|
||||
*/
|
||||
void atrace_update_tags(void);
|
||||
|
||||
/**
|
||||
* Enables or disables tracing for the current process (to prevent tracing in Zygote).
|
||||
*/
|
||||
void atrace_set_tracing_enabled(bool enabled);
|
||||
|
||||
/**
|
||||
* Set of enabled trace tags, initialized to ATRACE_TAG_NOT_READY. Zero indicates failure.
|
||||
*/
|
||||
extern uint64_t atrace_enabled_tags;
|
||||
|
||||
/**
|
||||
* Kernel trace buffer handle, initialized to -1. A valid fd indicates setup success.
|
||||
*/
|
||||
extern int atrace_marker_fd;
|
||||
|
||||
/**
|
||||
* Initializes tracing by opening the trace_marker file (optional, runs automatically).
|
||||
*/
|
||||
#define ATRACE_INIT() atrace_init()
|
||||
#define ATRACE_GET_ENABLED_TAGS() atrace_get_enabled_tags()
|
||||
|
||||
void atrace_init(void);
|
||||
uint64_t atrace_get_enabled_tags(void);
|
||||
|
||||
/**
|
||||
* Checks if a given trace tag is enabled, useful for expensive trace calculations.
|
||||
* Returns nonzero if enabled, zero otherwise.
|
||||
*/
|
||||
#define ATRACE_ENABLED() atrace_is_tag_enabled(ATRACE_TAG)
|
||||
static inline uint64_t atrace_is_tag_enabled(uint64_t tag)
|
||||
{
|
||||
return atrace_get_enabled_tags() & tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a tracing context, typically used for function timing.
|
||||
* @param name: Context name.
|
||||
*/
|
||||
#define ATRACE_BEGIN(name) atrace_begin(ATRACE_TAG, name)
|
||||
void atrace_begin_body(const char*);
|
||||
static inline void atrace_begin(uint64_t tag, const char* name)
|
||||
{
|
||||
if (atrace_is_tag_enabled(tag)) {
|
||||
atrace_begin_body(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends a tracing context that was started by ATRACE_BEGIN.
|
||||
*/
|
||||
#define ATRACE_END() atrace_end(ATRACE_TAG)
|
||||
void atrace_end_body(void);
|
||||
static inline void atrace_end(uint64_t tag)
|
||||
{
|
||||
if (atrace_is_tag_enabled(tag)) {
|
||||
atrace_end_body();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an asynchronous tracing event. Unlike ATRACE_BEGIN/END, async events don't require nesting.
|
||||
* @param name: Event name
|
||||
* @param cookie: Unique identifier for the event.
|
||||
*/
|
||||
#define ATRACE_ASYNC_BEGIN(name, cookie) \
|
||||
atrace_async_begin(ATRACE_TAG, name, cookie)
|
||||
void atrace_async_begin_body(const char*, int32_t);
|
||||
static inline void atrace_async_begin(uint64_t tag, const char* name,
|
||||
int32_t cookie)
|
||||
{
|
||||
if (atrace_is_tag_enabled(tag)) {
|
||||
atrace_async_begin_body(name, cookie);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends an asynchronous tracing event, matching a previous ATRACE_ASYNC_BEGIN.
|
||||
* @param name: Event name
|
||||
* @param cookie: Unique identifier for the event.
|
||||
*/
|
||||
#define ATRACE_ASYNC_END(name, cookie) atrace_async_end(ATRACE_TAG, name, cookie)
|
||||
void atrace_async_end_body(const char*, int32_t);
|
||||
static inline void atrace_async_end(uint64_t tag, const char* name, int32_t cookie)
|
||||
{
|
||||
if (atrace_is_tag_enabled(tag)) {
|
||||
atrace_async_end_body(name, cookie);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an asynchronous event with a trace track.
|
||||
* @param track_name: Track name where this async event is recorded.
|
||||
* @param name: Event name.
|
||||
* @param cookie: Unique event identifier.
|
||||
*/
|
||||
|
||||
#define ATRACE_ASYNC_FOR_TRACK_BEGIN(track_name, name, cookie) \
|
||||
atrace_async_for_track_begin(ATRACE_TAG, track_name, name, cookie)
|
||||
void atrace_async_for_track_begin_body(const char*, const char*, int32_t);
|
||||
static inline void atrace_async_for_track_begin(uint64_t tag, const char* track_name,
|
||||
const char* name, int32_t cookie) {
|
||||
if (atrace_is_tag_enabled(tag)) {
|
||||
atrace_async_for_track_begin_body(track_name, name, cookie);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief To indicate the beginning of async tracing action with trace info
|
||||
*
|
||||
* Trace the end of an asynchronous event.
|
||||
* This should correspond to a previous ATRACE_ASYNC_FOR_TRACK_BEGIN.
|
||||
* @param[in] trace_name the track name is the name of the row where this
|
||||
* async event should be recorded. The track name,
|
||||
* name, and cookie used to begin an event must be
|
||||
* used to end it.
|
||||
* @param[in] name the name to describes the event
|
||||
* @param[in] cookie using to provides a unique identifier for distinguishing
|
||||
* simultaneous events. The name and cookie used to begin an
|
||||
* event must be used to end it.
|
||||
*/
|
||||
#define ATRACE_ASYNC_FOR_TRACK_END(track_name, name, cookie) \
|
||||
atrace_async_for_track_end(ATRACE_TAG, track_name, name, cookie)
|
||||
void atrace_async_for_track_end_body(const char*, const char*, int32_t);
|
||||
static inline void atrace_async_for_track_end(uint64_t tag, const char* track_name,
|
||||
const char* name, int32_t cookie) {
|
||||
if (atrace_is_tag_enabled(tag)) {
|
||||
atrace_async_for_track_end_body(track_name, name, cookie);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief To indicate trace an instantaneous context
|
||||
* An "instant" is an event with no defined duration. Visually is displayed like a single marker
|
||||
* in the timeline (rather than a span, in the case of begin/end events).
|
||||
*
|
||||
* By default, instant events are added into a dedicated track that has the same name of the event.
|
||||
* Use atrace_instant_for_track to put different instant events into the same timeline track/row.
|
||||
*
|
||||
* @param[in] name the value that using to identify the context
|
||||
*/
|
||||
#define ATRACE_INSTANT(name) atrace_instant(ATRACE_TAG, name)
|
||||
void atrace_instant_body(const char*);
|
||||
static inline void atrace_instant(uint64_t tag, const char* name) {
|
||||
if (atrace_is_tag_enabled(tag)) {
|
||||
atrace_instant_body(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief To indicate trace an instantaneous context with track specified
|
||||
*
|
||||
* An "instant" is an event with no defined duration. Visually is displayed like a single marker
|
||||
* in the timeline (rather than a span, in the case of begin/end events).
|
||||
*
|
||||
* @param[in] name the name used to identify the context.
|
||||
* @param[in] track_name is the name of the row where the event should be recorded.
|
||||
*/
|
||||
#define ATRACE_INSTANT_FOR_TRACK(trackName, name) \
|
||||
atrace_instant_for_track(ATRACE_TAG, trackName, name)
|
||||
void atrace_instant_for_track_body(const char*, const char*);
|
||||
static inline void atrace_instant_for_track(uint64_t tag, const char* track_name,
|
||||
const char* name) {
|
||||
if (atrace_is_tag_enabled(tag)) {
|
||||
atrace_instant_for_track_body(track_name, name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief To indicate trace an integer counter value
|
||||
*
|
||||
* @param[in] name is used to identify the counter.
|
||||
* @param[in] velue this can be used to track how a value changes over time.
|
||||
*/
|
||||
#define ATRACE_INT(name, value) atrace_int(ATRACE_TAG, name, value)
|
||||
void atrace_int_body(const char*, int32_t);
|
||||
static inline void atrace_int(uint64_t tag, const char* name, int32_t value)
|
||||
{
|
||||
if (atrace_is_tag_enabled(tag)) {
|
||||
atrace_int_body(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief To indicate trace an 64-bit integer counter value
|
||||
*
|
||||
* @param[in] name is used to identify the counter.
|
||||
* @param[in] value This can be used to track how a value changes over time.
|
||||
*/
|
||||
#define ATRACE_INT64(name, value) atrace_int64(ATRACE_TAG, name, value)
|
||||
void atrace_int64_body(const char*, int64_t);
|
||||
static inline void atrace_int64(uint64_t tag, const char* name, int64_t value)
|
||||
{
|
||||
if (atrace_is_tag_enabled(tag)) {
|
||||
atrace_int64_body(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
/* clang-format on */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _LIBS_CUTILS_TRACE_H
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* Copyright (C) 2020 Xiaomi Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __KVDB_H
|
||||
#define __KVDB_H
|
||||
|
||||
#include <cutils/properties.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define PROP_MSG_MAX (3 + PROP_NAME_MAX + PROP_VALUE_MAX) /* +3 = +1(opcode) +2(len) */
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Store Key-Values to database.
|
||||
* @param[in] key entry key string
|
||||
* @param[in] value entry value string
|
||||
* @note Key starting with "persist." will be stored permanently after commit,
|
||||
* others will be lost after reboot.
|
||||
* @return On success returns 0, -errno otherwise.
|
||||
*/
|
||||
int property_set_oneway(const char* key, const char* value);
|
||||
|
||||
/**
|
||||
* @brief Delete a KV pair by key.
|
||||
* @param[in] key entry key string
|
||||
* @return On success returns 0, -errno otherwise.
|
||||
*/
|
||||
int property_delete(const char* key);
|
||||
|
||||
/**
|
||||
* @brief Actively commit all property changes.
|
||||
* @return On success returns 0, -errno otherwise.
|
||||
*/
|
||||
int property_commit(void);
|
||||
|
||||
/**
|
||||
* @brief load default property value.
|
||||
* @return On success returns 0, -errno otherwise.
|
||||
*/
|
||||
int property_load(const char* path);
|
||||
|
||||
/**
|
||||
* @brief Exit the kvdb service.
|
||||
* @return On success returns 0, -errno otherwise.
|
||||
*/
|
||||
int property_exit(void);
|
||||
|
||||
/**
|
||||
* @brief Wait the monitored key until its value updated or key deleted
|
||||
* @param[in] key the monitored key string, support fnmatch pattern
|
||||
* @param[in] the length of the newvalue
|
||||
* @param[in] timeout the wait timeout time (in milliseconds)
|
||||
* @param[out] newkey pointer to a string buffer to receive the key of
|
||||
* the updated/deleted value
|
||||
* @param[out] newvalue pointer to a string buffer to receive the updated
|
||||
* value or deleted value
|
||||
* @return On success returns the length of the value.
|
||||
*/
|
||||
ssize_t property_wait(const char* key, char* newkey, void* newvalue, size_t val_len, int timeout);
|
||||
|
||||
/**
|
||||
* @brief Open a key monitor channel
|
||||
* @param[in] key the monitored key string, support fnmatch pattern
|
||||
* @return On success returns a file descriptor, -errno otherwise.
|
||||
*/
|
||||
int property_monitor_open(const char* key);
|
||||
|
||||
/**
|
||||
* @brief Read the monitored key and value
|
||||
* @param[in] fd file descriptor returned by property_monitor_open()
|
||||
* @param[in] the length of the newvalue
|
||||
* @param[out] newkey pointer to a string buffer to receive the key of
|
||||
* the updated/deleted value
|
||||
* @param[out] newvalue pointer to a string buffer to receive the updated
|
||||
* value or deleted value
|
||||
* @return On success returns the length of the value.
|
||||
*/
|
||||
ssize_t property_monitor_read(int fd, char* newkey, void* newvalue, size_t val_len);
|
||||
|
||||
/**
|
||||
* @brief Close a key monitor channel
|
||||
* @param[in] fd file descriptor returned by property_monitor_open()
|
||||
* @return On success returns 0, -errno otherwise.
|
||||
*/
|
||||
int property_monitor_close(int fd);
|
||||
|
||||
/**
|
||||
* @brief Saves a boolean to database.
|
||||
* @param[in] key entry key string
|
||||
* @param[in] value entry boolean value
|
||||
* @return On success returns 0, -errno otherwise.
|
||||
*/
|
||||
int property_set_bool(const char* key, int8_t value);
|
||||
int property_set_bool_oneway(const char* key, int8_t value);
|
||||
|
||||
/**
|
||||
* @brief Saves an 32-bit integer to database.
|
||||
* @param[in] key entry key string
|
||||
* @param[in] value entry value
|
||||
* @return On success returns 0, -errno otherwise.
|
||||
*/
|
||||
int property_set_int32(const char* key, int32_t value);
|
||||
int property_set_int32_oneway(const char* key, int32_t value);
|
||||
|
||||
/**
|
||||
* @brief Saves an 64-bit integer to database.
|
||||
* @param[in] key entry key string
|
||||
* @param[in] value entry value
|
||||
* @return On success returns 0, -errno otherwise.
|
||||
*/
|
||||
int property_set_int64(const char* key, int64_t value);
|
||||
int property_set_int64_oneway(const char* key, int64_t value);
|
||||
|
||||
/**
|
||||
* @brief Saves a binary buffer to database.
|
||||
* @param[in] key entry key string
|
||||
* @param[in] value buffer value
|
||||
* @param[in] size entry size
|
||||
* @return On success returns 0, -errno otherwise.
|
||||
*/
|
||||
int property_set_buffer(const char* key, const void* value, size_t size);
|
||||
int property_set_buffer_oneway(const char* key, const void* value, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Retrieve a Key-Value from database and interpret as binary buffer.
|
||||
* @param[in] key entry key string
|
||||
* @param[in] value buffer value
|
||||
* @param[in] size buffer size
|
||||
* @return On success returns the array length, -errno otherwise.
|
||||
*/
|
||||
ssize_t property_get_buffer(const char* key, void* value, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Saves a the specified length buffer to database.
|
||||
* @param[in] key entry key string
|
||||
* @param[in] value buffer value
|
||||
* @param[in] value buffer size
|
||||
* @return On success returns the array length, -errno otherwise.
|
||||
*/
|
||||
int property_set_binary(const char* key, const void* value, size_t val_len, bool oneway);
|
||||
ssize_t property_get_binary(const char* key, void* value, size_t val_len);
|
||||
|
||||
/**
|
||||
* @List all KVs in every database and calls callback function.
|
||||
* @param[in] callback function
|
||||
* @param[in] cookie data to pass to callback function
|
||||
* @Returns 0 on success, <0 if all databases failed to open.
|
||||
*/
|
||||
int property_list_binary(void (*propfn)(const char* key, const void* value, size_t val_len, void* cookie), void* cookie);
|
||||
|
||||
/**
|
||||
* @Get property with an error param
|
||||
* @param[in] key entry key string
|
||||
* @param[out] value entry
|
||||
* @Retruns >0 length of the value, <0 if failed.
|
||||
*/
|
||||
int property_get_with_err(const char* key, char* value);
|
||||
|
||||
/**
|
||||
* @Get bool property with an error param
|
||||
* @param[in] key entry key string
|
||||
* @param[out] value entry
|
||||
* @Retruns =0 if success !=0 if failed.
|
||||
*/
|
||||
int property_get_bool_with_err(const char* key, int8_t* value);
|
||||
|
||||
/**
|
||||
* @Get int32 property with an error param
|
||||
* @param[in] key entry key string
|
||||
* @param[out] value entry
|
||||
* @Retruns =0 if success !=0 if failed.
|
||||
*/
|
||||
int property_get_int32_with_err(const char* key, int32_t* value);
|
||||
|
||||
/**
|
||||
* @Get int64 property with an error param
|
||||
* @param[in] key entry key string
|
||||
* @param[out] value entry
|
||||
* @Retruns =0 if success !=0 if failed.
|
||||
*/
|
||||
int property_get_int64_with_err(const char* key, int64_t* value);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define EVENT_TAG_MAP_FILE "/system/etc/event-log-tags"
|
||||
|
||||
struct EventTagMap;
|
||||
typedef struct EventTagMap EventTagMap;
|
||||
|
||||
/*
|
||||
* Open the specified file as an event log tag map.
|
||||
*
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
EventTagMap* android_openEventTagMap(const char* fileName);
|
||||
|
||||
/*
|
||||
* Close the map.
|
||||
*/
|
||||
void android_closeEventTagMap(EventTagMap* map);
|
||||
|
||||
/*
|
||||
* Look up a tag by index. Returns the tag string & string length, or NULL if
|
||||
* not found. Returned string is not guaranteed to be nul terminated.
|
||||
*/
|
||||
const char* android_lookupEventTag_len(const EventTagMap* map, size_t* len,
|
||||
unsigned int tag);
|
||||
|
||||
/*
|
||||
* Look up a format by index. Returns the format string & string length,
|
||||
* or NULL if not found. Returned string is not guaranteed to be nul terminated.
|
||||
*/
|
||||
const char* android_lookupEventFormat_len(const EventTagMap* map, size_t* len,
|
||||
unsigned int tag);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Too many in the ecosystem assume these are included */
|
||||
#if !defined(_WIN32)
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#include <stdint.h> /* uint16_t, int32_t */
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android/log.h>
|
||||
#include <log/log_id.h>
|
||||
#include <log/log_main.h>
|
||||
#include <log/log_radio.h>
|
||||
#include <log/log_safetynet.h>
|
||||
#include <log/log_system.h>
|
||||
#include <log/log_time.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* LOG_TAG is the local tag used for the following simplified
|
||||
* logging macros. You can change this preprocessor definition
|
||||
* before using the other macros to change the tag.
|
||||
*/
|
||||
|
||||
#ifndef LOG_TAG
|
||||
#define LOG_TAG NULL
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Normally we strip the effects of ALOGV (VERBOSE messages),
|
||||
* LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
|
||||
* release builds be defining NDEBUG. You can modify this (for
|
||||
* example with "#define LOG_NDEBUG 0" at the top of your source
|
||||
* file) to change that behavior.
|
||||
*/
|
||||
|
||||
#ifndef LOG_NDEBUG
|
||||
#ifdef NDEBUG
|
||||
#define LOG_NDEBUG 1
|
||||
#else
|
||||
#define LOG_NDEBUG 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The maximum size of the log entry payload that can be
|
||||
* written to the logger. An attempt to write more than
|
||||
* this amount will result in a truncated log entry.
|
||||
*/
|
||||
#define LOGGER_ENTRY_MAX_PAYLOAD 4068
|
||||
|
||||
/*
|
||||
* Event logging.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The following should not be used directly.
|
||||
*/
|
||||
|
||||
int __android_log_bwrite(int32_t tag, const void* payload, size_t len);
|
||||
int __android_log_btwrite(int32_t tag, char type, const void* payload,
|
||||
size_t len);
|
||||
int __android_log_bswrite(int32_t tag, const char* payload);
|
||||
|
||||
int __android_log_stats_bwrite(int32_t tag, const void* payload, size_t len);
|
||||
|
||||
#define android_bWriteLog(tag, payload, len) \
|
||||
__android_log_bwrite(tag, payload, len)
|
||||
#define android_btWriteLog(tag, type, payload, len) \
|
||||
__android_log_btwrite(tag, type, payload, len)
|
||||
|
||||
/*
|
||||
* Event log entry types.
|
||||
*/
|
||||
typedef enum {
|
||||
/* Special markers for android_log_list_element type */
|
||||
EVENT_TYPE_LIST_STOP = '\n', /* declare end of list */
|
||||
EVENT_TYPE_UNKNOWN = '?', /* protocol error */
|
||||
|
||||
/* must match with declaration in java/android/android/util/EventLog.java */
|
||||
EVENT_TYPE_INT = 0, /* int32_t */
|
||||
EVENT_TYPE_LONG = 1, /* int64_t */
|
||||
EVENT_TYPE_STRING = 2,
|
||||
EVENT_TYPE_LIST = 3,
|
||||
EVENT_TYPE_FLOAT = 4,
|
||||
} AndroidEventLogType;
|
||||
|
||||
#ifndef LOG_EVENT_INT
|
||||
#define LOG_EVENT_INT(_tag, _value) \
|
||||
{ \
|
||||
int intBuf = _value; \
|
||||
(void)android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, sizeof(intBuf)); \
|
||||
}
|
||||
#endif
|
||||
#ifndef LOG_EVENT_LONG
|
||||
#define LOG_EVENT_LONG(_tag, _value) \
|
||||
{ \
|
||||
long long longBuf = _value; \
|
||||
(void)android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, sizeof(longBuf)); \
|
||||
}
|
||||
#endif
|
||||
#ifndef LOG_EVENT_FLOAT
|
||||
#define LOG_EVENT_FLOAT(_tag, _value) \
|
||||
{ \
|
||||
float floatBuf = _value; \
|
||||
(void)android_btWriteLog(_tag, EVENT_TYPE_FLOAT, &floatBuf, \
|
||||
sizeof(floatBuf)); \
|
||||
}
|
||||
#endif
|
||||
#ifndef LOG_EVENT_STRING
|
||||
#define LOG_EVENT_STRING(_tag, _value) \
|
||||
(void)__android_log_bswrite(_tag, _value);
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Release any logger resources (a new log write will immediately re-acquire)
|
||||
*
|
||||
* This is specifically meant to be used by Zygote to close open file descriptors after fork()
|
||||
* and before specialization. O_CLOEXEC is used on file descriptors, so they will be closed upon
|
||||
* exec() in normal use cases.
|
||||
*
|
||||
* Note that this is not safe to call from a multi-threaded program.
|
||||
*/
|
||||
void __android_log_close(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* For manipulating lists of events. */
|
||||
|
||||
#define ANDROID_MAX_LIST_NEST_DEPTH 8
|
||||
|
||||
/*
|
||||
* The opaque context used to manipulate lists of events.
|
||||
*/
|
||||
typedef struct android_log_context_internal* android_log_context;
|
||||
|
||||
/*
|
||||
* Elements returned when reading a list of events.
|
||||
*/
|
||||
typedef struct {
|
||||
AndroidEventLogType type;
|
||||
uint16_t complete;
|
||||
uint16_t len;
|
||||
union {
|
||||
int32_t int32;
|
||||
int64_t int64;
|
||||
char* string;
|
||||
float float32;
|
||||
} data;
|
||||
} android_log_list_element;
|
||||
|
||||
/*
|
||||
* Creates a context associated with an event tag to write elements to
|
||||
* the list of events.
|
||||
*/
|
||||
android_log_context create_android_logger(uint32_t tag);
|
||||
|
||||
/* All lists must be braced by a begin and end call */
|
||||
/*
|
||||
* NB: If the first level braces are missing when specifying multiple
|
||||
* elements, we will manufacturer a list to embrace it for your API
|
||||
* convenience. For a single element, it will remain solitary.
|
||||
*/
|
||||
int android_log_write_list_begin(android_log_context ctx);
|
||||
int android_log_write_list_end(android_log_context ctx);
|
||||
|
||||
int android_log_write_int32(android_log_context ctx, int32_t value);
|
||||
int android_log_write_int64(android_log_context ctx, int64_t value);
|
||||
int android_log_write_string8(android_log_context ctx, const char* value);
|
||||
int android_log_write_string8_len(android_log_context ctx, const char* value,
|
||||
size_t maxlen);
|
||||
int android_log_write_float32(android_log_context ctx, float value);
|
||||
|
||||
/* Submit the composed list context to the specified logger id */
|
||||
/* NB: LOG_ID_EVENTS and LOG_ID_SECURITY only valid binary buffers */
|
||||
int android_log_write_list(android_log_context ctx, log_id_t id);
|
||||
|
||||
/*
|
||||
* Creates a context from a raw buffer representing a list of events to be read.
|
||||
*/
|
||||
android_log_context create_android_log_parser(const char* msg, size_t len);
|
||||
|
||||
android_log_list_element android_log_read_next(android_log_context ctx);
|
||||
android_log_list_element android_log_peek_next(android_log_context ctx);
|
||||
|
||||
/* Reset writer context */
|
||||
int android_log_reset(android_log_context ctx);
|
||||
|
||||
/* Reset reader context */
|
||||
int android_log_parser_reset(android_log_context ctx,
|
||||
const char* msg, size_t len);
|
||||
|
||||
/* Finished with reader or writer context */
|
||||
int android_log_destroy(android_log_context* ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
/* android_log_list C++ helpers */
|
||||
extern "C++" {
|
||||
class android_log_event_list {
|
||||
private:
|
||||
android_log_context ctx;
|
||||
int ret;
|
||||
|
||||
android_log_event_list(const android_log_event_list&) = delete;
|
||||
void operator=(const android_log_event_list&) = delete;
|
||||
|
||||
public:
|
||||
explicit android_log_event_list(int tag) : ret(0) {
|
||||
ctx = create_android_logger(static_cast<uint32_t>(tag));
|
||||
}
|
||||
~android_log_event_list() {
|
||||
android_log_destroy(&ctx);
|
||||
}
|
||||
|
||||
int close() {
|
||||
int retval = android_log_destroy(&ctx);
|
||||
if (retval < 0) ret = retval;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* To allow above C calls to use this class as parameter */
|
||||
operator android_log_context() const {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/* return errors or transmit status */
|
||||
int status() const {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int begin() {
|
||||
int retval = android_log_write_list_begin(ctx);
|
||||
if (retval < 0) ret = retval;
|
||||
return ret;
|
||||
}
|
||||
int end() {
|
||||
int retval = android_log_write_list_end(ctx);
|
||||
if (retval < 0) ret = retval;
|
||||
return ret;
|
||||
}
|
||||
|
||||
android_log_event_list& operator<<(int32_t value) {
|
||||
int retval = android_log_write_int32(ctx, value);
|
||||
if (retval < 0) ret = retval;
|
||||
return *this;
|
||||
}
|
||||
|
||||
android_log_event_list& operator<<(uint32_t value) {
|
||||
int retval = android_log_write_int32(ctx, static_cast<int32_t>(value));
|
||||
if (retval < 0) ret = retval;
|
||||
return *this;
|
||||
}
|
||||
|
||||
android_log_event_list& operator<<(bool value) {
|
||||
int retval = android_log_write_int32(ctx, value ? 1 : 0);
|
||||
if (retval < 0) ret = retval;
|
||||
return *this;
|
||||
}
|
||||
|
||||
android_log_event_list& operator<<(int64_t value) {
|
||||
int retval = android_log_write_int64(ctx, value);
|
||||
if (retval < 0) ret = retval;
|
||||
return *this;
|
||||
}
|
||||
|
||||
android_log_event_list& operator<<(uint64_t value) {
|
||||
int retval = android_log_write_int64(ctx, static_cast<int64_t>(value));
|
||||
if (retval < 0) ret = retval;
|
||||
return *this;
|
||||
}
|
||||
|
||||
android_log_event_list& operator<<(const char* value) {
|
||||
int retval = android_log_write_string8(ctx, value);
|
||||
if (retval < 0) ret = retval;
|
||||
return *this;
|
||||
}
|
||||
|
||||
android_log_event_list& operator<<(const std::string& value) {
|
||||
int retval =
|
||||
android_log_write_string8_len(ctx, value.data(), value.length());
|
||||
if (retval < 0) ret = retval;
|
||||
return *this;
|
||||
}
|
||||
|
||||
android_log_event_list& operator<<(float value) {
|
||||
int retval = android_log_write_float32(ctx, value);
|
||||
if (retval < 0) ret = retval;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int write(log_id_t id = LOG_ID_EVENTS) {
|
||||
/* facilitate -EBUSY retry */
|
||||
if ((ret == -EBUSY) || (ret > 0)) ret = 0;
|
||||
int retval = android_log_write_list(ctx, id);
|
||||
/* existing errors trump transmission errors */
|
||||
if (!ret) ret = retval;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int operator<<(log_id_t id) {
|
||||
write(id);
|
||||
android_log_destroy(&ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Append<Type> methods removes any integer promotion
|
||||
* confusion, and adds access to string with length.
|
||||
* Append methods are also added for all types for
|
||||
* convenience.
|
||||
*/
|
||||
|
||||
bool AppendInt(int32_t value) {
|
||||
int retval = android_log_write_int32(ctx, value);
|
||||
if (retval < 0) ret = retval;
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
bool AppendLong(int64_t value) {
|
||||
int retval = android_log_write_int64(ctx, value);
|
||||
if (retval < 0) ret = retval;
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
bool AppendString(const char* value) {
|
||||
int retval = android_log_write_string8(ctx, value);
|
||||
if (retval < 0) ret = retval;
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
bool AppendString(const char* value, size_t len) {
|
||||
int retval = android_log_write_string8_len(ctx, value, len);
|
||||
if (retval < 0) ret = retval;
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
bool AppendString(const std::string& value) {
|
||||
int retval =
|
||||
android_log_write_string8_len(ctx, value.data(), value.length());
|
||||
if (retval < 0) ret = retval;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Append(const std::string& value) {
|
||||
int retval =
|
||||
android_log_write_string8_len(ctx, value.data(), value.length());
|
||||
if (retval < 0) ret = retval;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool AppendFloat(float value) {
|
||||
int retval = android_log_write_float32(ctx, value);
|
||||
if (retval < 0) ret = retval;
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
template <typename Tvalue>
|
||||
bool Append(Tvalue value) {
|
||||
*this << value;
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
bool Append(const char* value, size_t len) {
|
||||
int retval = android_log_write_string8_len(ctx, value, len);
|
||||
if (retval < 0) ret = retval;
|
||||
return ret >= 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* log_id_t helpers
|
||||
*/
|
||||
log_id_t android_name_to_log_id(const char* logName);
|
||||
const char* android_log_id_to_name(log_id_t log_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,422 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* clang-format off */
|
||||
/*
|
||||
* Normally we strip the effects of ALOGV (VERBOSE messages),
|
||||
* LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
|
||||
* release builds be defining NDEBUG. You can modify this (for
|
||||
* example with "#define LOG_NDEBUG 0" at the top of your source
|
||||
* file) to change that behavior.
|
||||
*/
|
||||
|
||||
#ifndef LOG_NDEBUG
|
||||
#ifdef NDEBUG
|
||||
#define LOG_NDEBUG 1
|
||||
#else
|
||||
#define LOG_NDEBUG 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_ALOG
|
||||
#define CONFIG_ALOG_LEVEL -1
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* This file uses ", ## __VA_ARGS__" zero-argument token pasting to
|
||||
* work around issues with debug-only syntax errors in assertions
|
||||
* that are missing format strings. See commit
|
||||
* 19299904343daf191267564fe32e6cd5c165cd42
|
||||
*/
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Use __VA_ARGS__ if running a static analyzer,
|
||||
* to avoid warnings of unused variables in __VA_ARGS__.
|
||||
* Use constexpr function in C++ mode, so these macros can be used
|
||||
* in other constexpr functions without warning.
|
||||
*/
|
||||
#ifdef __clang_analyzer__
|
||||
#ifdef __cplusplus
|
||||
extern "C++" {
|
||||
template <typename... Ts>
|
||||
constexpr int __fake_use_va_args(Ts...)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
extern int __fake_use_va_args(int, ...);
|
||||
#endif /* __cplusplus */
|
||||
#define __FAKE_USE_VA_ARGS(...) ((void)__fake_use_va_args(0, ##__VA_ARGS__))
|
||||
#else
|
||||
#define __FAKE_USE_VA_ARGS(...) ((void)(0))
|
||||
#endif /* __clang_analyzer__ */
|
||||
|
||||
#ifndef __predict_false
|
||||
#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
|
||||
#endif
|
||||
|
||||
#define android_writeLog(prio, tag, text) __android_log_write(prio, tag, text)
|
||||
|
||||
#define android_printLog(prio, tag, ...) \
|
||||
__android_log_print(prio, tag, __VA_ARGS__)
|
||||
|
||||
#define android_vprintLog(prio, cond, tag, ...) \
|
||||
__android_log_vprint(prio, tag, __VA_ARGS__)
|
||||
|
||||
/*
|
||||
* Log macro that allows you to specify a number for the priority.
|
||||
*/
|
||||
#ifndef LOG_PRI
|
||||
#define LOG_PRI(priority, tag, ...) android_printLog(priority, tag, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Log macro that allows you to pass in a varargs ("args" is a va_list).
|
||||
*/
|
||||
#ifndef LOG_PRI_VA
|
||||
#define LOG_PRI_VA(priority, tag, fmt, args) \
|
||||
android_vprintLog(priority, NULL, tag, fmt, args)
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/* XXX Macros to work around syntax errors in places where format string
|
||||
* arg is not passed to ALOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF
|
||||
* (happens only in debug builds).
|
||||
*/
|
||||
|
||||
/* Returns 2nd arg. Used to substitute default value if caller's vararg list
|
||||
* is empty.
|
||||
*/
|
||||
#define __android_second(dummy, second, ...) second
|
||||
|
||||
/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise
|
||||
* returns nothing.
|
||||
*/
|
||||
#define __android_rest(first, ...) , ##__VA_ARGS__
|
||||
|
||||
#define android_printAssert(cond, tag, ...) \
|
||||
__android_log_assert(cond, tag, \
|
||||
__android_second(0, ##__VA_ARGS__, NULL) \
|
||||
__android_rest(__VA_ARGS__))
|
||||
|
||||
/*
|
||||
* Log a fatal error. If the given condition fails, this stops program
|
||||
* execution like a normal assertion, but also generating the given message.
|
||||
* It is NOT stripped from release builds. Note that the condition test
|
||||
* is -inverted- from the normal assert() semantics.
|
||||
*/
|
||||
#ifndef LOG_ALWAYS_FATAL_IF
|
||||
#if !defined(CONFIG_ALOG_LEVEL) || CONFIG_ALOG_LEVEL >= 0
|
||||
#define LOG_ALWAYS_FATAL_IF(cond, ...) \
|
||||
((__predict_false(cond)) ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), \
|
||||
((void)android_printAssert(#cond, LOG_TAG, ##__VA_ARGS__))) \
|
||||
: ((void)0))
|
||||
#else
|
||||
#define LOG_ALWAYS_FATAL_IF(cond, ...) \
|
||||
((__predict_false(cond)) ? (__FAKE_USE_VA_ARGS(__VA_ARGS__)) : ((void)0))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef LOG_ALWAYS_FATAL
|
||||
#if !defined(CONFIG_ALOG_LEVEL) || CONFIG_ALOG_LEVEL >= 0
|
||||
#define LOG_ALWAYS_FATAL(...) \
|
||||
(((void)android_printAssert(NULL, LOG_TAG, ##__VA_ARGS__)))
|
||||
#else
|
||||
#define LOG_ALWAYS_FATAL(...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that
|
||||
* are stripped out of release builds.
|
||||
*/
|
||||
|
||||
#if LOG_NDEBUG && !defined(CONFIG_ALOG_LEVEL) || defined(CONFIG_ALOG_LEVEL) && CONFIG_ALOG_LEVEL < 1
|
||||
|
||||
#ifndef LOG_FATAL_IF
|
||||
#define LOG_FATAL_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
|
||||
#endif
|
||||
#ifndef LOG_FATAL
|
||||
#define LOG_FATAL(...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifndef LOG_FATAL_IF
|
||||
#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ##__VA_ARGS__)
|
||||
#endif
|
||||
#ifndef LOG_FATAL
|
||||
#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Assertion that generates a log message when the assertion fails.
|
||||
* Stripped out of release builds. Uses the current LOG_TAG.
|
||||
*/
|
||||
#ifndef ALOG_ASSERT
|
||||
#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* C/C++ logging functions. See the logging documentation for API details.
|
||||
*
|
||||
* We'd like these to be available from C code (in case we import some from
|
||||
* somewhere), so this has a C interface.
|
||||
*
|
||||
* The output will be correct when the log file is shared between multiple
|
||||
* threads and/or multiple processes so long as the operating system
|
||||
* supports O_APPEND. These calls have mutex-protected data structures
|
||||
* and so are NOT reentrant. Do not use LOG in a signal handler.
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Simplified macro to send a verbose log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef ALOGV
|
||||
#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef ALOGV_IF
|
||||
#define ALOGV_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
|
||||
: ((void)0))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Simplified macro to send a debug log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef ALOGD
|
||||
#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef ALOGD_IF
|
||||
#define ALOGD_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
|
||||
: ((void)0))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Simplified macro to send an info log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef ALOGI
|
||||
#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef ALOGI_IF
|
||||
#define ALOGI_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
|
||||
: ((void)0))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Simplified macro to send a warning log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef ALOGW
|
||||
#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef ALOGW_IF
|
||||
#define ALOGW_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
|
||||
: ((void)0))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Simplified macro to send an error log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef ALOGE
|
||||
#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef ALOGE_IF
|
||||
#define ALOGE_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
|
||||
: ((void)0))
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Conditional based on whether the current LOG_TAG is enabled at
|
||||
* verbose priority.
|
||||
*/
|
||||
#ifndef IF_ALOGV
|
||||
#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Conditional based on whether the current LOG_TAG is enabled at
|
||||
* debug priority.
|
||||
*/
|
||||
#ifndef IF_ALOGD
|
||||
#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Conditional based on whether the current LOG_TAG is enabled at
|
||||
* info priority.
|
||||
*/
|
||||
#ifndef IF_ALOGI
|
||||
#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Conditional based on whether the current LOG_TAG is enabled at
|
||||
* warn priority.
|
||||
*/
|
||||
#ifndef IF_ALOGW
|
||||
#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Conditional based on whether the current LOG_TAG is enabled at
|
||||
* error priority.
|
||||
*/
|
||||
#ifndef IF_ALOGE
|
||||
#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG)
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Basic log message macro.
|
||||
*
|
||||
* Example:
|
||||
* ALOG(LOG_WARN, NULL, "Failed with error %d", errno);
|
||||
*
|
||||
* The second argument may be NULL or "" to indicate the "global" tag.
|
||||
*/
|
||||
#ifndef ALOG
|
||||
#ifndef CONFIG_ALOG_LEVEL
|
||||
#define ALOG(priority, tag, ...) LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
|
||||
#elif CONFIG_ALOG_LEVEL == 2
|
||||
#define ALOG(priority, tag, ...) \
|
||||
((ANDROID_##priority >= ANDROID_LOG_ERROR) ? LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) : 0)
|
||||
#elif CONFIG_ALOG_LEVEL == 3
|
||||
#define ALOG(priority, tag, ...) \
|
||||
((ANDROID_##priority >= ANDROID_LOG_WARN) ? LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) : 0)
|
||||
#elif CONFIG_ALOG_LEVEL == 4
|
||||
#define ALOG(priority, tag, ...) \
|
||||
((ANDROID_##priority >= ANDROID_LOG_INFO) ? LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) : 0)
|
||||
#elif CONFIG_ALOG_LEVEL == 5
|
||||
#define ALOG(priority, tag, ...) \
|
||||
((ANDROID_##priority >= ANDROID_LOG_DEBUG) ? LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) : 0)
|
||||
#elif CONFIG_ALOG_LEVEL == 6
|
||||
#define ALOG(priority, tag, ...) \
|
||||
((ANDROID_##priority >= ANDROID_LOG_VERBOSE) ? LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) : 0)
|
||||
#else
|
||||
#define ALOG(priority, tag, ...) \
|
||||
((ANDROID_##priority >= ANDROID_LOG_FATAL) ? LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) : 0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional given a desired logging priority and tag.
|
||||
*/
|
||||
#ifndef IF_ALOG
|
||||
#define IF_ALOG(priority, tag) if (android_testLog(ANDROID_##priority, tag))
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* IF_ALOG uses android_testLog, but IF_ALOG can be overridden.
|
||||
* android_testLog will remain constant in its purpose as a wrapper
|
||||
* for Android logging filter policy, and can be subject to
|
||||
* change. It can be reused by the developers that override
|
||||
* IF_ALOG as a convenient means to reimplement their policy
|
||||
* over Android.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Use the per-tag properties "log.tag.<tagname>" to generate a runtime
|
||||
* result of non-zero to expose a log. prio is ANDROID_LOG_VERBOSE to
|
||||
* ANDROID_LOG_FATAL. default_prio if no property. Undefined behavior if
|
||||
* any other value.
|
||||
*/
|
||||
int __android_log_is_loggable(int prio, const char* tag, int default_prio);
|
||||
int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio);
|
||||
|
||||
#ifdef CONFIG_ALOG_LEVEL
|
||||
|
||||
#if CONFIG_ALOG_LEVEL == 1
|
||||
#define android_testLog(prio, tag) \
|
||||
((prio >= ANDROID_LOG_FATAL) ? (__android_log_is_loggable_len(prio, tag, (tag) ? strlen(tag) : 0, prio) != 0) : 0)
|
||||
#elif CONFIG_ALOG_LEVEL == 2
|
||||
#define android_testLog(prio, tag) \
|
||||
((prio >= ANDROID_LOG_ERROR) ? (__android_log_is_loggable_len(prio, tag, (tag) ? strlen(tag) : 0, prio) != 0) : 0)
|
||||
#elif CONFIG_ALOG_LEVEL == 3
|
||||
#define android_testLog(prio, tag) \
|
||||
((prio >= ANDROID_LOG_WARN) ? (__android_log_is_loggable_len(prio, tag, (tag) ? strlen(tag) : 0, prio) != 0) : 0)
|
||||
#elif CONFIG_ALOG_LEVEL == 4
|
||||
#define android_testLog(prio, tag) \
|
||||
((prio >= ANDROID_LOG_INFO) ? (__android_log_is_loggable_len(prio, tag, (tag) ? strlen(tag) : 0, prio) != 0) : 0)
|
||||
#elif CONFIG_ALOG_LEVEL == 5
|
||||
#define android_testLog(prio, tag) \
|
||||
((prio >= ANDROID_LOG_DEBUG) ? (__android_log_is_loggable_len(prio, tag, (tag) ? strlen(tag) : 0, prio) != 0) : 0)
|
||||
#elif CONFIG_ALOG_LEVEL == 6
|
||||
#define android_testLog(prio, tag) \
|
||||
((prio >= ANDROID_LOG_VERBOSE) ? (__android_log_is_loggable_len(prio, tag, (tag) ? strlen(tag) : 0, prio) != 0) : 0)
|
||||
#else
|
||||
#define android_testLog(prio, tag) 0
|
||||
#endif
|
||||
|
||||
#elif LOG_NDEBUG /* Production */
|
||||
#define android_testLog(prio, tag) \
|
||||
(__android_log_is_loggable_len(prio, tag, (tag) ? strlen(tag) : 0, ANDROID_LOG_DEBUG) != 0)
|
||||
#else
|
||||
#define android_testLog(prio, tag) \
|
||||
(__android_log_is_loggable_len(prio, tag, (tag) ? strlen(tag) : 0, ANDROID_LOG_VERBOSE) != 0)
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
/* clang-format off */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
extern "C" {
|
||||
|
||||
#endif
|
||||
|
||||
/* Returns `1` if the device is debuggable */
|
||||
|
||||
int __android_log_is_debuggable();
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
/*
|
||||
* Normally we strip the effects of ALOGV (VERBOSE messages),
|
||||
* LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
|
||||
* release builds be defining NDEBUG. You can modify this (for
|
||||
* example with "#define LOG_NDEBUG 0" at the top of your source
|
||||
* file) to change that behavior.
|
||||
*/
|
||||
|
||||
#ifndef LOG_NDEBUG
|
||||
#ifdef NDEBUG
|
||||
#define LOG_NDEBUG 1
|
||||
#else
|
||||
#define LOG_NDEBUG 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#ifndef __predict_false
|
||||
#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a verbose radio log message using current LOG_TAG.
|
||||
*/
|
||||
#ifndef RLOGV
|
||||
#define __RLOGV(...) \
|
||||
((void)__android_log_buf_print(LOG_ID_RADIO, \
|
||||
ANDROID_LOG_VERBOSE, \
|
||||
LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#if LOG_NDEBUG
|
||||
#define RLOGV(...)
|
||||
#else
|
||||
#define RLOGV(...) __RLOGV(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef RLOGV_IF
|
||||
#if LOG_NDEBUG
|
||||
#define RLOGV_IF(cond, ...) ((void)0)
|
||||
#else
|
||||
#define RLOGV_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, \
|
||||
LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Simplified macro to send a debug radio log message using current LOG_TAG. */
|
||||
|
||||
#ifndef RLOGD
|
||||
#if LOG_NDEBUG
|
||||
#define RLOGD(...) ((void)0)
|
||||
#else
|
||||
#define RLOGD(...) \
|
||||
((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef RLOGD_IF
|
||||
#define RLOGD_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, \
|
||||
LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
||||
|
||||
/* Simplified macro to send an info radio log message using current LOG_TAG. */
|
||||
|
||||
#ifndef RLOGI
|
||||
#define RLOGI(...) \
|
||||
((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef RLOGI_IF
|
||||
#define RLOGI_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, \
|
||||
LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
||||
|
||||
/* Simplified macro to send a warning radio log message using current LOG_TAG. */
|
||||
#ifndef RLOGW
|
||||
#define RLOGW(...) \
|
||||
((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef RLOGW_IF
|
||||
#define RLOGW_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, \
|
||||
LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an error radio log message using current LOG_TAG.
|
||||
*/
|
||||
#ifndef RLOGE
|
||||
#define RLOGE(...) \
|
||||
((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef RLOGE_IF
|
||||
#define RLOGE_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, \
|
||||
LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <android/log.h>
|
||||
#include <log/log_time.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ANDROID_LOG_WRAP_DEFAULT_TIMEOUT 7200 /* 2 hour default */
|
||||
|
||||
/*
|
||||
* Native log reading interface section. See logcat for sample code.
|
||||
*
|
||||
* The preferred API is an exec of logcat. Likely uses of this interface
|
||||
* are if native code suffers from exec or filtration being too costly,
|
||||
* access to raw information, or parsing is an issue.
|
||||
*/
|
||||
|
||||
struct logger_entry {
|
||||
uint16_t len; /* length of the payload */
|
||||
uint16_t hdr_size; /* sizeof(struct logger_entry) */
|
||||
int32_t pid; /* generating process's pid */
|
||||
uint32_t tid; /* generating process's tid */
|
||||
uint32_t sec; /* seconds since Epoch */
|
||||
uint32_t nsec; /* nanoseconds */
|
||||
uint32_t lid; /* log id of the payload, bottom 4 bits currently */
|
||||
uint32_t uid; /* generating process's uid */
|
||||
};
|
||||
|
||||
/*
|
||||
* The maximum size of a log entry which can be read.
|
||||
* An attempt to read less than this amount may result
|
||||
* in read() returning EINVAL.
|
||||
*/
|
||||
#define LOGGER_ENTRY_MAX_LEN (5 * 1024)
|
||||
|
||||
struct log_msg {
|
||||
union {
|
||||
unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
|
||||
struct logger_entry entry;
|
||||
} __attribute__((aligned(4)));
|
||||
#ifdef __cplusplus
|
||||
uint64_t nsec() const {
|
||||
return static_cast<uint64_t>(entry.sec) * NS_PER_SEC + entry.nsec;
|
||||
}
|
||||
log_id_t id() {
|
||||
return static_cast<log_id_t>(entry.lid);
|
||||
}
|
||||
char* msg() {
|
||||
unsigned short hdr_size = entry.hdr_size;
|
||||
if (hdr_size >= sizeof(struct log_msg) - sizeof(entry)) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<char*>(buf) + hdr_size;
|
||||
}
|
||||
unsigned int len() { return entry.hdr_size + entry.len; }
|
||||
#endif
|
||||
};
|
||||
|
||||
struct logger;
|
||||
|
||||
log_id_t android_logger_get_id(struct logger* logger);
|
||||
|
||||
/* Clears the given log buffer. */
|
||||
int android_logger_clear(struct logger* logger);
|
||||
/* Return the allotted size for the given log buffer. */
|
||||
long android_logger_get_log_size(struct logger* logger);
|
||||
/* Set the allotted size for the given log buffer. */
|
||||
int android_logger_set_log_size(struct logger* logger, unsigned long size);
|
||||
/* Return the actual, uncompressed size that can be read from the given log buffer. */
|
||||
long android_logger_get_log_readable_size(struct logger* logger);
|
||||
/* Return the actual, compressed size that the given log buffer is consuming. */
|
||||
long android_logger_get_log_consumed_size(struct logger* logger);
|
||||
/* Deprecated. Always returns '4' regardless of input. */
|
||||
int android_logger_get_log_version(struct logger* logger);
|
||||
|
||||
struct logger_list;
|
||||
|
||||
ssize_t android_logger_get_statistics(struct logger_list* logger_list,
|
||||
char* buf, size_t len);
|
||||
ssize_t android_logger_get_prune_list(struct logger_list* logger_list,
|
||||
char* buf, size_t len);
|
||||
int android_logger_set_prune_list(struct logger_list* logger_list, const char* buf, size_t len);
|
||||
|
||||
/* The below values are used for the `mode` argument of the below functions. */
|
||||
/* Note that 0x00000003 were previously used and should be considered reserved. */
|
||||
#define ANDROID_LOG_NONBLOCK 0x00000800
|
||||
#define ANDROID_LOG_WRAP 0x40000000 /* Block until buffer about to wrap */
|
||||
#define ANDROID_LOG_PSTORE 0x80000000
|
||||
|
||||
struct logger_list* android_logger_list_alloc(int mode, unsigned int tail,
|
||||
pid_t pid);
|
||||
struct logger_list* android_logger_list_alloc_time(int mode, log_time start,
|
||||
pid_t pid);
|
||||
void android_logger_list_free(struct logger_list* logger_list);
|
||||
/* In the purest sense, the following two are orthogonal interfaces */
|
||||
int android_logger_list_read(struct logger_list* logger_list,
|
||||
struct log_msg* log_msg);
|
||||
|
||||
/* Multiple log_id_t opens */
|
||||
struct logger* android_logger_open(struct logger_list* logger_list, log_id_t id);
|
||||
/* Single log_id_t open */
|
||||
struct logger_list* android_logger_list_open(log_id_t id, int mode,
|
||||
unsigned int tail, pid_t pid);
|
||||
#define android_logger_list_close android_logger_list_free
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define android_errorWriteLog(tag, subTag) \
|
||||
__android_log_error_write(tag, subTag, -1, NULL, 0)
|
||||
|
||||
#define android_errorWriteWithInfoLog(tag, subTag, uid, data, dataLen) \
|
||||
__android_log_error_write(tag, subTag, uid, data, dataLen)
|
||||
|
||||
int __android_log_error_write(int tag, const char* subTag, int32_t uid,
|
||||
const char* data, uint32_t dataLen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
/*
|
||||
* Normally we strip the effects of ALOGV (VERBOSE messages),
|
||||
* LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
|
||||
* release builds be defining NDEBUG. You can modify this (for
|
||||
* example with "#define LOG_NDEBUG 0" at the top of your source
|
||||
* file) to change that behavior.
|
||||
*/
|
||||
|
||||
#ifndef LOG_NDEBUG
|
||||
#ifdef NDEBUG
|
||||
#define LOG_NDEBUG 1
|
||||
#else
|
||||
#define LOG_NDEBUG 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef __predict_false
|
||||
#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a verbose system log message using current LOG_TAG.
|
||||
*/
|
||||
#ifndef SLOGV
|
||||
#define __SLOGV(...) \
|
||||
((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#if LOG_NDEBUG
|
||||
#define SLOGV(...) \
|
||||
do { \
|
||||
if (0) { \
|
||||
__SLOGV(__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define SLOGV(...) __SLOGV(__VA_ARGS__)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef SLOGV_IF
|
||||
#if LOG_NDEBUG
|
||||
#define SLOGV_IF(cond, ...) ((void)0)
|
||||
#else
|
||||
#define SLOGV_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, \
|
||||
LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a debug system log message using current LOG_TAG.
|
||||
*/
|
||||
#ifndef SLOGD
|
||||
#define SLOGD(...) \
|
||||
((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef SLOGD_IF
|
||||
#define SLOGD_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, \
|
||||
LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an info system log message using current LOG_TAG.
|
||||
*/
|
||||
#ifndef SLOGI
|
||||
#define SLOGI(...) \
|
||||
((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef SLOGI_IF
|
||||
#define SLOGI_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, \
|
||||
LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a warning system log message using current LOG_TAG.
|
||||
*/
|
||||
#ifndef SLOGW
|
||||
#define SLOGW(...) \
|
||||
((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef SLOGW_IF
|
||||
#define SLOGW_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, \
|
||||
LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an error system log message using current LOG_TAG.
|
||||
*/
|
||||
#ifndef SLOGE
|
||||
#define SLOGE(...) \
|
||||
((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef SLOGE_IF
|
||||
#define SLOGE_IF(cond, ...) \
|
||||
((__predict_false(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, \
|
||||
LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
/* struct log_time is a wire-format variant of struct timespec */
|
||||
#define NS_PER_SEC 1000000000ULL
|
||||
#define US_PER_SEC 1000000ULL
|
||||
#define MS_PER_SEC 1000ULL
|
||||
|
||||
#define LOG_TIME_SEC(t) ((t)->tv_sec)
|
||||
/* next power of two after NS_PER_SEC */
|
||||
#define LOG_TIME_NSEC(t) ((t)->tv_nsec & (UINT32_MAX >> 2))
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
extern "C" {
|
||||
|
||||
struct log_time {
|
||||
public:
|
||||
uint32_t tv_sec = 0; /* good to Feb 5 2106 */
|
||||
uint32_t tv_nsec = 0;
|
||||
|
||||
static constexpr timespec EPOCH = {0, 0};
|
||||
|
||||
log_time() {}
|
||||
explicit log_time(const timespec& T)
|
||||
: tv_sec(static_cast<uint32_t>(T.tv_sec)), tv_nsec(static_cast<uint32_t>(T.tv_nsec)) {}
|
||||
explicit log_time(uint32_t sec, uint32_t nsec = 0)
|
||||
: tv_sec(sec), tv_nsec(nsec) {
|
||||
}
|
||||
#ifdef __linux__
|
||||
explicit log_time(clockid_t id) {
|
||||
timespec T;
|
||||
clock_gettime(id, &T);
|
||||
tv_sec = static_cast<uint32_t>(T.tv_sec);
|
||||
tv_nsec = static_cast<uint32_t>(T.tv_nsec);
|
||||
}
|
||||
#endif
|
||||
/* timespec */
|
||||
bool operator==(const timespec& T) const {
|
||||
return (tv_sec == static_cast<uint32_t>(T.tv_sec)) &&
|
||||
(tv_nsec == static_cast<uint32_t>(T.tv_nsec));
|
||||
}
|
||||
bool operator!=(const timespec& T) const {
|
||||
return !(*this == T);
|
||||
}
|
||||
bool operator<(const timespec& T) const {
|
||||
return (tv_sec < static_cast<uint32_t>(T.tv_sec)) ||
|
||||
((tv_sec == static_cast<uint32_t>(T.tv_sec)) &&
|
||||
(tv_nsec < static_cast<uint32_t>(T.tv_nsec)));
|
||||
}
|
||||
bool operator>=(const timespec& T) const {
|
||||
return !(*this < T);
|
||||
}
|
||||
bool operator>(const timespec& T) const {
|
||||
return (tv_sec > static_cast<uint32_t>(T.tv_sec)) ||
|
||||
((tv_sec == static_cast<uint32_t>(T.tv_sec)) &&
|
||||
(tv_nsec > static_cast<uint32_t>(T.tv_nsec)));
|
||||
}
|
||||
bool operator<=(const timespec& T) const {
|
||||
return !(*this > T);
|
||||
}
|
||||
|
||||
/* log_time */
|
||||
bool operator==(const log_time& T) const {
|
||||
return (tv_sec == T.tv_sec) && (tv_nsec == T.tv_nsec);
|
||||
}
|
||||
bool operator!=(const log_time& T) const {
|
||||
return !(*this == T);
|
||||
}
|
||||
bool operator<(const log_time& T) const {
|
||||
return (tv_sec < T.tv_sec) ||
|
||||
((tv_sec == T.tv_sec) && (tv_nsec < T.tv_nsec));
|
||||
}
|
||||
bool operator>=(const log_time& T) const {
|
||||
return !(*this < T);
|
||||
}
|
||||
bool operator>(const log_time& T) const {
|
||||
return (tv_sec > T.tv_sec) ||
|
||||
((tv_sec == T.tv_sec) && (tv_nsec > T.tv_nsec));
|
||||
}
|
||||
bool operator<=(const log_time& T) const {
|
||||
return !(*this > T);
|
||||
}
|
||||
|
||||
log_time operator-=(const log_time& T) {
|
||||
// No concept of negative time, clamp to EPOCH
|
||||
if (*this <= T) {
|
||||
return *this = log_time(EPOCH);
|
||||
}
|
||||
|
||||
if (this->tv_nsec < T.tv_nsec) {
|
||||
--this->tv_sec;
|
||||
this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec;
|
||||
} else {
|
||||
this->tv_nsec -= T.tv_nsec;
|
||||
}
|
||||
this->tv_sec -= T.tv_sec;
|
||||
|
||||
return *this;
|
||||
}
|
||||
log_time operator-(const log_time& T) const {
|
||||
log_time local(*this);
|
||||
return local -= T;
|
||||
}
|
||||
log_time operator+=(const log_time& T) {
|
||||
this->tv_nsec += T.tv_nsec;
|
||||
if (this->tv_nsec >= NS_PER_SEC) {
|
||||
this->tv_nsec -= NS_PER_SEC;
|
||||
++this->tv_sec;
|
||||
}
|
||||
this->tv_sec += T.tv_sec;
|
||||
|
||||
return *this;
|
||||
}
|
||||
log_time operator+(const log_time& T) const {
|
||||
log_time local(*this);
|
||||
return local += T;
|
||||
}
|
||||
|
||||
uint64_t nsec() const {
|
||||
return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
|
||||
}
|
||||
uint64_t usec() const {
|
||||
return static_cast<uint64_t>(tv_sec) * US_PER_SEC +
|
||||
tv_nsec / (NS_PER_SEC / US_PER_SEC);
|
||||
}
|
||||
uint64_t msec() const {
|
||||
return static_cast<uint64_t>(tv_sec) * MS_PER_SEC +
|
||||
tv_nsec / (NS_PER_SEC / MS_PER_SEC);
|
||||
}
|
||||
|
||||
/* Add %#q for the fraction of a second to the standard library functions */
|
||||
char* strptime(const char* s, const char* format);
|
||||
} __attribute__((__packed__));
|
||||
}
|
||||
|
||||
#else /* __cplusplus */
|
||||
|
||||
typedef struct log_time {
|
||||
uint32_t tv_sec;
|
||||
uint32_t tv_nsec;
|
||||
} __attribute__((__packed__)) log_time;
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <android/log.h>
|
||||
#include <log/event_tag_map.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
/* Verbs */
|
||||
FORMAT_OFF = 0,
|
||||
FORMAT_BRIEF,
|
||||
FORMAT_PROCESS,
|
||||
FORMAT_TAG,
|
||||
FORMAT_THREAD,
|
||||
FORMAT_RAW,
|
||||
FORMAT_TIME,
|
||||
FORMAT_THREADTIME,
|
||||
FORMAT_LONG,
|
||||
/* Adverbs. The following are modifiers to above format verbs */
|
||||
FORMAT_MODIFIER_COLOR, /* converts priority to color */
|
||||
FORMAT_MODIFIER_TIME_USEC, /* switches from msec to usec time precision */
|
||||
FORMAT_MODIFIER_PRINTABLE, /* converts non-printable to printable escapes */
|
||||
FORMAT_MODIFIER_YEAR, /* Adds year to date */
|
||||
FORMAT_MODIFIER_ZONE, /* Adds zone to date, + UTC */
|
||||
FORMAT_MODIFIER_EPOCH, /* Print time as seconds since Jan 1 1970 */
|
||||
FORMAT_MODIFIER_MONOTONIC, /* Print cpu time as seconds since start */
|
||||
FORMAT_MODIFIER_UID, /* Adds uid */
|
||||
FORMAT_MODIFIER_DESCRIPT, /* Adds descriptive */
|
||||
/* private, undocumented */
|
||||
FORMAT_MODIFIER_TIME_NSEC, /* switches from msec to nsec time precision */
|
||||
} AndroidLogPrintFormat;
|
||||
|
||||
typedef struct AndroidLogFormat_t AndroidLogFormat;
|
||||
|
||||
typedef struct AndroidLogEntry_t {
|
||||
time_t tv_sec;
|
||||
long tv_nsec;
|
||||
android_LogPriority priority;
|
||||
int32_t uid;
|
||||
int32_t pid;
|
||||
int32_t tid;
|
||||
const char* tag;
|
||||
size_t tagLen;
|
||||
size_t messageLen;
|
||||
const char* message;
|
||||
} AndroidLogEntry;
|
||||
|
||||
AndroidLogFormat* android_log_format_new();
|
||||
|
||||
void android_log_format_free(AndroidLogFormat* p_format);
|
||||
|
||||
/* currently returns 0 if format is a modifier, 1 if not */
|
||||
int android_log_setPrintFormat(AndroidLogFormat* p_format,
|
||||
AndroidLogPrintFormat format);
|
||||
|
||||
/**
|
||||
* Returns FORMAT_OFF on invalid string
|
||||
*/
|
||||
AndroidLogPrintFormat android_log_formatFromString(const char* s);
|
||||
|
||||
/**
|
||||
* filterExpression: a single filter expression
|
||||
* eg "AT:d"
|
||||
*
|
||||
* returns 0 on success and -1 on invalid expression
|
||||
*
|
||||
* Assumes single threaded execution
|
||||
*
|
||||
*/
|
||||
|
||||
int android_log_addFilterRule(AndroidLogFormat* p_format,
|
||||
const char* filterExpression);
|
||||
|
||||
/**
|
||||
* filterString: a whitespace-separated set of filter expressions
|
||||
* eg "AT:d *:i"
|
||||
*
|
||||
* returns 0 on success and -1 on invalid expression
|
||||
*
|
||||
* Assumes single threaded execution
|
||||
*
|
||||
*/
|
||||
|
||||
int android_log_addFilterString(AndroidLogFormat* p_format,
|
||||
const char* filterString);
|
||||
|
||||
/**
|
||||
* returns 1 if this log line should be printed based on its priority
|
||||
* and tag, and 0 if it should not
|
||||
*/
|
||||
int android_log_shouldPrintLine(AndroidLogFormat* p_format, const char* tag,
|
||||
android_LogPriority pri);
|
||||
|
||||
/**
|
||||
* Splits a wire-format buffer into an AndroidLogEntry
|
||||
* entry allocated by caller. Pointers will point directly into buf
|
||||
*
|
||||
* Returns 0 on success and -1 on invalid wire format (entry will be
|
||||
* in unspecified state)
|
||||
*/
|
||||
int android_log_processLogBuffer(struct logger_entry* buf,
|
||||
AndroidLogEntry* entry);
|
||||
|
||||
/**
|
||||
* Like android_log_processLogBuffer, but for binary logs.
|
||||
*
|
||||
* If "map" is non-NULL, it will be used to convert the log tag number
|
||||
* into a string.
|
||||
*/
|
||||
int android_log_processBinaryLogBuffer(struct logger_entry* buf,
|
||||
AndroidLogEntry* entry,
|
||||
const EventTagMap* map, char* messageBuf,
|
||||
int messageBufLen);
|
||||
|
||||
/**
|
||||
* Formats a log message into a buffer
|
||||
*
|
||||
* Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
|
||||
* If return value != defaultBuffer, caller must call free()
|
||||
* Returns NULL on malloc error
|
||||
*/
|
||||
|
||||
char* android_log_formatLogLine(AndroidLogFormat* p_format, char* defaultBuffer,
|
||||
size_t defaultBufferSize,
|
||||
const AndroidLogEntry* p_line,
|
||||
size_t* p_outLength);
|
||||
|
||||
/**
|
||||
* Formats a log message into a FILE*.
|
||||
*/
|
||||
size_t android_log_printLogLine(AndroidLogFormat* p_format, FILE* fp, const AndroidLogEntry* entry);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
#ifndef __MURMURHASH_H
|
||||
#define __MURMURHASH_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
|
||||
static inline uint32_t murmurhash(const char *name)
|
||||
{
|
||||
uint32_t c1 = 0xcc9e2d51;
|
||||
uint32_t c2 = 0x1b873593;
|
||||
uint32_t n = 0xe6546b64;
|
||||
uint32_t m = 5;
|
||||
uint32_t h = 0;
|
||||
uint32_t k = 0;
|
||||
uint32_t r1 = 15;
|
||||
uint32_t r2 = 13;
|
||||
int i = 0, seed = 0;
|
||||
uint8_t *d = (uint8_t *) name; // 32 bit extract from `key'
|
||||
const uint32_t *chunks = NULL;
|
||||
const uint8_t *tail = NULL; // tail - last 8 bytes
|
||||
int len = strlen(name);
|
||||
int l = len / 4; // chunk length
|
||||
|
||||
h = seed;
|
||||
|
||||
chunks = (const uint32_t *) (d + l * 4); // body
|
||||
tail = (const uint8_t *) (d + l * 4); // last 8 byte chunk of `key'
|
||||
|
||||
// for each 4 byte chunk of `key'
|
||||
for (i = -l; i != 0; ++i) {
|
||||
// next 4 byte chunk of `key'
|
||||
k = chunks[i];
|
||||
|
||||
// encode next 4 byte chunk of `key'
|
||||
k *= c1;
|
||||
k = (k << r1) | (k >> (32 - r1));
|
||||
k *= c2;
|
||||
|
||||
// append to hash
|
||||
h ^= k;
|
||||
h = (h << r2) | (h >> (32 - r2));
|
||||
h = h * m + n;
|
||||
}
|
||||
|
||||
k = 0;
|
||||
|
||||
// remainder
|
||||
switch (len & 3) { // `len % 4'
|
||||
case 3: k ^= (tail[2] << 16);
|
||||
[[fallthrough]];
|
||||
case 2: k ^= (tail[1] << 8);
|
||||
[[fallthrough]];
|
||||
case 1:
|
||||
k ^= tail[0];
|
||||
k *= c1;
|
||||
k = (k << r1) | (k >> (32 - r1));
|
||||
k *= c2;
|
||||
h ^= k;
|
||||
}
|
||||
|
||||
h ^= len;
|
||||
|
||||
h ^= (h >> 16);
|
||||
h *= 0x85ebca6b;
|
||||
h ^= (h >> 13);
|
||||
h *= 0xc2b2ae35;
|
||||
h ^= (h >> 16);
|
||||
|
||||
return h;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* clang-format on */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* This file is used to define the internal protocol for the Android Logger */
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Android private interfaces */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
#include <log/log.h>
|
||||
#include <log/log_event_list.h>
|
||||
|
||||
#define LOGGER_MAGIC 'l'
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Header Structure to pstore */
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
uint8_t magic;
|
||||
uint16_t len;
|
||||
uint16_t uid;
|
||||
uint16_t pid;
|
||||
} android_pmsg_log_header_t;
|
||||
|
||||
/* Header Structure to logd, and second header for pstore */
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
uint8_t id;
|
||||
uint16_t tid;
|
||||
log_time realtime;
|
||||
} android_log_header_t;
|
||||
|
||||
/* Event Header Structure to logd */
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
int32_t tag; // Little Endian Order
|
||||
} android_event_header_t;
|
||||
|
||||
// Event payload EVENT_TYPE_LIST
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
int8_t type; // EVENT_TYPE_LIST
|
||||
int8_t element_count;
|
||||
} android_event_list_t;
|
||||
|
||||
// Event payload EVENT_TYPE_FLOAT
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
int8_t type; // EVENT_TYPE_FLOAT
|
||||
float data;
|
||||
} android_event_float_t;
|
||||
|
||||
/* Event payload EVENT_TYPE_INT */
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
int8_t type; // EVENT_TYPE_INT
|
||||
int32_t data; // Little Endian Order
|
||||
} android_event_int_t;
|
||||
|
||||
/* Event with single EVENT_TYPE_INT */
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
android_event_header_t header;
|
||||
android_event_int_t payload;
|
||||
} android_log_event_int_t;
|
||||
|
||||
/* Event payload EVENT_TYPE_LONG */
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
int8_t type; // EVENT_TYPE_LONG
|
||||
int64_t data; // Little Endian Order
|
||||
} android_event_long_t;
|
||||
|
||||
/* Event with single EVENT_TYPE_LONG */
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
android_event_header_t header;
|
||||
android_event_long_t payload;
|
||||
} android_log_event_long_t;
|
||||
|
||||
/*
|
||||
* Event payload EVENT_TYPE_STRING
|
||||
*
|
||||
* Danger: do not embed this structure into another structure.
|
||||
* This structure uses a flexible array member, and when
|
||||
* compiled using g++, __builtin_object_size(data, 1) returns
|
||||
* a bad value. This is possibly a g++ bug, or a bug due to
|
||||
* the fact that flexible array members are not supported
|
||||
* in C++.
|
||||
* http://stackoverflow.com/questions/4412749/are-flexible-array-members-valid-in-c
|
||||
*/
|
||||
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
int8_t type; // EVENT_TYPE_STRING;
|
||||
int32_t length; // Little Endian Order
|
||||
char data[];
|
||||
} android_event_string_t;
|
||||
|
||||
/* Event with single EVENT_TYPE_STRING */
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
android_event_header_t header;
|
||||
int8_t type; // EVENT_TYPE_STRING;
|
||||
int32_t length; // Little Endian Order
|
||||
char data[];
|
||||
} android_log_event_string_t;
|
||||
|
||||
#define ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE 256 /* 1MB file */
|
||||
#define ANDROID_LOG_PMSG_FILE_SEQUENCE 1000
|
||||
|
||||
ssize_t __android_log_pmsg_file_write(log_id_t logId, char prio,
|
||||
const char* filename, const char* buf,
|
||||
size_t len);
|
||||
|
||||
#define ANDROID_LOG_ANY ANDROID_LOG_UNKNOWN
|
||||
|
||||
/* first 5 arguments match __android_log_msg_file_write, a cast is safe */
|
||||
typedef ssize_t (*__android_log_pmsg_file_read_fn)(log_id_t logId, char prio,
|
||||
const char* filename,
|
||||
const char* buf, size_t len,
|
||||
void* arg);
|
||||
|
||||
ssize_t __android_log_pmsg_file_read(log_id_t logId, char prio,
|
||||
const char* prefix,
|
||||
__android_log_pmsg_file_read_fn fn,
|
||||
void* arg);
|
||||
|
||||
int __android_log_security_bwrite(int32_t tag, const void* payload, size_t len);
|
||||
int __android_log_security_bswrite(int32_t tag, const char* payload);
|
||||
int __android_log_security(); /* Device Owner is present */
|
||||
|
||||
/* Retrieve the composed event buffer */
|
||||
int android_log_write_list_buffer(android_log_context ctx, const char** msg);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SYS_SYSTEM_PROPERTIES_H
|
||||
#define _INCLUDE_SYS_SYSTEM_PROPERTIES_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct prop_info prop_info;
|
||||
|
||||
#define PROP_VALUE_MAX 255
|
||||
|
||||
/*
|
||||
* Sets system property `name` to `value`, creating it if it doesn't exist.
|
||||
*/
|
||||
int __system_property_set(const char* __name,
|
||||
const char* __value);
|
||||
|
||||
/*
|
||||
* Returns a `prop_info` for system property `name`, or NULL if not found.
|
||||
* Consider caching the result due to the expensive lookup.
|
||||
*/
|
||||
const prop_info* __system_property_find(const char* __name);
|
||||
|
||||
/*
|
||||
* Calls `callback` with name, value, and serial number for property `pi`.
|
||||
*/
|
||||
void __system_property_read_callback(const prop_info* __pi,
|
||||
void (*__callback)(void* __cookie, const char* __name, const char* __value, uint32_t __serial),
|
||||
void* __cookie);
|
||||
|
||||
/*
|
||||
* Iterates through all system properties and calls the provided callback for each.
|
||||
* Primarily for debugging and inspection.
|
||||
*/
|
||||
int __system_property_foreach(void (*__callback)(const prop_info* __pi, void* __cookie), void* __cookie);
|
||||
|
||||
/*
|
||||
* Waits for the system property `pi` to be updated past `old_serial`, with an optional timeout.
|
||||
* If `pi` is NULL, waits for the global serial number.
|
||||
*/
|
||||
struct timespec;
|
||||
bool __system_property_wait(const prop_info* __pi, uint32_t __old_serial, uint32_t* __new_serial_ptr, const struct timespec* __relative_timeout);
|
||||
|
||||
/* Deprecated: Property name length limit. */
|
||||
#define PROP_NAME_MAX 127
|
||||
|
||||
/* Deprecated: Use __system_property_read_callback instead. */
|
||||
int __system_property_read(const prop_info* __pi, char* __name, char* __value);
|
||||
|
||||
/* Deprecated: Use __system_property_read_callback instead. */
|
||||
int __system_property_get(const char* __name, char* __value);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
* Copyright (C) 2024 Xiaomi Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <kvdb.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
static int kvdb_get_index(const char* key)
|
||||
{
|
||||
if (strncmp(key, PERSIST_LABEL, PERSIST_LABEL_LEN) == 0) {
|
||||
return KVDB_PERSIST;
|
||||
} else {
|
||||
#ifdef CONFIG_KVDB_TEMPORARY_STORAGE
|
||||
return KVDB_MEM;
|
||||
#else
|
||||
return -EINVAL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static bool kvdb_kv_is_exist(struct kvdb* kvdb, const char* key,
|
||||
size_t key_len, size_t val_len)
|
||||
{
|
||||
return kvdb_get(kvdb, key, key_len, NULL, val_len) >= 0;
|
||||
}
|
||||
|
||||
static bool kvdb_is_readonly(const char* key)
|
||||
{
|
||||
return strncmp(key, "ro.", 3) == 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kvdb_init
|
||||
*
|
||||
* Description:
|
||||
* init resource of nvs .
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save filekv instance.
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int kvdb_init(struct kvdb** kvdb)
|
||||
{
|
||||
int ret = kvdb_persist_init(kvdb);
|
||||
#ifdef CONFIG_KVDB_TEMPORARY_STORAGE
|
||||
if (ret >= 0) {
|
||||
ret = mkdir(CONFIG_KVDB_TEMPORARY_PATH, 0666);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
|
||||
/* Existing tmp dir is acceptable. */
|
||||
|
||||
if (ret == -EEXIST)
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kvdb_uninit
|
||||
*
|
||||
* Description:
|
||||
* init resource of filekv .
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save filekv instance.
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void kvdb_uninit(struct kvdb* kvdb)
|
||||
{
|
||||
kvdb_persist_uninit(kvdb);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kvdb_set
|
||||
*
|
||||
* Description:
|
||||
* key-value set.
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save filekv instance.
|
||||
* key - Pointer to key to set.
|
||||
* key_len - the length of the key
|
||||
* value - Pointer to data to be saved
|
||||
* val_len - the length of the value
|
||||
* force - unused parameter
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int kvdb_set(struct kvdb* kvdb, const char* key, size_t key_len,
|
||||
const void* value, size_t val_len, bool force)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!force && kvdb_is_readonly(key) && kvdb_kv_is_exist(kvdb, key, key_len, val_len))
|
||||
return -EPERM;
|
||||
|
||||
ret = kvdb_get_index(key);
|
||||
switch (ret) {
|
||||
case KVDB_PERSIST:
|
||||
ret = kvdb_persist_set(kvdb, key, key_len, value, val_len, force);
|
||||
break;
|
||||
#ifdef CONFIG_KVDB_TEMPORARY_STORAGE
|
||||
case KVDB_MEM:
|
||||
ret = kvdb_file_set(CONFIG_KVDB_TEMPORARY_PATH, key, value, val_len);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kvdb_get
|
||||
*
|
||||
* Description:
|
||||
* key-value get.
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save filekv instance.
|
||||
* key - Pointer to key to get.
|
||||
* key_len - the length of the key
|
||||
* value - Pointer to data to be get
|
||||
* val_len - the length of the value
|
||||
*
|
||||
* Returned Value:
|
||||
* the length of value, > 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
ssize_t kvdb_get(struct kvdb* kvdb, const char* key, size_t key_len,
|
||||
void* value, size_t val_len)
|
||||
{
|
||||
ssize_t ret = kvdb_get_index(key);
|
||||
switch (ret) {
|
||||
case KVDB_PERSIST:
|
||||
ret = kvdb_persist_get(kvdb, key, key_len, value, val_len);
|
||||
break;
|
||||
#ifdef CONFIG_KVDB_TEMPORARY_STORAGE
|
||||
case KVDB_MEM:
|
||||
ret = kvdb_file_get(CONFIG_KVDB_TEMPORARY_PATH, key, value, val_len);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kvdb_delete
|
||||
*
|
||||
* Description:
|
||||
* key-value delete.
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save filekv instance.
|
||||
* key - Pointer to key to delete.
|
||||
* key_len - the length of the key
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int kvdb_delete(struct kvdb* kvdb, const char* key, size_t key_len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (kvdb_is_readonly(key))
|
||||
return -EPERM;
|
||||
|
||||
ret = kvdb_get_index(key);
|
||||
switch (ret) {
|
||||
case KVDB_PERSIST:
|
||||
ret = kvdb_persist_delete(kvdb, key, key_len);
|
||||
break;
|
||||
#ifdef CONFIG_KVDB_TEMPORARY_STORAGE
|
||||
case KVDB_MEM:
|
||||
ret = kvdb_file_delete(CONFIG_KVDB_TEMPORARY_PATH, key);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kvdb_list
|
||||
*
|
||||
* key-value list.
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save filekv instance.
|
||||
* consume - callback when key-value fetch success.
|
||||
* cookie - private data for consume callback.
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int kvdb_list(struct kvdb* kvdb, kvdb_consume consume, void* cookie)
|
||||
{
|
||||
int ret = kvdb_persist_list(kvdb, consume, cookie);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
#ifdef CONFIG_KVDB_TEMPORARY_STORAGE
|
||||
ret = kvdb_file_list(CONFIG_KVDB_TEMPORARY_PATH, consume, cookie);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kvdb_commit
|
||||
*
|
||||
* key-value commit.
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save filekv instance.
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int kvdb_commit(struct kvdb* kvdb)
|
||||
{
|
||||
/* Filekv over tmpfs always no need for commit */
|
||||
|
||||
return kvdb_persist_commit(kvdb);
|
||||
}
|
||||
|
|
@ -0,0 +1,925 @@
|
|||
/*
|
||||
* Copyright (C) 2023 Xiaomi Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <netpacket/rpmsg.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <kvdb.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
static ssize_t recv_safe(int sockfd, char* buf, size_t offset, size_t len)
|
||||
{
|
||||
while (offset < len) {
|
||||
ssize_t ret = recv(sockfd, buf + offset, len - offset, 0);
|
||||
if (ret < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
struct pollfd pfd;
|
||||
pfd.fd = sockfd;
|
||||
pfd.events = POLLIN;
|
||||
ret = poll(&pfd, 1, -1);
|
||||
if (ret > 0 && (pfd.revents & POLLIN)) {
|
||||
continue;
|
||||
} else {
|
||||
KVERR("poll ret %d error %d\n", ret, errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return -errno;
|
||||
}
|
||||
if (ret == 0)
|
||||
return -ENODATA;
|
||||
offset += ret;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_connect_one
|
||||
*
|
||||
* Description:
|
||||
* Initialize client socket and connect to server
|
||||
*
|
||||
* Input Parameters:
|
||||
* addr - server address
|
||||
* addrlen - server address length
|
||||
*
|
||||
* Returned Value:
|
||||
* On success return client socket fd.
|
||||
* On error return error value (<0).
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#if defined(CONFIG_NET_LOCAL) || defined(CONFIG_NET_RPMSG)
|
||||
static int property_connect_one(const struct sockaddr* addr,
|
||||
socklen_t addrlen)
|
||||
{
|
||||
int fd = socket(addr->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
int ret = connect(fd, addr, addrlen);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_NET_LOCAL) && defined(CONFIG_NET_RPMSG)
|
||||
/****************************************************************************
|
||||
* Name: use_local_socket
|
||||
*
|
||||
* Description:
|
||||
* Determine whether to use local socket or rpmsg socket to
|
||||
* connect to server
|
||||
*
|
||||
* Returned Value:
|
||||
* true: use local socket
|
||||
* false: use rpmsg socket
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline bool use_local_socket(void)
|
||||
{
|
||||
#ifdef CONFIG_KVDB_SERVER_CPUNAME
|
||||
struct sockaddr_rpmsg addr;
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
int fd = socket(AF_RPMSG, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
if (fd >= 0) {
|
||||
int ret = getsockname(fd, (struct sockaddr*)&addr, &addrlen);
|
||||
close(fd);
|
||||
if (ret == 0) {
|
||||
return strcmp(addr.rp_cpu, CONFIG_KVDB_SERVER_CPUNAME) == 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_connect
|
||||
*
|
||||
* Description:
|
||||
* Initialize client socket and connect to server
|
||||
*
|
||||
* Returned Value:
|
||||
* On success return client socket fd.
|
||||
* On error return error value (<0).
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int property_connect(void)
|
||||
{
|
||||
#ifdef CONFIG_NET_LOCAL
|
||||
const struct sockaddr_un laddr = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = PROP_SERVER_PATH,
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NET_RPMSG
|
||||
const struct sockaddr_rpmsg raddr = {
|
||||
.rp_family = AF_RPMSG,
|
||||
.rp_name = PROP_SERVER_PATH,
|
||||
.rp_cpu = CONFIG_KVDB_SERVER_CPUNAME,
|
||||
};
|
||||
#endif
|
||||
int ret = -ENOTSUP;
|
||||
#if defined(CONFIG_NET_LOCAL) && defined(CONFIG_NET_RPMSG)
|
||||
bool use_local = use_local_socket();
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
#if defined(CONFIG_NET_LOCAL) && defined(CONFIG_NET_RPMSG)
|
||||
if (use_local)
|
||||
ret = property_connect_one((const struct sockaddr*)&laddr, sizeof(laddr));
|
||||
else
|
||||
ret = property_connect_one((const struct sockaddr*)&raddr, sizeof(raddr));
|
||||
#elif defined(CONFIG_NET_LOCAL)
|
||||
ret = property_connect_one((const struct sockaddr*)&laddr, sizeof(laddr));
|
||||
#elif defined(CONFIG_NET_RPMSG)
|
||||
ret = property_connect_one((const struct sockaddr*)&raddr, sizeof(raddr));
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
if (ret >= 0)
|
||||
break;
|
||||
|
||||
usleep(1000);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_set_binary
|
||||
*
|
||||
* Description:
|
||||
* Store Key-Values to database.
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
* const void* value: entry value string
|
||||
* size_t val_len: the length of the value
|
||||
*
|
||||
* Returned Value:
|
||||
* 0: success
|
||||
* <0: failure during execution
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_set_binary(const char* key, const void* value, size_t val_len, bool oneway)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (!key)
|
||||
return -EINVAL;
|
||||
|
||||
size_t key_len = strlen(key) + 1;
|
||||
if (key_len > PROP_NAME_MAX)
|
||||
return -E2BIG;
|
||||
|
||||
if (val_len == 0 || val_len >= PROP_VALUE_MAX)
|
||||
return -E2BIG;
|
||||
|
||||
again:
|
||||
fd = property_connect();
|
||||
if (fd < 0) {
|
||||
KVERR("connect failed, fd=%d\n", fd);
|
||||
return fd;
|
||||
}
|
||||
|
||||
/*-------------------------------------*
|
||||
| 1 | 1 | 1 | key_len |val_len|
|
||||
|-------------------------------------|
|
||||
|'S'|key_len|val_len|[key'\0']|[value]|
|
||||
*-------------------------------------*/
|
||||
|
||||
char cmd[3] = {
|
||||
'S', key_len, val_len
|
||||
};
|
||||
|
||||
struct iovec iov[3] = {
|
||||
{ .iov_base = cmd, .iov_len = 3 },
|
||||
{ .iov_base = (char*)key, .iov_len = key_len },
|
||||
{ .iov_base = (char*)value, .iov_len = val_len },
|
||||
};
|
||||
|
||||
struct msghdr msg = { 0 };
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 3;
|
||||
|
||||
int ret = sendmsg(fd, &msg, 0);
|
||||
if (ret < 0) {
|
||||
/* handle server refused by backlog limitation */
|
||||
|
||||
if (oneway && errno == ECONNRESET) {
|
||||
close(fd);
|
||||
goto again;
|
||||
}
|
||||
|
||||
ret = -errno;
|
||||
KVERR("sendmsg failed, ret=%d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!oneway) {
|
||||
|
||||
/*-----*
|
||||
| 4 |
|
||||
|-----|
|
||||
|error|
|
||||
*-----*/
|
||||
|
||||
int32_t err;
|
||||
ret = recv(fd, &err, 4, 0);
|
||||
if (ret < 4) {
|
||||
KVERR("recv failed, ret=%d\n", ret);
|
||||
ret = ret < 0 ? -errno : -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = err;
|
||||
}
|
||||
|
||||
out:
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_get_binary
|
||||
*
|
||||
* Description:
|
||||
* Retrieve Key-Values from database.
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
* void* value: not NULL : pointer to string buffer
|
||||
* NULL : check whether this [key, value] exists
|
||||
* size_t val_len: the length of the value
|
||||
*
|
||||
* Returned Value:
|
||||
* On success returns the length of the value which will never be greater
|
||||
* than PROP_NAME_MAX.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
ssize_t property_get_binary(const char* key, void* value, size_t val_len)
|
||||
{
|
||||
if (!key)
|
||||
return -EINVAL;
|
||||
|
||||
size_t key_len = strlen(key) + 1;
|
||||
if (key_len > PROP_NAME_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
int fd = property_connect();
|
||||
if (fd < 0) {
|
||||
KVERR("connect failed, fd=%d\n", fd);
|
||||
return fd;
|
||||
}
|
||||
|
||||
/*-----------------------------*
|
||||
| 1 | 1 | key_len |val_len|
|
||||
| --------------------|-------|
|
||||
|'G'|key_len|[key'\0']|[value]|
|
||||
*-----------------------------*/
|
||||
|
||||
char cmd[3] = {
|
||||
'G', key_len, val_len
|
||||
};
|
||||
|
||||
struct iovec iov[2] = {
|
||||
{ .iov_base = cmd, .iov_len = 3 },
|
||||
{ .iov_base = (char*)key, .iov_len = key_len },
|
||||
};
|
||||
|
||||
struct msghdr msg = { 0 };
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 2;
|
||||
|
||||
ssize_t len = -1;
|
||||
int ret = sendmsg(fd, &msg, 0);
|
||||
if (ret < 0) {
|
||||
KVERR("sendmsg failed, errno=%d\n", errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*-------*
|
||||
|val_len|
|
||||
|-------|
|
||||
|[value]|
|
||||
*-------*/
|
||||
|
||||
if (value) {
|
||||
/* value is not NULL, receive all the value */
|
||||
|
||||
len = recv(fd, value, val_len, 0);
|
||||
if (len <= 0) {
|
||||
if (len != 0) {
|
||||
KVERR("recv failed, len=%d\n", len);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
/* value is NULL, receive two chars to check whether this
|
||||
* [key, value] exists
|
||||
*/
|
||||
|
||||
char tmpvalue[1];
|
||||
len = recv(fd, tmpvalue, 1, 0);
|
||||
if (len <= 0) {
|
||||
if (len != 0) {
|
||||
KVERR("recv failed, len=%d\n", len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
close(fd);
|
||||
return len < 0 ? -errno : len;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_delete
|
||||
*
|
||||
* Description:
|
||||
* Delete a KV pair by key
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
*
|
||||
* Returned Value:
|
||||
* 0: success
|
||||
* <0: failure during execution
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_delete(const char* key)
|
||||
{
|
||||
if (!key)
|
||||
return -EINVAL;
|
||||
|
||||
size_t key_len = strlen(key) + 1;
|
||||
if (key_len > PROP_NAME_MAX)
|
||||
return -E2BIG;
|
||||
|
||||
/* in environment variable? */
|
||||
if (getenv(key)) {
|
||||
int ret = unsetenv(key);
|
||||
if (ret < 0)
|
||||
ret = -errno;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fd = property_connect();
|
||||
if (fd < 0) {
|
||||
KVERR("connect failed, fd=%d\n", fd);
|
||||
return fd;
|
||||
}
|
||||
|
||||
/*---------------------*
|
||||
| 1 | 1 | key_len |
|
||||
| --------------------|
|
||||
|'D'|key_len|[key'\0']|
|
||||
*---------------------*/
|
||||
|
||||
char cmd[2] = {
|
||||
'D', key_len
|
||||
};
|
||||
|
||||
struct iovec iov[2] = {
|
||||
{ .iov_base = cmd, .iov_len = 2 },
|
||||
{ .iov_base = (char*)key, .iov_len = key_len },
|
||||
};
|
||||
|
||||
struct msghdr msg = { 0 };
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 2;
|
||||
|
||||
int ret = sendmsg(fd, &msg, 0);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
KVERR("sendmsg failed, ret=%d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*-----*
|
||||
| 4 |
|
||||
|-----|
|
||||
|error|
|
||||
*-----*/
|
||||
|
||||
int32_t err;
|
||||
ret = recv(fd, &err, 4, 0);
|
||||
if (ret < 4) {
|
||||
KVERR("recv failed, ret=%d\n", ret);
|
||||
ret = ret < 0 ? -errno : -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = err;
|
||||
|
||||
out:
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_list_binary
|
||||
*
|
||||
* Description:
|
||||
* List all KVs in every database and calls callback function.
|
||||
*
|
||||
* Input Parameters:
|
||||
* property_callback propfn: callback function
|
||||
* void* cookie: cookie data to pass to callback function
|
||||
*
|
||||
* Returned Value:
|
||||
* Returns 0 on success, <0 if all databases failed to open.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_list_binary(void (*propfn)(const char* key, const void* value, size_t val_len, void* cookie), void* cookie)
|
||||
{
|
||||
char* msg = NULL;
|
||||
|
||||
int fd = property_connect();
|
||||
if (fd < 0) {
|
||||
KVERR("connect failed, fd=%d\n", fd);
|
||||
return fd;
|
||||
}
|
||||
|
||||
/*---*
|
||||
| 1 |
|
||||
| --|
|
||||
|'L'|
|
||||
*---*/
|
||||
|
||||
int ret = send(fd, "L", 1, 0);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
KVERR("send failed, ret=%d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
msg = malloc(PROP_MSG_MAX);
|
||||
if (msg == NULL) {
|
||||
KVERR("malloc failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
/*---------------------------------*
|
||||
| 1 | 1 | key_len |val_len|
|
||||
|---------------------------------|
|
||||
|key_len|val_len|[key'\0']|[value]|
|
||||
*---------------------------------*/
|
||||
|
||||
ret = recv_safe(fd, msg, 0, 2);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
KVERR("recv_safe failed, ret=%d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (msg[0] == 0 && msg[1] == 0) {
|
||||
/* end of list */
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
size_t key_len = (unsigned char)msg[0];
|
||||
if (key_len > PROP_NAME_MAX)
|
||||
continue;
|
||||
|
||||
size_t val_len = (unsigned char)msg[1];
|
||||
if (val_len >= PROP_VALUE_MAX)
|
||||
continue;
|
||||
|
||||
size_t total = key_len + val_len + 2;
|
||||
ret = recv_safe(fd, msg, 2, total);
|
||||
if (ret < 0) {
|
||||
KVERR("recv_safe failed, ret=%d\n", ret);
|
||||
break;
|
||||
}
|
||||
|
||||
const char* key = msg + 2;
|
||||
void* value = msg + 2 + key_len;
|
||||
if (key[key_len - 1])
|
||||
continue;
|
||||
|
||||
propfn(key, value, val_len, cookie);
|
||||
}
|
||||
|
||||
out:
|
||||
free(msg);
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_wait
|
||||
*
|
||||
* Description:
|
||||
* Wait the monitored key until its value updated or key deleted.
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key : the monitored key string, support fnmatch pattern
|
||||
* char* newkey : pointer to a string buffer to receive the key of
|
||||
* the updated/deleted value
|
||||
* void* newvalue: pointer to a string buffer to receive the updated
|
||||
* value or deleted value
|
||||
* size_t val_len : the length of the newvalue
|
||||
* int timeout : the wait timeout time (in milliseconds)
|
||||
*
|
||||
* Returned Value:
|
||||
* On success returns the length of the value.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
ssize_t property_wait(const char* key, char* newkey, void* newvalue, size_t val_len, int timeout)
|
||||
{
|
||||
if (key == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
int fd = property_monitor_open(key);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
struct pollfd fds = {
|
||||
.fd = fd,
|
||||
.events = POLLIN
|
||||
};
|
||||
|
||||
int ret = poll(&fds, 1, timeout);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
KVERR("poll failed, ret=%d\n", ret);
|
||||
goto out;
|
||||
} else if (ret == 0 || (fds.revents & POLLIN) == 0) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = property_monitor_read(fd, newkey, newvalue, val_len);
|
||||
|
||||
out:
|
||||
property_monitor_close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_monitor_open
|
||||
*
|
||||
* Description:
|
||||
* Open a key monitor channel
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key : the monitored key string, support fnmatch pattern
|
||||
*
|
||||
* Returned Value:
|
||||
* On success returns a file descriptor, -errno otherwise.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_monitor_open(const char* key)
|
||||
{
|
||||
if (key == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
size_t key_len = strlen(key) + 1;
|
||||
if (key_len > PROP_NAME_MAX)
|
||||
return -E2BIG;
|
||||
|
||||
int fd = property_connect();
|
||||
if (fd < 0) {
|
||||
KVERR("connect failed, fd=%d\n", fd);
|
||||
return fd;
|
||||
}
|
||||
|
||||
/*------------------------*
|
||||
| 1 | 1 | key_len |
|
||||
|-------|-----------------|
|
||||
| 'M' |key_len|[key'\0']|
|
||||
*-------------------------*/
|
||||
|
||||
char cmd[2] = { 'M', key_len };
|
||||
|
||||
struct iovec iov[2] = {
|
||||
{ .iov_base = cmd, .iov_len = 2 },
|
||||
{ .iov_base = (char*)key, .iov_len = key_len },
|
||||
};
|
||||
|
||||
struct msghdr msg = { 0 };
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 2;
|
||||
|
||||
int ret = sendmsg(fd, &msg, 0);
|
||||
if (ret < 0) {
|
||||
KVERR("sendmsg failed, ret=%d\n", ret);
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*-----*
|
||||
| 4 |
|
||||
|-----|
|
||||
|error|
|
||||
*-----*/
|
||||
|
||||
int32_t err;
|
||||
ret = recv(fd, &err, 4, 0);
|
||||
if (ret < 4) {
|
||||
KVERR("recv failed, ret=%d\n", ret);
|
||||
ret = ret < 0 ? -errno : -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (err < 0) {
|
||||
ret = err;
|
||||
goto out;
|
||||
}
|
||||
|
||||
return fd;
|
||||
|
||||
out:
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_monitor_read
|
||||
*
|
||||
* Description:
|
||||
* Wait the monitored key until its value updated or key deleted.
|
||||
*
|
||||
* Input Parameters:
|
||||
* int fd : file descriptor returned by property_monitor_open()
|
||||
* char* newkey : pointer to a strint buffer to receive the key of the
|
||||
* updated/deleted value
|
||||
* void* newvalue: pointer to a string buffer to receive the updated
|
||||
* value or deleted value
|
||||
* size_t val_len: newvalue length
|
||||
*
|
||||
* Returned Value:
|
||||
* On success returns the length of the value.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
ssize_t property_monitor_read(int fd, char* newkey, void* newvalue, size_t val_len)
|
||||
{
|
||||
char* msg = malloc(PROP_MSG_MAX);
|
||||
if (msg == NULL) {
|
||||
KVERR("malloc failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ssize_t ret = recv(fd, msg, 2, 0);
|
||||
if (ret < 2) {
|
||||
KVERR("recv failed, ret=%d, errno=%d\n", ret, errno);
|
||||
free(msg);
|
||||
return ret < 0 ? -errno : -ENODATA;
|
||||
}
|
||||
|
||||
size_t key_len = (unsigned char)msg[0];
|
||||
if (key_len > PROP_NAME_MAX) {
|
||||
free(msg);
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
size_t len = (unsigned char)msg[1];
|
||||
if (len > PROP_VALUE_MAX) {
|
||||
free(msg);
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
size_t total = key_len + len + 2;
|
||||
ret = recv_safe(fd, msg, ret, total);
|
||||
if (ret < 0) {
|
||||
KVERR("recv_safe failed, ret=%d\n", ret);
|
||||
free(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char* key = &msg[2];
|
||||
if (newkey != NULL)
|
||||
strlcpy(newkey, key, PROP_NAME_MAX);
|
||||
|
||||
if (newvalue != NULL) {
|
||||
/*--------------------------------*
|
||||
| 1 | 1 | key_len |val_len|
|
||||
|---------------------------------|
|
||||
|key_len|val_len|[key'\0']|[value]|
|
||||
*---------------------------------*/
|
||||
|
||||
const void* value = &msg[2 + key_len];
|
||||
len = val_len > len ? len : val_len;
|
||||
memcpy(newvalue, value, len);
|
||||
}
|
||||
|
||||
free(msg);
|
||||
return len;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_monitor_close
|
||||
*
|
||||
* Description:
|
||||
* Close a key monitor channel
|
||||
*
|
||||
* Input Parameters:
|
||||
* int fd : file descriptor returned by property_monitor_open()
|
||||
*
|
||||
* Returned Value:
|
||||
* On success returns 0, -errno otherwise.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_monitor_close(int fd)
|
||||
{
|
||||
int ret = close(fd);
|
||||
if (ret < 0)
|
||||
ret = -errno;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_commit
|
||||
*
|
||||
* Description:
|
||||
* Actively commit all property changes
|
||||
*
|
||||
* Input Parameters:
|
||||
* None
|
||||
*
|
||||
* Returned Value:
|
||||
* On success returns 0.
|
||||
* On failure returns -errno.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_commit(void)
|
||||
{
|
||||
int fd = property_connect();
|
||||
int ret;
|
||||
int value;
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
ret = send(fd, "C", 1, 0);
|
||||
if (ret < 0) {
|
||||
KVERR("send error %d\n", errno);
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = recv(fd, &value, sizeof(value), 0);
|
||||
if (ret < sizeof(value)) {
|
||||
KVERR("recv error %d, ret %d\n", errno, ret);
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = value;
|
||||
if (ret < 0) {
|
||||
KVERR("commit error %d\n", ret);
|
||||
}
|
||||
|
||||
out:
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_load
|
||||
*
|
||||
* Description:
|
||||
* load default property value
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* path: file path
|
||||
*
|
||||
* Returned Value:
|
||||
* On success returns 0.
|
||||
* On failure returns -errno.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_load(const char* path)
|
||||
{
|
||||
size_t file_len;
|
||||
int ret;
|
||||
int fd;
|
||||
|
||||
if (path == NULL)
|
||||
path = "";
|
||||
|
||||
file_len = strlen(path) + 1;
|
||||
fd = property_connect();
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
char cmd[2] = {
|
||||
'R', file_len
|
||||
};
|
||||
|
||||
struct iovec iov[2] = {
|
||||
{ .iov_base = cmd, .iov_len = 2 },
|
||||
{ .iov_base = (char*)path, .iov_len = file_len },
|
||||
};
|
||||
|
||||
struct msghdr msg = { 0 };
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 2;
|
||||
|
||||
ret = sendmsg(fd, &msg, 0) > 0 ? 0 : -errno;
|
||||
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_exit
|
||||
*
|
||||
* Description:
|
||||
* Exit Kvdb Server
|
||||
*
|
||||
* Input Parameters:
|
||||
* None
|
||||
*
|
||||
* Returned Value:
|
||||
* On success returns 0.
|
||||
* On failure returns -errno.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_exit(void)
|
||||
{
|
||||
int fd = property_connect();
|
||||
int ret;
|
||||
int value;
|
||||
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
ret = send(fd, "E", 1, 0);
|
||||
if (ret < 0) {
|
||||
KVERR("send error %d\n", errno);
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = recv(fd, &value, sizeof(value), 0);
|
||||
if (ret < sizeof(value)) {
|
||||
KVERR("recv error %d, ret %d\n", errno, ret);
|
||||
ret = -errno;
|
||||
}
|
||||
|
||||
out:
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,639 @@
|
|||
/*
|
||||
* Copyright (C) 2023 Xiaomi Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <kvdb.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Private Types
|
||||
****************************************************************************/
|
||||
|
||||
struct property_list_arg {
|
||||
void* cookie;
|
||||
void (*propfn)(const char* key, const char* value, void* cookie);
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
static void property_list_fn(const char* key, const void* value, size_t val_len, void* cookie)
|
||||
{
|
||||
struct property_list_arg* list = (struct property_list_arg*)cookie;
|
||||
*((char*)value + val_len) = '\0';
|
||||
list->propfn(key, value, list->cookie);
|
||||
}
|
||||
|
||||
static inline char nibble2ascii(unsigned char nibble)
|
||||
{
|
||||
if (nibble < 10)
|
||||
return '0' + nibble;
|
||||
else
|
||||
return 'a' + nibble - 10;
|
||||
}
|
||||
|
||||
static inline int ascii2nibble(char ascii)
|
||||
{
|
||||
if (isdigit(ascii))
|
||||
return ascii - '0';
|
||||
else if (isxdigit(ascii))
|
||||
return tolower(ascii) - 'a' + 10;
|
||||
else
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_set_
|
||||
*
|
||||
* Description:
|
||||
* Store Key-Values to database.
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
* const char* value: entry value string
|
||||
*
|
||||
* Returned Value:
|
||||
* 0: success
|
||||
* <0: failure during execution
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int property_set_(const char* key, const char* value, bool oneway)
|
||||
{
|
||||
if (!value)
|
||||
value = "";
|
||||
|
||||
/* in environment variable? */
|
||||
if (getenv(key)) {
|
||||
int ret = setenv(key, value, 1);
|
||||
if (ret < 0)
|
||||
ret = -errno;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return property_set_binary(key, value, strlen(value) + 1, oneway);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_set
|
||||
*
|
||||
* Description:
|
||||
* Store Key-Values to database.
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
* const char* value: entry value string
|
||||
*
|
||||
* Returned Value:
|
||||
* 0: success
|
||||
* <0: failure during execution
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_set(const char* key, const char* value)
|
||||
{
|
||||
return property_set_(key, value, false);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_get_with_err
|
||||
*
|
||||
* Description:
|
||||
* Retrieve Key-Values from database.
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
* char* value: not NULL : pointer to string buffer
|
||||
* NULL : check whether this [key, value] exists
|
||||
*
|
||||
* Returned Value:
|
||||
* On success returns the length of the value which will never be greater
|
||||
* than PROP_NAME_MAX - 1 and will always be zero terminated.
|
||||
* (the length does not include the terminating zero).
|
||||
* On failure returns negative value.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_get_with_err(const char* key, char* value)
|
||||
{
|
||||
/* in environment variable? */
|
||||
const char* env = getenv(key);
|
||||
if (env) {
|
||||
size_t len = strlen(env);
|
||||
if (len >= PROP_VALUE_MAX)
|
||||
return -E2BIG;
|
||||
|
||||
if (value)
|
||||
memcpy(value, env, len + 1);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
ssize_t ret = property_get_binary(key, value, PROP_VALUE_MAX);
|
||||
if (ret == 0) {
|
||||
return -ENODATA;
|
||||
} else if (ret > 0) {
|
||||
*(value + ret) = '\0';
|
||||
return strlen(value);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_get
|
||||
*
|
||||
* Description:
|
||||
* Retrieve Key-Values from database.
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
* char* value: not NULL : pointer to string buffer
|
||||
* NULL : check whether this [key, value] exists
|
||||
* const char* default_value: the value to return on failure
|
||||
*
|
||||
* Returned Value:
|
||||
* On success returns the length of the value which will never be greater
|
||||
* than PROP_NAME_MAX - 1 and will always be zero terminated.
|
||||
* (the length does not include the terminating zero).
|
||||
* On failure returns length of default_value.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_get(const char* key, char* value, const char* default_value)
|
||||
{
|
||||
int ret = property_get_with_err(key, value);
|
||||
if (ret <= 0) {
|
||||
if (!default_value)
|
||||
return -EINVAL;
|
||||
size_t len = strlen(default_value);
|
||||
if (value)
|
||||
memcpy(value, default_value, len + 1);
|
||||
return len;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_list
|
||||
*
|
||||
* Description:
|
||||
* List all KVs in every database and calls callback function.
|
||||
*
|
||||
* Input Parameters:
|
||||
* property_callback propfn: callback function
|
||||
* void* cookie: cookie data to pass to callback function
|
||||
*
|
||||
* Returned Value:
|
||||
* Returns 0 on success, <0 if all databases failed to open.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_list(void (*propfn)(const char* key, const char* value, void* cookie), void* cookie)
|
||||
{
|
||||
struct property_list_arg list;
|
||||
list.cookie = cookie;
|
||||
list.propfn = propfn;
|
||||
return property_list_binary(property_list_fn, &list);
|
||||
}
|
||||
|
||||
int property_set_oneway(const char* key, const char* value)
|
||||
{
|
||||
return property_set_(key, value, true);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_set_bool
|
||||
*
|
||||
* Description:
|
||||
* Saves a boolean to database.
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
* int8_t value: entry value
|
||||
*
|
||||
* Returned Value:
|
||||
* 0: success
|
||||
* <0: failure during execution
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int property_set_bool_(const char* key, int8_t value, bool oneway)
|
||||
{
|
||||
return property_set_(key, value ? "true" : "false", oneway);
|
||||
}
|
||||
|
||||
int property_set_bool(const char* key, int8_t value)
|
||||
{
|
||||
return property_set_bool_(key, value, false);
|
||||
}
|
||||
|
||||
int property_set_bool_oneway(const char* key, int8_t value)
|
||||
{
|
||||
return property_set_bool_(key, value, true);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_get_bool_with_err
|
||||
*
|
||||
* Description:
|
||||
* Retrieve a Key-Value from backend and interpret the value as boolean.
|
||||
* This is modified from Android libcutils:
|
||||
* https://android.googlesource.com/platform/system/core/+/master/libcutils/
|
||||
* properties.cpp
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
* int8_t* value: the entry value
|
||||
*
|
||||
* Returned Value:
|
||||
* On success returns zero.
|
||||
* On failure returns non-zero.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_get_bool_with_err(const char* key, int8_t* value)
|
||||
{
|
||||
char buf[PROP_VALUE_MAX];
|
||||
|
||||
if (value == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
int len = property_get_with_err(key, buf);
|
||||
if (len < 0)
|
||||
return len;
|
||||
if (len == 1) {
|
||||
char ch = buf[0];
|
||||
if (ch == '0' || ch == 'n') {
|
||||
*value = 0;
|
||||
return 0;
|
||||
} else if (ch == '1' || ch == 'y') {
|
||||
*value = 1;
|
||||
return 0;
|
||||
}
|
||||
} else if (len > 1) {
|
||||
if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) {
|
||||
*value = 0;
|
||||
return 0;
|
||||
} else if (!strcmp(buf, "yes") || !strcmp(buf, "true") || !strcmp(buf, "on")) {
|
||||
*value = 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_get_bool
|
||||
*
|
||||
* Description:
|
||||
* Retrieve a Key-Value from backend and interpret the value as boolean.
|
||||
* This is taken from Android libcutils:
|
||||
* https://android.googlesource.com/platform/system/core/+/master/libcutils/
|
||||
* properties.cpp
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
* int8_t default_value: the value to return on failure
|
||||
*
|
||||
* Returned Value:
|
||||
* On success returns a boolean.
|
||||
* On failure returns default_value.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int8_t property_get_bool(const char* key, int8_t default_value)
|
||||
{
|
||||
int8_t value;
|
||||
int ret = property_get_bool_with_err(key, &value);
|
||||
|
||||
return ret ? default_value : value;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_set_int32
|
||||
*
|
||||
* Description:
|
||||
* Saves an 32-bit integer to unqlite backend or nvs backend.
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
* int32_t value: entry value
|
||||
*
|
||||
* Returned Value:
|
||||
* 0: success
|
||||
* <0: failure during execution
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int property_set_int32_(const char* key, int32_t value, bool oneway)
|
||||
{
|
||||
char buf[32];
|
||||
snprintf(buf, 32, "%" PRId32, value);
|
||||
return property_set_(key, buf, oneway);
|
||||
}
|
||||
|
||||
int property_set_int32(const char* key, int32_t value)
|
||||
{
|
||||
return property_set_int32_(key, value, false);
|
||||
}
|
||||
|
||||
int property_set_int32_oneway(const char* key, int32_t value)
|
||||
{
|
||||
return property_set_int32_(key, value, true);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_get_int32_with_err
|
||||
*
|
||||
* Description:
|
||||
* Retrieve a Key-Value from backend and interpret the value as int32_t.
|
||||
* This is modified from Android libcutils:
|
||||
* https://android.googlesource.com/platform/system/core/+/master/libcutils/
|
||||
* properties.cpp
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
* int32_t* value: the entry value
|
||||
*
|
||||
* Returned Value:
|
||||
* On success returns zero.
|
||||
* On failure returns non-zero.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_get_int32_with_err(const char* key, int32_t* value)
|
||||
{
|
||||
char buf[PROP_VALUE_MAX];
|
||||
|
||||
if (value == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
int32_t ret = property_get_with_err(key, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
errno = 0;
|
||||
char* end;
|
||||
ret = strtol(buf, &end, 0);
|
||||
if (errno || *end || buf == end)
|
||||
return -ENODATA;
|
||||
|
||||
*value = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_get_int32
|
||||
*
|
||||
* Description:
|
||||
* Retrieve a Key-Value from backend and interpret the value as int32_t.
|
||||
* This is modified from Android libcutils:
|
||||
* https://android.googlesource.com/platform/system/core/+/master/libcutils/
|
||||
* properties.cpp
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
* int32_t default_value: the value to return on failure
|
||||
*
|
||||
* Returned Value:
|
||||
* On success returns a int32_t.
|
||||
* On failure returns default_value.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int32_t property_get_int32(const char* key, int32_t default_value)
|
||||
{
|
||||
int32_t value;
|
||||
int ret = property_get_int32_with_err(key, &value);
|
||||
|
||||
return ret ? default_value : value;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_set_int64
|
||||
*
|
||||
* Description:
|
||||
* Saves an 64-bit integer to unqlite backend or nvs backend.
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
* int64_t value: entry value
|
||||
*
|
||||
* Returned Value:
|
||||
* 0: success
|
||||
* <0: failure during execution
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int property_set_int64_(const char* key, int64_t value, bool oneway)
|
||||
{
|
||||
char buf[32];
|
||||
snprintf(buf, 32, "%" PRId64, value);
|
||||
return property_set_(key, buf, oneway);
|
||||
}
|
||||
|
||||
int property_set_int64(const char* key, int64_t value)
|
||||
{
|
||||
return property_set_int64_(key, value, false);
|
||||
}
|
||||
|
||||
int property_set_int64_oneway(const char* key, int64_t value)
|
||||
{
|
||||
return property_set_int64_(key, value, true);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_get_int64_with_err
|
||||
*
|
||||
* Description:
|
||||
* Retrieve a Key-Value from backend and interpret the value as int64_t.
|
||||
* This is modified from Android libcutils:
|
||||
* https://android.googlesource.com/platform/system/core/+/master/libcutils/
|
||||
* properties.cpp
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
* int64_t* value: the entry value
|
||||
*
|
||||
* Returned Value:
|
||||
* On success returns zero.
|
||||
* On failure returns non-zero.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_get_int64_with_err(const char* key, int64_t* value)
|
||||
{
|
||||
char buf[PROP_VALUE_MAX];
|
||||
|
||||
if (value == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
int64_t ret = property_get_with_err(key, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
errno = 0;
|
||||
char* end;
|
||||
ret = strtoll(buf, &end, 0);
|
||||
if (errno || *end || buf == end)
|
||||
return -ENODATA;
|
||||
|
||||
*value = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_get_int64
|
||||
*
|
||||
* Description:
|
||||
* Retrieve a Key-Value from backend and interpret the value as int64_t.
|
||||
* This is modified from Android libcutils:
|
||||
* https://android.googlesource.com/platform/system/core/+/master/libcutils/
|
||||
* properties.cpp
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
* int64_t default_value: the value to return on failure
|
||||
*
|
||||
* Returned Value:
|
||||
* On success returns a int64_t.
|
||||
* On failure returns default_value.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int64_t property_get_int64(const char* key, int64_t default_value)
|
||||
{
|
||||
int64_t value;
|
||||
int ret = property_get_int64_with_err(key, &value);
|
||||
|
||||
return ret ? default_value : value;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_set_buffer
|
||||
*
|
||||
* Description:
|
||||
* Saves a binary buffer to unqlite backend or nvs backend.
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
* const void* value: buffer value
|
||||
* size_t size: buffer size
|
||||
*
|
||||
* Returned Value:
|
||||
* 0: success
|
||||
* <0: failure during execution
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int property_set_buffer_(const char* key, const void* value,
|
||||
size_t size, bool oneway)
|
||||
{
|
||||
size_t buf_size = 2 * size;
|
||||
if (buf_size >= PROP_VALUE_MAX)
|
||||
return -E2BIG;
|
||||
|
||||
const unsigned char* tmp = value;
|
||||
char buf[PROP_VALUE_MAX];
|
||||
size_t i = 0;
|
||||
|
||||
while (i < buf_size) {
|
||||
buf[i++] = nibble2ascii(*tmp >> 4);
|
||||
buf[i++] = nibble2ascii(*tmp++ & 0x0f);
|
||||
}
|
||||
|
||||
buf[i] = '\0';
|
||||
return property_set_(key, buf, oneway);
|
||||
}
|
||||
|
||||
int property_set_buffer(const char* key, const void* value, size_t size)
|
||||
{
|
||||
return property_set_buffer_(key, value, size, false);
|
||||
}
|
||||
|
||||
int property_set_buffer_oneway(const char* key, const void* value, size_t size)
|
||||
{
|
||||
return property_set_buffer_(key, value, size, false);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_get_buffer
|
||||
*
|
||||
* Description:
|
||||
* Retrieve a Key-Value from backend and interpret as binary buffer.
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
* void* value: buffer value
|
||||
* size_t size: buffer size
|
||||
*
|
||||
* Returned Value:
|
||||
* On success returns buffer length.
|
||||
* On failure returns -errno.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
ssize_t property_get_buffer(const char* key, void* value, size_t size)
|
||||
{
|
||||
char buf[PROP_VALUE_MAX];
|
||||
size_t buf_size = 2 * size;
|
||||
int ret = property_get(key, buf, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
char* tmp = value;
|
||||
size_t i = 0;
|
||||
|
||||
while (buf[i]) {
|
||||
if (i >= buf_size)
|
||||
return -E2BIG;
|
||||
|
||||
ret = ascii2nibble(buf[i++]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*tmp = ret << 4;
|
||||
|
||||
ret = ascii2nibble(buf[i++]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*tmp++ |= ret;
|
||||
}
|
||||
|
||||
return i / 2;
|
||||
}
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
* Copyright (C) 2023 Xiaomi Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <kvdb.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_set_binary
|
||||
*
|
||||
* Description:
|
||||
* Store Key-Values to unqlite backend or nvs backend.
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
* const void* value: entry value string
|
||||
*
|
||||
* Returned Value:
|
||||
* 0: success
|
||||
* <0: failure during execution
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_set_binary(const char* key, const void* value, size_t val_len, bool oneway)
|
||||
{
|
||||
if (!key)
|
||||
return -EINVAL;
|
||||
|
||||
size_t key_len = strlen(key) + 1;
|
||||
if (key_len > PROP_NAME_MAX)
|
||||
return -E2BIG;
|
||||
|
||||
if (val_len == 0 || val_len >= PROP_VALUE_MAX)
|
||||
return -E2BIG;
|
||||
|
||||
struct kvdb* client;
|
||||
int ret = kvdb_init(&client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = kvdb_set(client, key, key_len, value, val_len, false);
|
||||
kvdb_uninit(client);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_get_binary
|
||||
*
|
||||
* Description:
|
||||
* Retrieve Key-Values from database.
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
* void* value: not NULL : pointer to string buffer
|
||||
* NULL : check whether this [key, value] exists
|
||||
*
|
||||
* Returned Value:
|
||||
* On success returns the length of the value which will never be greater
|
||||
* than PROP_NAME_MAX.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
ssize_t property_get_binary(const char* key, void* value, size_t val_len)
|
||||
{
|
||||
if (!key)
|
||||
return -E2BIG;
|
||||
|
||||
size_t key_len = strlen(key) + 1;
|
||||
if (key_len > PROP_NAME_MAX)
|
||||
return -E2BIG;
|
||||
|
||||
struct kvdb* client;
|
||||
int ret = kvdb_init(&client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ssize_t len = kvdb_get(client, key, key_len, value, val_len);
|
||||
kvdb_uninit(client);
|
||||
return len;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_delete
|
||||
*
|
||||
* Description:
|
||||
* Delete a KV pair by key
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* key: entry key string
|
||||
*
|
||||
* Returned Value:
|
||||
* 0: success
|
||||
* <0: failure during execution
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_delete(const char* key)
|
||||
{
|
||||
if (!key)
|
||||
return -EINVAL;
|
||||
|
||||
size_t key_len = strlen(key) + 1;
|
||||
if (key_len > PROP_NAME_MAX)
|
||||
return -E2BIG;
|
||||
|
||||
/* in environment variable? */
|
||||
if (getenv(key)) {
|
||||
int ret = unsetenv(key);
|
||||
if (ret < 0)
|
||||
ret = -errno;
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct kvdb* client;
|
||||
int ret = kvdb_init(&client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = kvdb_delete(client, key, key_len);
|
||||
kvdb_uninit(client);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_list
|
||||
*
|
||||
* Description:
|
||||
* List all KVs.
|
||||
*
|
||||
* Input Parameters:
|
||||
* property_callback propfn: callback function
|
||||
* void* cookie: cookie data to pass to callback function
|
||||
*
|
||||
* Returned Value:
|
||||
* Returns 0 on success, <0 if all backend failed to open.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_list_binary(void (*propfn)(const char* key, const void* value, size_t val_len, void* cookie), void* cookie)
|
||||
{
|
||||
struct kvdb* client;
|
||||
int ret = kvdb_init(&client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = kvdb_list(client, propfn, cookie);
|
||||
kvdb_uninit(client);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_commit
|
||||
*
|
||||
* Description:
|
||||
* Actively commit all property changes
|
||||
*
|
||||
* Input Parameters:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_commit(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_load
|
||||
*
|
||||
* Description:
|
||||
* load default property value
|
||||
*
|
||||
* Input Parameters:
|
||||
* const char* path: file path
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_load(const char* path)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: property_exit
|
||||
*
|
||||
* Description:
|
||||
* Exit Kvdb Server (Dummy Function)
|
||||
*
|
||||
* Input Parameters:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int property_exit(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (C) 2020 Xiaomi Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <kvdb.h>
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
return property_exit();
|
||||
}
|
||||
|
|
@ -0,0 +1,330 @@
|
|||
/*
|
||||
* Copyright (C) 2020 Xiaomi Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include "kvdb.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* kvdb_file_genpath
|
||||
****************************************************************************/
|
||||
|
||||
static void kvdb_file_genpath(const char* path, const char* key, char* filepath)
|
||||
{
|
||||
snprintf(filepath, PATH_MAX, "%s/%s", path, key);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* kvdb_file_set
|
||||
****************************************************************************/
|
||||
|
||||
int kvdb_file_set(const char* path, const char* key,
|
||||
const void* value, size_t val_len)
|
||||
{
|
||||
char filepath[PATH_MAX];
|
||||
size_t nbyteswrite = 0;
|
||||
ssize_t result;
|
||||
int fd;
|
||||
|
||||
kvdb_file_genpath(path, key, filepath);
|
||||
fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0666);
|
||||
if (fd < 0) {
|
||||
KVERR("open %s error with %d", filepath, errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
while (nbyteswrite < val_len) {
|
||||
result = write(fd, value, val_len);
|
||||
if (result < 0) {
|
||||
if (result == -EINTR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
KVERR("write %s error with %d", filepath, errno);
|
||||
close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
nbyteswrite += result;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* kvdb_file_get
|
||||
****************************************************************************/
|
||||
|
||||
ssize_t kvdb_file_get(const char* path, const char* key,
|
||||
void* value, size_t val_len)
|
||||
{
|
||||
char filepath[PATH_MAX];
|
||||
size_t nbytesread = 0;
|
||||
ssize_t result;
|
||||
ssize_t ret;
|
||||
int fd;
|
||||
|
||||
kvdb_file_genpath(path, key, filepath);
|
||||
|
||||
if (value == NULL) {
|
||||
|
||||
/* Readonly property only check if exist, no read required. */
|
||||
|
||||
if (access(filepath, O_RDONLY) == 0)
|
||||
return val_len;
|
||||
else
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
fd = open(filepath, O_RDONLY | O_CLOEXEC, 0666);
|
||||
if (fd < 0) {
|
||||
KVERR("open %s error with %d", filepath, errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
do {
|
||||
result = read(fd, value + nbytesread, val_len - nbytesread);
|
||||
if (result < 0) {
|
||||
result = -errno;
|
||||
if (result == -EINTR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
KVERR("read %s error with %d", filepath, errno);
|
||||
ret = -errno;
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
nbytesread += result;
|
||||
} while (result > 0 && nbytesread < val_len);
|
||||
|
||||
ret = nbytesread;
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* kvdb_file_list
|
||||
****************************************************************************/
|
||||
|
||||
int kvdb_file_list(const char* path, kvdb_consume consume, void* cookie)
|
||||
{
|
||||
char value[PROP_VALUE_MAX];
|
||||
struct dirent* entry;
|
||||
int ret = 0;
|
||||
DIR* dir;
|
||||
|
||||
dir = opendir(path);
|
||||
if (!dir) {
|
||||
KVERR("opendir %s error with %d", path, errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (entry->d_type != DT_REG) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = kvdb_file_get(path, entry->d_name, value, PROP_VALUE_MAX);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
break;
|
||||
}
|
||||
|
||||
consume(entry->d_name, value, ret, cookie);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* kvdb_file_delete
|
||||
****************************************************************************/
|
||||
|
||||
int kvdb_file_delete(const char* path, const char* key)
|
||||
{
|
||||
char filepath[PATH_MAX];
|
||||
|
||||
kvdb_file_genpath(path, key, filepath);
|
||||
return unlink(filepath);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KVDB_FILE
|
||||
/****************************************************************************
|
||||
* Name: kvdb_persist_init
|
||||
*
|
||||
* Description:
|
||||
* init resource of nvs .
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save filekv instance.
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int kvdb_persist_init(struct kvdb** kvdb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kvdb_persist_uninit
|
||||
*
|
||||
* Description:
|
||||
* init resource of filekv .
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save filekv instance.
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void kvdb_persist_uninit(struct kvdb* kvdb)
|
||||
{
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kvdb_persist_set
|
||||
*
|
||||
* Description:
|
||||
* key-value set.
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save filekv instance.
|
||||
* key - Pointer to key to set.
|
||||
* key_len - the length of the key
|
||||
* value - Pointer to data to be saved
|
||||
* val_len - the length of the value
|
||||
* force - unused parameter
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int kvdb_persist_set(struct kvdb* kvdb, const char* key, size_t key_len,
|
||||
const void* value, size_t val_len, bool force)
|
||||
{
|
||||
return kvdb_file_set(CONFIG_KVDB_PERSIST_PATH, key, value, val_len);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kvdb_persist_get
|
||||
*
|
||||
* Description:
|
||||
* key-value get.
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save filekv instance.
|
||||
* key - Pointer to key to get.
|
||||
* key_len - the length of the key
|
||||
* value - Pointer to data to be get
|
||||
* val_len - the length of the value
|
||||
*
|
||||
* Returned Value:
|
||||
* the length of value, > 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
ssize_t kvdb_persist_get(struct kvdb* kvdb, const char* key, size_t key_len,
|
||||
void* value, size_t val_len)
|
||||
{
|
||||
return kvdb_file_get(CONFIG_KVDB_PERSIST_PATH, key, value, val_len);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kvdb_persist_delete
|
||||
*
|
||||
* Description:
|
||||
* key-value delete.
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save filekv instance.
|
||||
* key - Pointer to key to delete.
|
||||
* key_len - the length of the key
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int kvdb_persist_delete(struct kvdb* kvdb, const char* key, size_t key_len)
|
||||
{
|
||||
return kvdb_file_delete(CONFIG_KVDB_PERSIST_PATH, key);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kvdb_persist_list
|
||||
*
|
||||
* key-value list.
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save filekv instance.
|
||||
* consume - callback when key-value fetch success.
|
||||
* cookie - private data for consume callback.
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int kvdb_persist_list(struct kvdb* kvdb, kvdb_consume consume, void* cookie)
|
||||
{
|
||||
return kvdb_file_list(CONFIG_KVDB_PERSIST_PATH, consume, cookie);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kvdb_persist_commit
|
||||
*
|
||||
* key-value commit.
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save filekv instance.
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int kvdb_persist_commit(struct kvdb* kvdb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (C) 2020 Xiaomi Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <kvdb.h>
|
||||
|
||||
static void callback(const char* name, const void* value, size_t val_len, void* cookie)
|
||||
{
|
||||
const char* temp = value;
|
||||
ssize_t i;
|
||||
|
||||
for (i = 0; i < val_len; i++) {
|
||||
if (!isprint(temp[i]))
|
||||
break;
|
||||
}
|
||||
|
||||
if (name != NULL)
|
||||
printf("%s: ", name);
|
||||
|
||||
/* All previous characters can be printed and ending '\0' */
|
||||
if (i == val_len - 1 && temp[i] == '\0') {
|
||||
printf("%s\n", temp);
|
||||
} else {
|
||||
for (i = 0; i < val_len; i++)
|
||||
printf("%02x", temp[i]);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (argc == 2 && strncmp(argv[1], "-h", 3)) {
|
||||
char buf[PROP_VALUE_MAX];
|
||||
|
||||
ssize_t len = property_get_binary(argv[1], buf, sizeof(buf));
|
||||
if (len < 0)
|
||||
return len;
|
||||
|
||||
callback(NULL, buf, len, NULL);
|
||||
}
|
||||
#ifdef CONFIG_KVDB_DUMPLIST
|
||||
else if (argc == 1)
|
||||
ret = -property_list_binary(callback, NULL);
|
||||
#endif
|
||||
else
|
||||
printf("Usage: %s [key]\n", argv[0]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (C) 2023 Xiaomi Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __INTERNAL_H
|
||||
#define __INTERNAL_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#define KVLOG(level, fmt, ...) \
|
||||
syslog(level, "[kvdb] [%s:%d] " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
|
||||
#if defined(CONFIG_KVDB_LOG_INFO)
|
||||
#define KVINFO(fmt, ...) KVLOG(LOG_INFO, fmt, ##__VA_ARGS__)
|
||||
#define KVWARN(fmt, ...) KVLOG(LOG_WARNING, fmt, ##__VA_ARGS__)
|
||||
#define KVERR(fmt, ...) KVLOG(LOG_ERR, fmt, ##__VA_ARGS__)
|
||||
#elif defined(CONFIG_KVDB_LOG_WARN)
|
||||
#define KVINFO(fmt, ...)
|
||||
#define KVWARN(fmt, ...) KVLOG(LOG_WARNING, fmt, ##__VA_ARGS__)
|
||||
#define KVERR(fmt, ...) KVLOG(LOG_ERR, fmt, ##__VA_ARGS__)
|
||||
#elif defined(CONFIG_KVDB_LOG_ERR)
|
||||
#define KVINFO(fmt, ...)
|
||||
#define KVWARN(fmt, ...)
|
||||
#define KVERR(fmt, ...) KVLOG(LOG_ERR, fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define KVINFO(fmt, ...)
|
||||
#define KVWARN(fmt, ...)
|
||||
#define KVERR(fmt, ...)
|
||||
#endif
|
||||
|
||||
#define PROP_SERVER_PATH "kvdbd"
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PERSIST_LABEL "persist."
|
||||
#define PERSIST_LABEL_LEN 8
|
||||
|
||||
/****************************************************************************
|
||||
* Public Type Definitions
|
||||
****************************************************************************/
|
||||
|
||||
enum {
|
||||
KVDB_PERSIST, /* save key-value pairs in flash */
|
||||
#ifdef CONFIG_KVDB_TEMPORARY_STORAGE
|
||||
KVDB_MEM, /* save key-value pairs in memory */
|
||||
#endif
|
||||
};
|
||||
|
||||
struct kvdb;
|
||||
|
||||
typedef void (*kvdb_consume)(const char* key, const void* value, size_t val_len, void* cookie);
|
||||
|
||||
int kvdb_set(struct kvdb* kvdb, const char* key, size_t key_len, const void* value, size_t val_len, bool force);
|
||||
ssize_t kvdb_get(struct kvdb* kvdb, const char* key, size_t key_len, void* value, size_t val_len);
|
||||
int kvdb_delete(struct kvdb* kvdb, const char* key, size_t key_len);
|
||||
int kvdb_list(struct kvdb* kvdb, kvdb_consume consume, void* cookie);
|
||||
int kvdb_commit(struct kvdb* kvdb);
|
||||
int kvdb_init(struct kvdb** kvdb);
|
||||
void kvdb_uninit(struct kvdb* kvdb);
|
||||
|
||||
int kvdb_persist_init(struct kvdb** kvdb);
|
||||
void kvdb_persist_uninit(struct kvdb* kvdb);
|
||||
int kvdb_persist_commit(struct kvdb* kvdb);
|
||||
int kvdb_persist_list(struct kvdb* kvdb, kvdb_consume consume, void* cookie);
|
||||
ssize_t kvdb_persist_get(struct kvdb* kvdb, const char* key, size_t key_len, void* value, size_t val_len);
|
||||
int kvdb_persist_set(struct kvdb* kvdb, const char* key, size_t key_len, const void* value, size_t val_len, bool force);
|
||||
int kvdb_persist_delete(struct kvdb* kvdb, const char* key, size_t key_len);
|
||||
|
||||
#ifdef CONFIG_KVDB_TEMPORARY_STORAGE
|
||||
int kvdb_file_set(const char* path, const char* key, const void* value, size_t val_len);
|
||||
ssize_t kvdb_file_get(const char* path, const char* key, void* value, size_t val_len);
|
||||
int kvdb_file_list(const char* path, kvdb_consume consume, void* cookie);
|
||||
int kvdb_file_delete(const char* path, const char* key);
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __INTERNAL_H */
|
||||
|
|
@ -0,0 +1,322 @@
|
|||
/*
|
||||
* Copyright (C) 2020 Xiaomi Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <nuttx/mtd/configdata.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include "kvdb.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Private Type Definitions
|
||||
****************************************************************************/
|
||||
|
||||
struct kvdb {
|
||||
int fd;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
static inline const char* kvdb_skip_prefix(const char* key)
|
||||
{
|
||||
return key + PERSIST_LABEL_LEN;
|
||||
}
|
||||
|
||||
static void kvdb_add_prefix(char* out, size_t outlen, const char* in)
|
||||
{
|
||||
strlcpy(out, PERSIST_LABEL, outlen);
|
||||
strlcat(out, in, outlen);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kvdb_persist_init
|
||||
*
|
||||
* Description:
|
||||
* init resource of nvs .
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save nvs kvdb instance.
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int kvdb_persist_init(struct kvdb** kvdb)
|
||||
{
|
||||
struct kvdb* handle;
|
||||
int ret;
|
||||
|
||||
handle = (struct kvdb*)zalloc(sizeof(struct kvdb));
|
||||
if (handle == NULL) {
|
||||
KVERR("kvdb init error !\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = open(CONFIG_KVDB_PERSIST_PATH, O_RDWR | O_CLOEXEC);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
KVERR("open %s error with %d", CONFIG_KVDB_PERSIST_PATH, ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
handle->fd = ret;
|
||||
*kvdb = handle;
|
||||
return 0;
|
||||
|
||||
err:
|
||||
free(handle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kvdb_persist_uninit
|
||||
*
|
||||
* Description:
|
||||
* init resource of nvs .
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save nvs kvdb instance.
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void kvdb_persist_uninit(struct kvdb* kvdb)
|
||||
{
|
||||
close(kvdb->fd);
|
||||
free(kvdb);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kvdb_persist_set
|
||||
*
|
||||
* Description:
|
||||
* key-value set.
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save nvs kvdb instance.
|
||||
* key - Pointer to key to set.
|
||||
* key_len - the length of the key
|
||||
* value - Pointer to data to be saved
|
||||
* val_len - the length of the value
|
||||
* force - unused parameter
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int kvdb_persist_set(struct kvdb* kvdb, const char* key, size_t key_len,
|
||||
const void* value, size_t val_len, bool force)
|
||||
{
|
||||
struct config_data_s data;
|
||||
int ret;
|
||||
|
||||
key = kvdb_skip_prefix(key);
|
||||
|
||||
if (strlen(key) >= sizeof(data.name))
|
||||
return -EINVAL;
|
||||
|
||||
strlcpy(data.name, key, sizeof(data.name));
|
||||
|
||||
data.len = val_len;
|
||||
data.configdata = (uint8_t*)value;
|
||||
|
||||
ret = ioctl(kvdb->fd, CFGDIOC_SETCONFIG, &data);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
KVERR("IOCTL_SETCONFIG ERROR %d", ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kvdb_persist_get
|
||||
*
|
||||
* Description:
|
||||
* key-value get.
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save nvs kvdb instance.
|
||||
* key - Pointer to key to get.
|
||||
* key_len - the length of the key
|
||||
* value - Pointer to data to be get
|
||||
* val_len - the length of the value
|
||||
*
|
||||
* Returned Value:
|
||||
* the length of value, > 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
ssize_t kvdb_persist_get(struct kvdb* kvdb, const char* key, size_t key_len, void* value, size_t val_len)
|
||||
{
|
||||
struct config_data_s data;
|
||||
int ret;
|
||||
|
||||
if (key == NULL || key_len == 0)
|
||||
return -EINVAL;
|
||||
|
||||
key = kvdb_skip_prefix(key);
|
||||
|
||||
if (strlen(key) >= sizeof(data.name))
|
||||
return -EINVAL;
|
||||
|
||||
strlcpy(data.name, key, sizeof(data.name));
|
||||
data.configdata = (uint8_t*)value;
|
||||
data.len = val_len;
|
||||
|
||||
ret = ioctl(kvdb->fd, CFGDIOC_GETCONFIG, &data);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
KVERR("CFGDIOC_GETCONFIG ERROR: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return data.len;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kvdb_persist_delete
|
||||
*
|
||||
* Description:
|
||||
* key-value delete.
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save nvs kvdb instance.
|
||||
* key - Pointer to key to delete.
|
||||
* key_len - the length of the key
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int kvdb_persist_delete(struct kvdb* kvdb, const char* key, size_t key_len)
|
||||
{
|
||||
struct config_data_s data;
|
||||
int ret;
|
||||
|
||||
key = kvdb_skip_prefix(key);
|
||||
|
||||
if (strlen(key) >= sizeof(data.name))
|
||||
return -EINVAL;
|
||||
|
||||
strlcpy(data.name, key, sizeof(data.name));
|
||||
|
||||
ret = ioctl(kvdb->fd, CFGDIOC_DELCONFIG, &data);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
KVERR("CFGDIOC_DELCONFIG ERROR: %d", ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kvdb_persist_list
|
||||
*
|
||||
* Description:
|
||||
* key-value list.
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save nvs kvdb instance.
|
||||
* consume - callback when key-value fetch success.
|
||||
* cookie - private data for consume callback.
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int kvdb_persist_list(struct kvdb* kvdb, kvdb_consume consume, void* cookie)
|
||||
{
|
||||
char key[CONFIG_NAME_MAX + PERSIST_LABEL_LEN];
|
||||
uint8_t buf[PROP_VALUE_MAX];
|
||||
struct config_data_s data;
|
||||
int ret;
|
||||
|
||||
if (!consume)
|
||||
return 0;
|
||||
|
||||
data.configdata = buf;
|
||||
data.len = PROP_VALUE_MAX;
|
||||
ret = ioctl(kvdb->fd, CFGDIOC_FIRSTCONFIG, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
kvdb_add_prefix(key, sizeof(key), data.name);
|
||||
|
||||
consume(key, data.configdata, data.len, cookie);
|
||||
|
||||
while (1) {
|
||||
data.configdata = buf;
|
||||
data.len = PROP_VALUE_MAX;
|
||||
ret = ioctl(kvdb->fd, CFGDIOC_NEXTCONFIG, &data);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
|
||||
/* ENOENT is expected when there are no more entries */
|
||||
|
||||
if (ret == -ENOENT)
|
||||
ret = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
kvdb_add_prefix(key, sizeof(key), data.name);
|
||||
|
||||
consume(key, data.configdata, data.len, cookie);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kvdb_persist_commit
|
||||
*
|
||||
* key-value commit.
|
||||
*
|
||||
* Input Parameters:
|
||||
* kvdb - Pointer to save filekv instance.
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success, -ERRNO errno code if error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int kvdb_persist_commit(struct kvdb* kvdb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,264 @@
|
|||
/****************************************************************************
|
||||
* frameworks/utils/kvdb/qemu_properties.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <kvdb.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define MAX_TRIES 5
|
||||
#define BUFF_SIZE (PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX + 2)
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
static int
|
||||
qemu_pipe_open_ns(const char* ns, const char* pipe_name, int flags)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
int buf_len;
|
||||
|
||||
int fd = open("/dev/goldfish_pipe", flags);
|
||||
if (fd < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (ns) {
|
||||
buf_len = snprintf(buf, sizeof(buf), "pipe:%s:%s", ns, pipe_name);
|
||||
} else {
|
||||
buf_len = snprintf(buf, sizeof(buf), "pipe:%s", pipe_name);
|
||||
}
|
||||
|
||||
if (write(fd, buf, buf_len + 1) < 0) {
|
||||
fprintf(stderr, "%s:%d: Could not connect to the '%s' service: %s\n",
|
||||
__func__, __LINE__, buf, strerror(errno));
|
||||
close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int
|
||||
qemud_channel_send(int pipe, const void* msg, int size)
|
||||
{
|
||||
char header[5];
|
||||
|
||||
if (size < 0) {
|
||||
size = strlen(msg);
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
snprintf(header, sizeof(header), "%04x", size);
|
||||
if (write(pipe, header, 4) < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (write(pipe, msg, size) < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int
|
||||
qemud_channel_recv(int pipe, void* msg, int maxsize)
|
||||
{
|
||||
char header[5];
|
||||
int size;
|
||||
|
||||
if (read(pipe, header, 4) < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
header[4] = 0;
|
||||
|
||||
if (sscanf(header, "%04x", &size) != 1) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (size > maxsize) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (read(pipe, msg, size) < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void
|
||||
misc_pipe_close(int* fd)
|
||||
{
|
||||
if (*fd >= 0) {
|
||||
close(*fd);
|
||||
*fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
misc_pipe_send(int* fd, const char* msg)
|
||||
{
|
||||
if (*fd < 0) {
|
||||
*fd = qemu_pipe_open_ns(NULL, "QemuMiscPipe", O_RDWR);
|
||||
if (*fd < 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t cmd_len = strlen(msg) + 1;
|
||||
write(*fd, &cmd_len, sizeof(cmd_len));
|
||||
write(*fd, msg, cmd_len);
|
||||
|
||||
int r = read(*fd, &cmd_len, sizeof(cmd_len));
|
||||
if (r < 0 || cmd_len < 0) {
|
||||
misc_pipe_close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
while (cmd_len > 0) {
|
||||
char buf[64];
|
||||
const size_t chunk = cmd_len < sizeof(buf) ? cmd_len : sizeof(buf);
|
||||
r = read(*fd, buf, chunk);
|
||||
if (r < 0) {
|
||||
misc_pipe_close(fd);
|
||||
return;
|
||||
} else {
|
||||
cmd_len -= chunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int qemud_fd;
|
||||
|
||||
/* try to connect to the qemud service */
|
||||
|
||||
int tries = MAX_TRIES;
|
||||
|
||||
while (true) {
|
||||
qemud_fd = qemu_pipe_open_ns("qemud", "boot-properties", O_RDWR);
|
||||
if (qemud_fd >= 0)
|
||||
break;
|
||||
|
||||
if (--tries <= 0) {
|
||||
fprintf(stderr,
|
||||
"Could not connect after too many tries. Aborting\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
fprintf(stderr, "waiting 1s to wait for qemud.\n");
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
fprintf(stderr, "connected to \"boot-properties\" qemud service.\n");
|
||||
|
||||
/* send the 'list' command to the service */
|
||||
|
||||
if (qemud_channel_send(qemud_fd, "list", -1) < 0) {
|
||||
fprintf(stderr,
|
||||
"could not send command to \"boot-properties\" service\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* read each system property as a single line from the service,
|
||||
* until exhaustion.
|
||||
*/
|
||||
|
||||
while (true) {
|
||||
char* prop_key;
|
||||
char* prop_value;
|
||||
char temp[BUFF_SIZE];
|
||||
int len = qemud_channel_recv(qemud_fd, temp, sizeof(temp) - 1);
|
||||
|
||||
/* lone NUL-byte signals end of properties */
|
||||
|
||||
if (len < 0 || len > (BUFF_SIZE - 1) || !temp[0]) {
|
||||
break;
|
||||
}
|
||||
|
||||
temp[len] = '\0'; /* zero-terminate string */
|
||||
|
||||
/* separate propery name from value */
|
||||
|
||||
prop_key = temp;
|
||||
prop_value = strchr(temp, '=');
|
||||
if (!prop_value) {
|
||||
continue;
|
||||
}
|
||||
|
||||
*prop_value = '\0';
|
||||
++prop_value;
|
||||
|
||||
fprintf(stderr, "key = %s | value = %s\n", prop_key, prop_value);
|
||||
|
||||
if (strcmp(prop_key, "qemu.sf.lcd_density") == 0) {
|
||||
property_set("ro.sf.lcd_density", prop_value);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (property_set(prop_key, prop_value) < 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
close(qemud_fd);
|
||||
|
||||
qemud_fd = -1;
|
||||
|
||||
do {
|
||||
misc_pipe_send(&qemud_fd, "heartbeat");
|
||||
sleep(5);
|
||||
if (property_get_bool("vendor.qemu.dev.bootcomplete", 0)) {
|
||||
fprintf(stderr, "tell the host boot completed\n");
|
||||
misc_pipe_send(&qemud_fd, "bootcomplete");
|
||||
break;
|
||||
}
|
||||
} while (qemud_fd >= 0);
|
||||
|
||||
while (qemud_fd >= 0) {
|
||||
sleep(30);
|
||||
misc_pipe_send(&qemud_fd, "heartbeat");
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -0,0 +1,650 @@
|
|||
/*
|
||||
* Copyright (C) 2020 Xiaomi Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <fnmatch.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/param.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <netpacket/rpmsg.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <kvdb.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#if defined(CONFIG_NET_LOCAL) && defined(CONFIG_NET_RPMSG)
|
||||
#define KVFD_LOCAL 0
|
||||
#define KVFD_REMOTE 1
|
||||
#define KVFD_COUNT 2
|
||||
#else
|
||||
#define KVFD_LOCAL 0
|
||||
#define KVFD_REMOTE 0
|
||||
#define KVFD_COUNT 1
|
||||
#endif
|
||||
#define KVFD_MAX 8
|
||||
|
||||
typedef struct kvdb_monitor {
|
||||
int fd;
|
||||
LIST_ENTRY(kvdb_monitor)
|
||||
entry;
|
||||
char key[0];
|
||||
} kvdb_monitor;
|
||||
|
||||
typedef LIST_HEAD(kvdb_monitor_head, kvdb_monitor) kvdb_monitor_head;
|
||||
|
||||
typedef struct kvdb_server {
|
||||
struct kvdb* kvdb;
|
||||
int fd[KVFD_COUNT];
|
||||
int efd;
|
||||
bool running;
|
||||
kvdb_monitor_head head;
|
||||
} kvdb_server;
|
||||
|
||||
/* Open a monitor channel, add the [key, fd] pair to the monitor list and
|
||||
* add the pollfd to the pollfd array.
|
||||
*/
|
||||
static int kvdb_monitor_open(kvdb_server* server, int fd, const char* key,
|
||||
size_t key_len)
|
||||
{
|
||||
/* Malloc monitor element to store [key, fd] pair */
|
||||
kvdb_monitor* mon = zalloc(sizeof(kvdb_monitor) + key_len);
|
||||
if (mon == NULL) {
|
||||
KVERR("kvdb_monitor_open: zalloc failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Add the monitor fd to the epoll */
|
||||
struct epoll_event ev = {
|
||||
.data.ptr = &mon->fd,
|
||||
.events = EPOLLIN
|
||||
};
|
||||
int ret = epoll_ctl(server->efd, EPOLL_CTL_ADD, fd, &ev);
|
||||
if (ret < 0) {
|
||||
KVERR("kvdb_monitor_open: epoll_ctl failed, ret=%d, errno=%d\n", ret, errno);
|
||||
free(mon);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Add the [key, fd] pair to the monitor list */
|
||||
mon->fd = fd;
|
||||
strcpy(mon->key, key);
|
||||
LIST_INSERT_HEAD(&server->head, mon, entry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Close the monitor fd, remove the fd from the monitor list and empty
|
||||
* corresponding pollfd.
|
||||
*/
|
||||
static void kvdb_monitor_close(kvdb_server* server, struct epoll_event* ev)
|
||||
{
|
||||
kvdb_monitor* mon = (kvdb_monitor*)ev->data.ptr;
|
||||
|
||||
/* Close the monitor fd and delete it from epoll */
|
||||
epoll_ctl(server->efd, EPOLL_CTL_DEL, mon->fd, NULL);
|
||||
close(mon->fd);
|
||||
|
||||
/* Remove the element from the monitor list */
|
||||
LIST_REMOVE(mon, entry);
|
||||
free(mon);
|
||||
}
|
||||
|
||||
/* Notify the client the value changed (updated or deleted) */
|
||||
static void kvdb_monitor_notify(kvdb_server* server, const char* key, const void* value, size_t val_len)
|
||||
{
|
||||
size_t key_len = strlen(key) + 1;
|
||||
|
||||
/* value != NULL
|
||||
*---------------------------------*
|
||||
| 1 | 1 | key_len |val_len|
|
||||
|---------------------------------|
|
||||
|key_len|val_len|[key'\0']|[value]|
|
||||
*---------------------------------*
|
||||
* value == NULL
|
||||
*-------------------------*
|
||||
| 1 | 1 | key_len |
|
||||
|-------------------------|
|
||||
|key_len| 0 |[key'\0']|
|
||||
*-------------------------*/
|
||||
|
||||
char cmd[2] = { key_len, val_len };
|
||||
struct iovec iov[3] = {
|
||||
{ .iov_base = cmd, .iov_len = 2 },
|
||||
{ .iov_base = (char*)key, .iov_len = key_len },
|
||||
{ .iov_base = (char*)value, .iov_len = val_len },
|
||||
};
|
||||
|
||||
struct msghdr msg = { 0 };
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = value ? 3 : 2;
|
||||
|
||||
kvdb_monitor* mon;
|
||||
kvdb_monitor* tmp;
|
||||
LIST_FOREACH_SAFE(mon, &server->head, entry, tmp)
|
||||
{
|
||||
if (fnmatch(mon->key, key, FNM_NOESCAPE) != 0)
|
||||
continue;
|
||||
|
||||
int space;
|
||||
ioctl(mon->fd, FIONSPACE, &space);
|
||||
if (space < (key_len + val_len + 2)) {
|
||||
/* Check space before monitor notify to avoid deadlock.
|
||||
Cause the send may write buffer full and wait,
|
||||
then the client just get a new key and also wait,
|
||||
deadlock! */
|
||||
KVWARN("monitor space is not enough for key %s\n", key);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sendmsg(mon->fd, &msg, 0) < 0) {
|
||||
/* Client close or some error happends, stop monitor */
|
||||
LIST_REMOVE(mon, entry);
|
||||
epoll_ctl(server->efd, EPOLL_CTL_DEL, mon->fd, NULL);
|
||||
close(mon->fd);
|
||||
free(mon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool kvdb_is_comment(const char* line)
|
||||
{
|
||||
size_t i = strspn(line, " \t\r\n");
|
||||
return line[i] == '\0' || line[i] == '#';
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Network Functions
|
||||
****************************************************************************/
|
||||
|
||||
static int kvdb_load(struct kvdb* kvdb, const char* src, bool force)
|
||||
{
|
||||
char* tmpb;
|
||||
const char* path;
|
||||
const char* sep;
|
||||
int retry = 20;
|
||||
|
||||
char* buf = malloc(PROP_MSG_MAX);
|
||||
if (buf == NULL) {
|
||||
KVERR("malloc failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tmpb = malloc(PATH_MAX);
|
||||
if (tmpb == NULL) {
|
||||
KVERR("malloc failed\n");
|
||||
free(buf);
|
||||
return -ENOMEM;
|
||||
}
|
||||
path = tmpb;
|
||||
|
||||
while (*src) {
|
||||
sep = strchr(src, ';');
|
||||
if (sep) {
|
||||
strlcpy(tmpb, src, MIN(PATH_MAX, sep - src + 1));
|
||||
src = sep + 1;
|
||||
} else {
|
||||
path = src;
|
||||
src += strlen(src);
|
||||
}
|
||||
|
||||
/* Wait filesystem mount success */
|
||||
while (access(path, 0) < 0 && retry-- > 0)
|
||||
usleep(1000);
|
||||
|
||||
FILE* f = fopen(path, "re");
|
||||
if (!f) {
|
||||
KVERR("kvdb open:%s failed, errno:%d\n", path, errno);
|
||||
continue;
|
||||
}
|
||||
|
||||
while (fgets(buf, PROP_MSG_MAX, f)) {
|
||||
if (kvdb_is_comment(buf))
|
||||
continue;
|
||||
|
||||
char* tmp;
|
||||
char* key = strtok_r(buf, "=", &tmp);
|
||||
char* value = strtok_r(NULL, "\n", &tmp);
|
||||
if (!key || !value)
|
||||
continue;
|
||||
|
||||
size_t key_len = strlen(key) + 1;
|
||||
if (!force && kvdb_get(kvdb, key, key_len, NULL, 0) >= 0)
|
||||
continue;
|
||||
|
||||
kvdb_set(kvdb, key, key_len, value, strlen(value) + 1, true);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
kvdb_commit(kvdb);
|
||||
free(tmpb);
|
||||
free(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvdb_bind(int fd[])
|
||||
{
|
||||
const int family[] = {
|
||||
#ifdef CONFIG_NET_LOCAL
|
||||
[KVFD_LOCAL] = AF_UNIX,
|
||||
#endif
|
||||
#ifdef CONFIG_NET_RPMSG
|
||||
[KVFD_REMOTE] = AF_RPMSG,
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_NET_LOCAL
|
||||
const struct sockaddr_un addr0 = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = PROP_SERVER_PATH,
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NET_RPMSG
|
||||
const struct sockaddr_rpmsg addr1 = {
|
||||
.rp_family = AF_RPMSG,
|
||||
.rp_cpu = "",
|
||||
.rp_name = PROP_SERVER_PATH,
|
||||
};
|
||||
#endif
|
||||
|
||||
const struct sockaddr* addr[] = {
|
||||
#ifdef CONFIG_NET_LOCAL
|
||||
[KVFD_LOCAL] = (const struct sockaddr*)&addr0,
|
||||
#endif
|
||||
#ifdef CONFIG_NET_RPMSG
|
||||
[KVFD_REMOTE] = (const struct sockaddr*)&addr1,
|
||||
#endif
|
||||
};
|
||||
|
||||
const socklen_t addrlen[] = {
|
||||
#ifdef CONFIG_NET_LOCAL
|
||||
[KVFD_LOCAL] = sizeof(struct sockaddr_un),
|
||||
#endif
|
||||
#ifdef CONFIG_NET_RPMSG
|
||||
[KVFD_REMOTE] = sizeof(struct sockaddr_rpmsg),
|
||||
#endif
|
||||
};
|
||||
|
||||
memset(fd, 0, sizeof(*fd) * KVFD_COUNT);
|
||||
|
||||
for (int i = 0; i < KVFD_COUNT; i++) {
|
||||
fd[i] = socket(family[i], SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
if (fd[i] < 0) {
|
||||
KVERR("kvdb_bind: socket failed, i=%d, errno=%d\n", i, errno);
|
||||
continue;
|
||||
}
|
||||
|
||||
int ret = bind(fd[i], addr[i], addrlen[i]);
|
||||
if (ret < 0) {
|
||||
KVERR("kvdb_bind: bind failed, i=%d, ret=%d, errno=%d\n", i, ret, errno);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = listen(fd[i], CONFIG_KVDB_BACKLOG_CONNS);
|
||||
if (ret < 0) {
|
||||
KVERR("kvdb_bind: listen failed, i=%d, ret=%d, errno=%d\n", i, ret, errno);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kvdb_unbind(int fd[])
|
||||
{
|
||||
for (int i = 0; i < KVFD_COUNT; i++)
|
||||
if (fd[i] > 0)
|
||||
close(fd[i]);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KVDB_DUMPLIST
|
||||
static void kvdb_list_consume(const char* key, const void* value, size_t val_len, void* cookie)
|
||||
{
|
||||
size_t key_len = strlen(key) + 1;
|
||||
char cmd[2] = {
|
||||
key_len, val_len
|
||||
};
|
||||
|
||||
struct iovec iov[3] = {
|
||||
{ .iov_base = cmd, .iov_len = 2 },
|
||||
{ .iov_base = (char*)key, .iov_len = key_len },
|
||||
{ .iov_base = (char*)value, .iov_len = val_len },
|
||||
};
|
||||
|
||||
struct msghdr msg = { 0 };
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 3;
|
||||
|
||||
int fd = (intptr_t)cookie;
|
||||
sendmsg(fd, &msg, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static ssize_t kvdb_recv(int sockfd, char* buf, size_t offset, size_t len)
|
||||
{
|
||||
while (offset < len) {
|
||||
ssize_t ret = recv(sockfd, buf + offset, len - offset, 0);
|
||||
if (ret < 0) {
|
||||
KVERR("kvdb_recv: recv failed, ret=%zd, errno=%d\n", ret, errno);
|
||||
return ret;
|
||||
}
|
||||
if (ret == 0) {
|
||||
KVERR("kvdb_recv: recv returned 0, connection closed\n");
|
||||
return -ENODATA;
|
||||
}
|
||||
offset += ret;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static bool kvdb_client(kvdb_server* server, int fd)
|
||||
{
|
||||
bool dirty = false;
|
||||
ssize_t len;
|
||||
char* msg;
|
||||
|
||||
#if defined(CONFIG_KVDB_TIMEOUT_INTERVAL) && CONFIG_KVDB_TIMEOUT_INTERVAL > 0
|
||||
struct timeval timeout = {
|
||||
.tv_sec = 0,
|
||||
.tv_usec = CONFIG_KVDB_TIMEOUT_INTERVAL,
|
||||
};
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0
|
||||
|| setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
|
||||
KVWARN("Failed to set socket timeout\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
msg = malloc(PROP_MSG_MAX);
|
||||
if (msg == NULL) {
|
||||
KVERR("kvdb_client: malloc failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
msg[0] = msg[1] = msg[2] = 0; /* zero the first key bytes */
|
||||
|
||||
len = recv(fd, msg, PROP_MSG_MAX, 0);
|
||||
if (len <= 0) {
|
||||
KVERR("kvdb_client: recv failed, len=%zd, errno=%d\n", len, errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (msg[0]) {
|
||||
case 'D': {
|
||||
if (len < 2)
|
||||
len = kvdb_recv(fd, msg, len, 2);
|
||||
if (len < 0)
|
||||
goto out;
|
||||
|
||||
size_t key_len = (unsigned char)msg[1];
|
||||
size_t end_pos = key_len + 2;
|
||||
if (end_pos >= PROP_MSG_MAX)
|
||||
break;
|
||||
|
||||
const char* key = msg + 2;
|
||||
len = kvdb_recv(fd, msg, len, end_pos);
|
||||
if (len > 0) {
|
||||
int32_t err = kvdb_delete(server->kvdb, key, key_len);
|
||||
if (err >= 0) {
|
||||
dirty = true;
|
||||
kvdb_monitor_notify(server, key, NULL, 0);
|
||||
}
|
||||
send(fd, &err, 4, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'G': {
|
||||
if (len < 3)
|
||||
len = kvdb_recv(fd, msg, len, 3);
|
||||
if (len < 0)
|
||||
goto out;
|
||||
|
||||
size_t key_len = (unsigned char)msg[1];
|
||||
size_t val_len = (unsigned char)msg[2];
|
||||
size_t end_pos = key_len + 3;
|
||||
if (end_pos >= PROP_MSG_MAX)
|
||||
break;
|
||||
|
||||
const char* key = msg + 3;
|
||||
char value[PROP_VALUE_MAX];
|
||||
len = kvdb_recv(fd, msg, len, end_pos);
|
||||
if (len > 0) {
|
||||
len = kvdb_get(server->kvdb, key, key_len, value, val_len);
|
||||
if (len > 0)
|
||||
send(fd, value, len, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'S': {
|
||||
if (len < 3)
|
||||
len = kvdb_recv(fd, msg, len, 3);
|
||||
if (len < 0)
|
||||
goto out;
|
||||
|
||||
size_t key_len = (unsigned char)msg[1];
|
||||
size_t val_len = (unsigned char)msg[2];
|
||||
size_t end_pos = key_len + val_len + 3;
|
||||
if (end_pos >= PROP_MSG_MAX)
|
||||
break;
|
||||
|
||||
const char* key = msg + 3;
|
||||
const char* value = key + key_len;
|
||||
len = kvdb_recv(fd, msg, len, end_pos);
|
||||
if (len > 0) {
|
||||
int32_t err = kvdb_set(server->kvdb, key, key_len, value, val_len, false);
|
||||
if (err >= 0) {
|
||||
dirty = true;
|
||||
kvdb_monitor_notify(server, key, value, val_len);
|
||||
}
|
||||
send(fd, &err, 4, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#ifdef CONFIG_KVDB_DUMPLIST
|
||||
case 'L': {
|
||||
kvdb_list(server->kvdb, kvdb_list_consume, (void*)(uintptr_t)fd);
|
||||
send(fd, "\0", 2, 0); /* terminator */
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case 'C': {
|
||||
int ret = kvdb_commit(server->kvdb);
|
||||
send(fd, &ret, sizeof(ret), 0);
|
||||
break;
|
||||
}
|
||||
case 'R': {
|
||||
if (len < 2)
|
||||
len = kvdb_recv(fd, msg, len, 2);
|
||||
if (len < 0)
|
||||
goto out;
|
||||
|
||||
size_t path_len = (unsigned char)msg[1];
|
||||
size_t end_pos = path_len + 2;
|
||||
if (end_pos >= PROP_MSG_MAX)
|
||||
break;
|
||||
|
||||
const char* path = msg + 2;
|
||||
len = kvdb_recv(fd, msg, len, end_pos);
|
||||
if (len > 0) {
|
||||
if (path[0])
|
||||
kvdb_load(server->kvdb, path, true);
|
||||
else
|
||||
kvdb_load(server->kvdb, CONFIG_KVDB_SOURCE_PATH, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'M': {
|
||||
if (len < 2)
|
||||
len = kvdb_recv(fd, msg, len, 2);
|
||||
if (len < 0)
|
||||
goto out;
|
||||
|
||||
/* Property monitor open operation */
|
||||
size_t key_len = (unsigned char)msg[1];
|
||||
size_t end_pos = key_len + 2;
|
||||
if (end_pos >= PROP_MSG_MAX)
|
||||
break;
|
||||
|
||||
const char* key = msg + 2;
|
||||
len = kvdb_recv(fd, msg, len, end_pos);
|
||||
if (len < 0 || key[key_len - 1]) {
|
||||
break;
|
||||
}
|
||||
if (len > 0) {
|
||||
int32_t err = kvdb_monitor_open(server, fd, key, key_len);
|
||||
send(fd, &err, 4, 0);
|
||||
}
|
||||
/* Direct return, not close the monitor fd */
|
||||
free(msg);
|
||||
return false;
|
||||
}
|
||||
case 'E': {
|
||||
int ret = 0;
|
||||
KVERR("kvdb_client: received exit command, stopping server\n");
|
||||
server->running = false;
|
||||
kvdb_uninit(server->kvdb);
|
||||
send(fd, &ret, sizeof(ret), 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
free(msg);
|
||||
close(fd); /* done, close client socket */
|
||||
return dirty;
|
||||
}
|
||||
|
||||
static void kvdb_loop(kvdb_server* server)
|
||||
{
|
||||
struct epoll_event evs[KVFD_MAX];
|
||||
struct timespec ts;
|
||||
|
||||
server->efd = epoll_create(KVFD_MAX);
|
||||
if (server->efd < 0) {
|
||||
KVERR("kvdb_loop: epoll_create failed, errno=%d\n", errno);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < KVFD_COUNT; i++) {
|
||||
if (server->fd[i] >= 0) {
|
||||
evs[0].data.ptr = &server->fd[i];
|
||||
evs[0].events = EPOLLIN;
|
||||
if (epoll_ctl(server->efd, EPOLL_CTL_ADD, server->fd[i], &evs[0]) < 0) {
|
||||
KVERR("kvdb_loop: epoll_ctl ADD failed, fd=%d, errno=%d\n", server->fd[i], errno);
|
||||
close(server->efd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
time_t next = 0;
|
||||
server->running = true;
|
||||
while (server->running) {
|
||||
int timeout = -1;
|
||||
|
||||
/* commit the change after timeout */
|
||||
if (next) {
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
timeout = (int)(next - ts.tv_sec);
|
||||
if (timeout <= 0) {
|
||||
kvdb_commit(server->kvdb);
|
||||
timeout = -1;
|
||||
next = 0;
|
||||
} else
|
||||
timeout *= 1000;
|
||||
}
|
||||
|
||||
int nfds = epoll_wait(server->efd, evs, KVFD_MAX, timeout);
|
||||
for (int i = 0; i < nfds; i++) {
|
||||
int fd = *(int*)evs[i].data.ptr;
|
||||
#if (KVFD_COUNT > 1)
|
||||
if (fd != server->fd[0] && fd != server->fd[1]) {
|
||||
#else
|
||||
if (fd != server->fd[0]) {
|
||||
#endif
|
||||
if ((evs[i].events & EPOLLHUP) != 0) {
|
||||
kvdb_monitor_close(server, &evs[i]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((evs[i].events & EPOLLIN) == 0)
|
||||
continue;
|
||||
|
||||
int newfd = accept(fd, NULL, NULL);
|
||||
if (newfd < 0)
|
||||
continue;
|
||||
|
||||
/* is database changed? */
|
||||
if (kvdb_client(server, newfd) && next == 0) {
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
next = ts.tv_sec + CONFIG_KVDB_COMMIT_INTERVAL;
|
||||
if (next == 0)
|
||||
next++; /* ensure no zero */
|
||||
}
|
||||
}
|
||||
}
|
||||
KVERR("kvdb_loop: server loop exited, running=%d\n", server->running);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: main
|
||||
*
|
||||
* Description:
|
||||
* Main entry point. Listens for UNIX domain socket connection and perform
|
||||
* corresponding database operations.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
kvdb_server server = {
|
||||
.head = LIST_HEAD_INITIALIZER(),
|
||||
};
|
||||
int ret = kvdb_bind(server.fd);
|
||||
if (ret < 0) {
|
||||
KVERR("main: kvdb_bind failed, ret=%d, errno=%d\n", ret, errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = kvdb_init(&server.kvdb);
|
||||
if (ret < 0) {
|
||||
KVERR("main: kvdb_init failed, ret=%d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
kvdb_load(server.kvdb, CONFIG_KVDB_SOURCE_PATH, false);
|
||||
kvdb_loop(&server);
|
||||
out:
|
||||
KVERR("main: exiting, ret=%d\n", ret);
|
||||
kvdb_unbind(server.fd);
|
||||
return -ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2020 Xiaomi Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <kvdb.h>
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (argc == 3)
|
||||
ret = -property_set(argv[1], argv[2]);
|
||||
else if (argc == 2 && strncmp(argv[1], "-h", 3))
|
||||
ret = -property_delete(argv[1]);
|
||||
else
|
||||
printf("Usage: %s <key> [value]\n", argv[0]);
|
||||
|
||||
if (ret > 0)
|
||||
printf("Error: %s\n", strerror(ret));
|
||||
else {
|
||||
ret = -property_commit();
|
||||
if (ret > 0)
|
||||
printf("Error: commit %s\n", strerror(ret));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright (C) 2023 Xiaomi Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <kvdb.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/system_properties.h>
|
||||
|
||||
struct system_property_foreach_cookie {
|
||||
void* __cookie;
|
||||
void (*__callback)(const prop_info* __pi, void* __cookie);
|
||||
};
|
||||
|
||||
static uint32_t __system_property_serial_num = 0;
|
||||
|
||||
/*
|
||||
* Sets system property `name` to `value`, creating the system property if it doesn't already exist.
|
||||
*/
|
||||
int __system_property_set(const char* __name, const char* __value)
|
||||
{
|
||||
int ret = property_set(__name, __value);
|
||||
if (ret >= 0)
|
||||
__system_property_serial_num++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a `prop_info` corresponding system property `name`, or nullptr if it doesn't exist.
|
||||
* Use __system_property_read_callback to query the current value.
|
||||
*
|
||||
* Property lookup is expensive, so it can be useful to cache the result of this function.
|
||||
*/
|
||||
const prop_info* __system_property_find(const char* __name)
|
||||
{
|
||||
int ret = property_get(__name, NULL, NULL);
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
|
||||
return (const prop_info*)__name;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calls `callback` with a consistent trio of name, value, and serial number for property `pi`.
|
||||
*/
|
||||
void __system_property_read_callback(const prop_info* __pi,
|
||||
void (*__callback)(void* __cookie, const char* __name, const char* __value, uint32_t __serial),
|
||||
void* __cookie)
|
||||
{
|
||||
char value[PROP_VALUE_MAX];
|
||||
|
||||
if (__callback == NULL)
|
||||
return;
|
||||
|
||||
int ret = property_get((const char*)__pi, value, NULL);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
__callback(__cookie, (const char*)__pi, value, __system_property_serial_num);
|
||||
}
|
||||
|
||||
/*
|
||||
* used by __system_property_foreach() to pass to property_list.
|
||||
*/
|
||||
static void __system_property_foreach_callback(const char* key, const char* value, void* cookie)
|
||||
{
|
||||
struct system_property_foreach_cookie* pcookie = cookie;
|
||||
pcookie->__callback((const prop_info*)key, pcookie->__cookie);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterates over each system property and invokes the provided callback.
|
||||
* Use __system_property_read_callback() to read property values.
|
||||
*
|
||||
* This function is mainly for inspecting and debugging the property system.
|
||||
*/
|
||||
|
||||
int __system_property_foreach(void (*__callback)(const prop_info* __pi, void* __cookie), void* __cookie)
|
||||
{
|
||||
struct system_property_foreach_cookie cookie = {
|
||||
.__cookie = __cookie,
|
||||
.__callback = __callback};
|
||||
|
||||
return property_list(__system_property_foreach_callback, &cookie);
|
||||
}
|
||||
|
||||
/*
|
||||
* Waits for the system property `pi` to be updated past `old_serial`, with an optional timeout.
|
||||
* If `pi` is NULL, it waits for the global serial number.
|
||||
* If the serial is unknown, pass 0.
|
||||
*
|
||||
* Returns true if updated within the timeout, false if the call times out.
|
||||
*/
|
||||
|
||||
bool __system_property_wait(const prop_info* __pi, uint32_t __old_serial, uint32_t* __new_serial_ptr, const struct timespec* __relative_timeout)
|
||||
{
|
||||
int timems = __relative_timeout->tv_sec * 1000 + __relative_timeout->tv_nsec / 1000000;
|
||||
int ret = property_wait(__pi ? (const char*)__pi : "*", NULL, NULL, 0, timems);
|
||||
if (ret >= 0) {
|
||||
if (__new_serial_ptr)
|
||||
*__new_serial_ptr = __system_property_serial_num;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int __system_property_read(const prop_info* __pi, char* __name, char* __value)
|
||||
{
|
||||
strlcpy(__name, (const char*)__pi, PROP_NAME_MAX);
|
||||
return property_get(__name, __value, NULL);
|
||||
}
|
||||
|
||||
int __system_property_get(const char* __name, char* __value)
|
||||
{
|
||||
return property_get(__name, __value, NULL);
|
||||
}
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright (C) 2020 Xiaomi Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <kvdb.h>
|
||||
#include <unqlite.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
typedef struct kvdb_consume_data {
|
||||
kvdb_consume consume;
|
||||
void* cookie;
|
||||
unqlite_kv_cursor* cur;
|
||||
const char* key;
|
||||
size_t key_len;
|
||||
} kvdb_consume_data;
|
||||
|
||||
struct kvdb {
|
||||
unqlite* db;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Database Functions
|
||||
****************************************************************************/
|
||||
|
||||
int kvdb_persist_set(struct kvdb* kvdb, const char* key, size_t key_len, const void* value, size_t val_len, bool force)
|
||||
{
|
||||
int ret = unqlite_kv_store(kvdb->db, key, key_len, value, val_len);
|
||||
if (ret < 0) {
|
||||
KVERR("kv_store key:%s key_len:%d error %d!\n", key, (int)key_len, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t kvdb_persist_get(struct kvdb* kvdb, const char* key, size_t key_len, void* value, size_t val_len)
|
||||
{
|
||||
unqlite_int64 val_size = val_len;
|
||||
ssize_t ret = unqlite_kv_fetch(kvdb->db, key, key_len, value, &val_size);
|
||||
if (ret < 0) {
|
||||
if (ret != UNQLITE_NOTFOUND)
|
||||
KVERR("kv_fetch key:%s key_len:%d error %d!\n", key, (int)key_len, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (val_size <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
return val_size;
|
||||
}
|
||||
|
||||
int kvdb_persist_delete(struct kvdb* kvdb, const char* key, size_t key_len)
|
||||
{
|
||||
int ret = unqlite_kv_delete(kvdb->db, key, key_len);
|
||||
if (ret < 0) {
|
||||
KVERR("kv_delete key:%s key_len:%d error %d!\n", key, (int)key_len, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kvdb_list_value(const void* value, unsigned int len, void* arg)
|
||||
{
|
||||
kvdb_consume_data* data = arg;
|
||||
data->consume(data->key, value, len, data->cookie);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvdb_list_key(const void* value, unsigned int len, void* arg)
|
||||
{
|
||||
int ret;
|
||||
kvdb_consume_data* data = arg;
|
||||
data->key = value;
|
||||
data->key_len = len;
|
||||
ret = unqlite_kv_cursor_data_callback(data->cur, kvdb_list_value, data);
|
||||
if (ret < 0) {
|
||||
KVERR("kv_cursor_data_callback failed error %d!\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int kvdb_persist_list(struct kvdb* kvdb, kvdb_consume consume, void* cookie)
|
||||
{
|
||||
unqlite_kv_cursor* cur = NULL;
|
||||
unqlite_kv_cursor_init(kvdb->db, &cur);
|
||||
|
||||
kvdb_consume_data data = {
|
||||
.consume = consume,
|
||||
.cookie = cookie,
|
||||
.cur = cur,
|
||||
};
|
||||
|
||||
unqlite_kv_cursor_first_entry(cur);
|
||||
while (unqlite_kv_cursor_valid_entry(cur)) {
|
||||
int ret = unqlite_kv_cursor_key_callback(cur, kvdb_list_key, &data);
|
||||
if (ret < 0) { /* exit loop demanded by consume */
|
||||
KVERR("kv_cursor_key_callback failed error %d!\n", ret);
|
||||
unqlite_kv_cursor_release(kvdb->db, cur);
|
||||
return ret;
|
||||
}
|
||||
unqlite_kv_cursor_next_entry(cur);
|
||||
}
|
||||
unqlite_kv_cursor_release(kvdb->db, cur);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvdb_persist_commit(struct kvdb* kvdb)
|
||||
{
|
||||
int ret = unqlite_commit(kvdb->db);
|
||||
if (ret < 0) {
|
||||
KVERR("commit db:%p error %d!\n", kvdb->db, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void kvdb_persist_uninit(struct kvdb* kvdb)
|
||||
{
|
||||
if (kvdb != NULL) {
|
||||
unqlite_close(kvdb->db);
|
||||
free(kvdb);
|
||||
}
|
||||
}
|
||||
|
||||
int kvdb_persist_init(struct kvdb** kvdb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
*kvdb = zalloc(sizeof(struct kvdb));
|
||||
if (*kvdb == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* open database */
|
||||
|
||||
ret = unqlite_open(&(*kvdb)->db, CONFIG_KVDB_PERSIST_PATH, UNQLITE_OPEN_CREATE | UNQLITE_OPEN_OMIT_JOURNALING);
|
||||
|
||||
if (ret < 0) {
|
||||
KVERR("unqlite_open failed error %d!\n", ret);
|
||||
free(*kvdb);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,500 @@
|
|||
/*
|
||||
* Copyright (C) 2023 Xiaomi Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/streams.h>
|
||||
|
||||
#include <debug.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <android/log.h>
|
||||
#include <android/set_abort_message.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define LOG_BUF_SIZE 256
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static const int g_logprimap[ANDROID_LOG_SILENT + 1] = {
|
||||
LOG_DEBUG, /* ANDROID_LOG_UNKNOWN */
|
||||
LOG_DEBUG, /* ANDROID_LOG_DEFAULT */
|
||||
LOG_DEBUG, /* ANDROID_LOG_VERBOSE */
|
||||
LOG_DEBUG, /* ANDROID_LOG_DEBUG */
|
||||
LOG_INFO, /* ANDROID_LOG_INFO */
|
||||
LOG_WARNING, /* ANDROID_LOG_WARN */
|
||||
LOG_ERR, /* ANDROID_LOG_ERROR */
|
||||
LOG_CRIT, /* ANDROID_LOG_FATAL */
|
||||
LOG_DEBUG, /* ANDROID_LOG_SILENT */
|
||||
};
|
||||
|
||||
static __android_logger_function g_logger_function = __android_log_logd_logger;
|
||||
static __android_aborter_function g_aborter_function = __android_log_default_aborter;
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
* Call get the default tag, if not set the default tag before, return "nullptr".
|
||||
*/
|
||||
static const char* __android_log_get_default_tag(void)
|
||||
{
|
||||
char* envtag = getenv("ANDROID_LOG_DEFAULT_TAG");
|
||||
if (envtag == NULL) {
|
||||
return getprogname();
|
||||
}
|
||||
|
||||
return envtag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call __android_log_write_log_message() with a large buffer to support pass the message to user set
|
||||
* logger
|
||||
*/
|
||||
static void __android_log_write_log_message_buf(int bufID, int prio, const char* tag, const char* fmt, va_list ap)
|
||||
{
|
||||
char buf[LOG_BUF_SIZE];
|
||||
|
||||
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
|
||||
|
||||
struct __android_log_message log_message = {
|
||||
sizeof(struct __android_log_message), bufID, prio, tag, NULL, 0, buf
|
||||
};
|
||||
__android_log_write_log_message(&log_message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a formatted string to log buffer `id`,
|
||||
* with priority `prio` and tag `tag`.
|
||||
*
|
||||
* 1 is returned on success. On failure, a negated errno value is
|
||||
* returned.
|
||||
*/
|
||||
static int __android_log_buf_vprint(int bufID, int prio, const char* tag, const char* fmt, va_list ap)
|
||||
{
|
||||
if (tag == NULL) {
|
||||
tag = __android_log_get_default_tag();
|
||||
}
|
||||
|
||||
if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (g_logger_function == __android_log_logd_logger) {
|
||||
struct va_format vaf;
|
||||
|
||||
#ifdef va_copy
|
||||
va_list copy;
|
||||
va_copy(copy, ap);
|
||||
vaf.fmt = fmt;
|
||||
vaf.va = ©
|
||||
#else
|
||||
vaf.fmt = fmt;
|
||||
vaf.va = ≈
|
||||
#endif
|
||||
|
||||
syslog(g_logprimap[prio], "[%s] %pV\n", tag, &vaf);
|
||||
|
||||
#ifdef va_copy
|
||||
va_end(copy);
|
||||
#endif
|
||||
} else {
|
||||
__android_log_write_log_message_buf(bufID, prio, tag, fmt, ap);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
* Writes the constant string `text` to the log, with priority `prio` and tag
|
||||
* `tag`.
|
||||
*/
|
||||
int __android_log_write(int prio, const char* tag, const char* text)
|
||||
{
|
||||
return __android_log_buf_write(LOG_ID_MAIN, prio, tag, text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a formatted string to the log, with priority `prio` and tag `tag`.
|
||||
* The details of formatting are the same as for
|
||||
* [printf(3)](http://man7.org/linux/man-pages/man3/printf.3.html).
|
||||
*/
|
||||
int __android_log_print(int prio, const char* tag, const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int ret = __android_log_vprint(prio, tag, fmt, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to `__android_log_print`, but taking a `va_list`.
|
||||
* (If `__android_log_print` is like `printf`, this is like `vprintf`.)
|
||||
*/
|
||||
int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap)
|
||||
{
|
||||
return __android_log_buf_vprint(LOG_ID_MAIN, prio, tag, fmt, ap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an assertion failure to the log (as `ANDROID_LOG_FATAL`) and to
|
||||
* stderr, before calling
|
||||
* [abort(3)](http://man7.org/linux/man-pages/man3/abort.3.html).
|
||||
*
|
||||
* If `fmt` is non-null, `cond` is unused. If `fmt` is null, the string
|
||||
* `Assertion failed: %s` is used with `cond` as the string argument.
|
||||
* If both `fmt` and `cond` are null, a default string is provided.
|
||||
*
|
||||
* Most callers should use
|
||||
* [assert(3)](http://man7.org/linux/man-pages/man3/assert.3.html) from
|
||||
* `<assert.h>` instead, or the `__assert` and `__assert2` functions
|
||||
* provided by bionic if more control is needed. They support automatically
|
||||
* including the source filename and line number more conveniently than this
|
||||
* function.
|
||||
*/
|
||||
void __android_log_assert(const char* cond, const char* tag, const char* fmt, ...)
|
||||
{
|
||||
if (fmt) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
__android_log_vprint(ANDROID_LOG_FATAL, tag, fmt, ap);
|
||||
va_end(ap);
|
||||
} else {
|
||||
/* Msg not provided, log condition. N.B. Do not use cond directly as
|
||||
* format string as it could contain spurious '%' syntax (e.g.
|
||||
* "%d" in "blocks%devs == 0").
|
||||
*/
|
||||
if (cond) {
|
||||
__android_log_print(ANDROID_LOG_FATAL, tag, "Assertion failed: %s\n", cond);
|
||||
} else {
|
||||
__android_log_print(ANDROID_LOG_FATAL, tag, "Unspecified assertion failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
__android_log_call_aborter(tag);
|
||||
|
||||
/* noreturn function */
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the constant string `text` to the log buffer `id`,
|
||||
* with priority `prio` and tag `tag`.
|
||||
*
|
||||
* Apps should use __android_log_write() instead.
|
||||
*/
|
||||
int __android_log_buf_write(int bufID, int prio, const char* tag, const char* text)
|
||||
{
|
||||
if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
struct __android_log_message log_message = {
|
||||
sizeof(struct __android_log_message), bufID, prio, tag, NULL, 0, text
|
||||
};
|
||||
__android_log_write_log_message(&log_message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a formatted string to log buffer `id`,
|
||||
* with priority `prio` and tag `tag`.
|
||||
* The details of formatting are the same as for
|
||||
* [printf(3)](http://man7.org/linux/man-pages/man3/printf.3.html).
|
||||
*
|
||||
* Apps should use __android_log_print() instead.
|
||||
*/
|
||||
int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int ret = __android_log_buf_vprint(bufID, prio, tag, fmt, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the log message specified by log_message. log_message includes additional file name and
|
||||
* line number information that a logger may use. log_message is versioned for backwards
|
||||
* compatibility.
|
||||
* This assumes that loggability has already been checked through __android_log_is_loggable().
|
||||
* Higher level logging libraries, such as libbase, first check loggability, then format their
|
||||
* buffers, then pass the message to liblog via this function, and therefore we do not want to
|
||||
* duplicate the loggability check here.
|
||||
*
|
||||
* @param log_message the log message itself, see __android_log_message.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_write_log_message(struct __android_log_message* log_message)
|
||||
{
|
||||
if (log_message->tag == NULL) {
|
||||
log_message->tag = __android_log_get_default_tag();
|
||||
}
|
||||
|
||||
if (log_message->priority == ANDROID_LOG_FATAL) {
|
||||
android_set_abort_message(log_message->message);
|
||||
}
|
||||
|
||||
g_logger_function(log_message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a user defined logger function. All log messages sent to liblog will be set to the
|
||||
* function pointer specified by logger for processing. It is not expected that log messages are
|
||||
* already terminated with a new line. This function should add new lines if required for line
|
||||
* separation.
|
||||
*
|
||||
* @param logger the new function that will handle log messages.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_set_logger(__android_logger_function logger)
|
||||
{
|
||||
g_logger_function = logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the log message to logd. This is an __android_logger_function and can be provided to
|
||||
* __android_log_set_logger(). It is the default logger when running liblog on a device.
|
||||
*
|
||||
* @param log_message the log message to write, see __android_log_message.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_logd_logger(const struct __android_log_message* log_message)
|
||||
{
|
||||
int32_t priority = log_message->priority > ANDROID_LOG_SILENT ? ANDROID_LOG_FATAL : log_message->priority;
|
||||
|
||||
if (log_message->file != NULL) {
|
||||
syslog(g_logprimap[priority], "[%s %s:%" PRIu32 "] %s\n", log_message->tag,
|
||||
log_message->file, log_message->line, log_message->message);
|
||||
} else {
|
||||
syslog(g_logprimap[priority], "[%s] %s\n", log_message->tag,
|
||||
log_message->message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the log message to stderr. This is an __android_logger_function and can be provided to
|
||||
* __android_log_set_logger(). It is the default logger when running liblog on host.
|
||||
*
|
||||
* @param log_message the log message to write, see __android_log_message.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_stderr_logger(const struct __android_log_message* log_message)
|
||||
{
|
||||
if (!__android_log_is_loggable(log_message->priority, NULL, ANDROID_LOG_VERBOSE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct tm now;
|
||||
time_t t = time(NULL);
|
||||
localtime_r(&t, &now);
|
||||
|
||||
char timestamp[32];
|
||||
strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
|
||||
|
||||
static const char log_characters[ANDROID_LOG_SILENT + 1] = "XXVDIWEF";
|
||||
int32_t priority = log_message->priority > ANDROID_LOG_SILENT ? ANDROID_LOG_FATAL : log_message->priority;
|
||||
if (log_message->file != NULL) {
|
||||
fprintf(stderr, "%s %c %s %5d %5d %s:%" PRIu32 "] %s\n",
|
||||
log_message->tag, log_characters[priority], timestamp, getpid(), gettid(),
|
||||
log_message->file, log_message->line, log_message->message);
|
||||
} else {
|
||||
fprintf(stderr, "%s %c %s %5d %5d] %s\n",
|
||||
log_message->tag, log_characters[priority], timestamp, getpid(), gettid(),
|
||||
log_message->message);
|
||||
}
|
||||
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a user defined aborter function that is called for __android_log_assert() failures. This
|
||||
* user defined aborter function is highly recommended to abort and be noreturn, but is not strictly
|
||||
* required to.
|
||||
*
|
||||
* @param aborter the new aborter function, see __android_aborter_function.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_set_aborter(__android_aborter_function aborter)
|
||||
{
|
||||
g_aborter_function = aborter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the stored aborter function. This allows for other logging libraries to use the same
|
||||
* aborter function by calling this function in liblog.
|
||||
*
|
||||
* @param abort_message an additional message supplied when aborting, for example this is used to
|
||||
* call android_set_abort_message() in __android_log_default_aborter().
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_call_aborter(const char* abort_message)
|
||||
{
|
||||
g_aborter_function(abort_message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets android_set_abort_message() on device then aborts(). This is the default aborter.
|
||||
*
|
||||
* @param abort_message an additional message supplied when aborting. This functions calls
|
||||
* android_set_abort_message() with its contents.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_default_aborter(const char* abort_message)
|
||||
{
|
||||
android_set_abort_message(abort_message);
|
||||
abort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the per-tag properties "log.tag.<tagname>" along with the minimum priority from
|
||||
* __android_log_set_minimum_priority() to determine if a log message with a given prio and tag will
|
||||
* be printed. A non-zero result indicates yes, zero indicates false.
|
||||
*
|
||||
* If both a priority for a tag and a minimum priority are set by
|
||||
* __android_log_set_minimum_priority(), then the lowest of the two values are to determine the
|
||||
* minimum priority needed to log. If only one is set, then that value is used to determine the
|
||||
* minimum priority needed. If none are set, then default_priority is used.
|
||||
*
|
||||
* @param prio the priority to test, takes android_LogPriority values.
|
||||
* @param tag the tag to test.
|
||||
* @param default_prio the default priority to use if no properties or minimum priority are set.
|
||||
* @return an integer where 1 indicates that the message is loggable and 0 indicates that it is not.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
int __android_log_is_loggable(int prio, const char* tag, int default_prio)
|
||||
{
|
||||
int minimum_log_priority = __android_log_get_minimum_priority();
|
||||
|
||||
if (minimum_log_priority != ANDROID_LOG_DEFAULT) {
|
||||
return prio >= minimum_log_priority;
|
||||
} else {
|
||||
return prio >= default_prio;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the per-tag properties "log.tag.<tagname>" along with the minimum priority from
|
||||
* __android_log_set_minimum_priority() to determine if a log message with a given prio and tag will
|
||||
* be printed. A non-zero result indicates yes, zero indicates false.
|
||||
*
|
||||
* If both a priority for a tag and a minimum priority are set by
|
||||
* __android_log_set_minimum_priority(), then the lowest of the two values are to determine the
|
||||
* minimum priority needed to log. If only one is set, then that value is used to determine the
|
||||
* minimum priority needed. If none are set, then default_priority is used.
|
||||
*
|
||||
* @param prio the priority to test, takes android_LogPriority values.
|
||||
* @param tag the tag to test.
|
||||
* @param len the length of the tag.
|
||||
* @param default_prio the default priority to use if no properties or minimum priority are set.
|
||||
* @return an integer where 1 indicates that the message is loggable and 0 indicates that it is not.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio)
|
||||
{
|
||||
return __android_log_is_loggable(prio, tag, default_prio);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum priority that will be logged for this process.
|
||||
*
|
||||
* @param priority the new minimum priority to set, takes android_LogPriority values.
|
||||
* @return the previous set minimum priority as android_LogPriority values, or
|
||||
* ANDROID_LOG_DEFAULT if none was set.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
int32_t __android_log_set_minimum_priority(int32_t priority)
|
||||
{
|
||||
char buffer[sizeof(int32_t) * 8 + 1];
|
||||
int32_t old_minimum_log_priority = __android_log_get_minimum_priority();
|
||||
|
||||
priority = priority > ANDROID_LOG_SILENT ? ANDROID_LOG_FATAL : priority;
|
||||
setenv("ANDROID_LOG_MIN_PRIORITY", itoa(priority, buffer, 10), true);
|
||||
return old_minimum_log_priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the minimum priority that will be logged for this process. If none has been set by a
|
||||
* previous __android_log_set_minimum_priority() call, this returns ANDROID_LOG_DEFAULT.
|
||||
*
|
||||
* @return the current minimum priority as android_LogPriority values, or
|
||||
* ANDROID_LOG_DEFAULT if none is set.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
int32_t __android_log_get_minimum_priority(void)
|
||||
{
|
||||
char* envprio = getenv("ANDROID_LOG_MIN_PRIORITY");
|
||||
if (envprio == NULL) {
|
||||
return ANDROID_LOG_DEFAULT;
|
||||
}
|
||||
|
||||
return atoi(envprio);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default tag if no tag is provided when writing a log message. Defaults to
|
||||
* getprogname(). This truncates tag to the maximum log message size, though appropriate tags
|
||||
* should be much smaller.
|
||||
*
|
||||
* @param tag the new log tag.
|
||||
*
|
||||
* Available since API level 30.
|
||||
*/
|
||||
void __android_log_set_default_tag(const char* tag)
|
||||
{
|
||||
if (tag != NULL) {
|
||||
setenv("ANDROID_LOG_DEFAULT_TAG", tag, true);
|
||||
}
|
||||
}
|
||||
|
||||
int __android_log_error_write(int tag, const char* subTag, int32_t uid,
|
||||
const char* data, uint32_t dataLen)
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_ERROR, subTag,
|
||||
"tag: %d uid: %" PRId32 " data: %s\n",
|
||||
tag, uid, data);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
/*
|
||||
* Copyright (C) 2023 Xiaomi Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <syslog.h>
|
||||
|
||||
#include <android/set_abort_message.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
void android_set_abort_message(const char* __msg)
|
||||
{
|
||||
syslog(LOG_ALERT, "Abort Message: %s", __msg);
|
||||
}
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* Copyright (C) 2020 Xiaomi Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <cutils/trace.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Private Types
|
||||
****************************************************************************/
|
||||
|
||||
struct atrace_category {
|
||||
uint64_t tag;
|
||||
const char* name;
|
||||
const char* help;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static const struct atrace_category category_list[] = {
|
||||
{ ATRACE_TAG_NEVER, "never", "never output trace" },
|
||||
{ ATRACE_TAG_ALWAYS, "always", "always output trace" },
|
||||
{ ATRACE_TAG_GRAPHICS, "gfx", "graphics" },
|
||||
{ ATRACE_TAG_INPUT, "input", "input" },
|
||||
{ ATRACE_TAG_VIEW, "view", "view" },
|
||||
{ ATRACE_TAG_WEBVIEW, "webview", "webview" },
|
||||
{ ATRACE_TAG_WINDOW_MANAGER, "wm", "window_manager" },
|
||||
{ ATRACE_TAG_ACTIVITY_MANAGER, "am", "activity_manager" },
|
||||
{ ATRACE_TAG_SYNC_MANAGER, "sm", "sync_manager" },
|
||||
{ ATRACE_TAG_AUDIO, "audio", "audio" },
|
||||
{ ATRACE_TAG_VIDEO, "video", "video" },
|
||||
{ ATRACE_TAG_CAMERA, "camera", "camera" },
|
||||
{ ATRACE_TAG_HAL, "hal", "hal" },
|
||||
{ ATRACE_TAG_APP, "app", "app" },
|
||||
{ ATRACE_TAG_RESOURCES, "res", "resources" },
|
||||
{ ATRACE_TAG_DALVIK, "dalvik", "dalvik" },
|
||||
{ ATRACE_TAG_RS, "rs", "rs" },
|
||||
{ ATRACE_TAG_BIONIC, "bionic", "bionic" },
|
||||
{ ATRACE_TAG_POWER, "power", "power" },
|
||||
{ ATRACE_TAG_PACKAGE_MANAGER, "pm", "package_manager" },
|
||||
{ ATRACE_TAG_SYSTEM_SERVER, "ss", "system_server" },
|
||||
{ ATRACE_TAG_DATABASE, "db", "database" },
|
||||
{ ATRACE_TAG_NETWORK, "net", "network" },
|
||||
{ ATRACE_TAG_ADB, "adb", "adb" },
|
||||
{ ATRACE_TAG_VIBRATOR, "vibrator", "vibrator" },
|
||||
{ ATRACE_TAG_AIDL, "aidl", "aidl" },
|
||||
{ ATRACE_TAG_NNAPI, "nnapi", "nnapi" },
|
||||
{ ATRACE_TAG_RRO, "rro", "rro" },
|
||||
{ ATRACE_TAG_THERMAL, "thermal", "thermal" },
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* atrace_help
|
||||
****************************************************************************/
|
||||
|
||||
static int atrace_help(const char* progname)
|
||||
{
|
||||
printf("Usage: %s [option] [categories...]\n"
|
||||
"options include:\n"
|
||||
" --list_categories\n"
|
||||
" list the available tracing categories\n",
|
||||
progname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* atrace_list
|
||||
*
|
||||
* Description:
|
||||
* List all available categories
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int atrace_list(void)
|
||||
{
|
||||
int i;
|
||||
printf("Available categories:\n");
|
||||
for (i = 0; i < sizeof(category_list) / sizeof(category_list[0]); i++) {
|
||||
printf("\t%s: %s\n", category_list[i].name, category_list[i].help);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* atrace_category
|
||||
*
|
||||
* Description:
|
||||
* Update tracing tag
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int atrace_category(int argc, char** argv)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
uint64_t tag = 0;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "always") == 0) {
|
||||
tag = UINT64_MAX;
|
||||
} else if (strcmp(argv[i], "never") == 0) {
|
||||
tag = 0ull;
|
||||
} else {
|
||||
for (j = 2; j < sizeof(category_list) / sizeof(category_list[0]); j++) {
|
||||
if (strcmp(argv[i], category_list[j].name) == 0) {
|
||||
tag |= category_list[j].tag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (j == sizeof(category_list) / sizeof(category_list[0])) {
|
||||
printf("Unknown category: %s", argv[i]);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
atrace_enabled_tags = tag;
|
||||
printf("Android tracing enabled with tag: %#" PRIx64 "\n", tag);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: main
|
||||
*
|
||||
* Description:
|
||||
* This is the main entry point for the atrace command.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int ret;
|
||||
if (argc < 2) {
|
||||
atrace_help(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "--list_categories") == 0) {
|
||||
ret = atrace_list();
|
||||
} else {
|
||||
ret = atrace_category(argc, argv);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright (C) 2020 Xiaomi Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <cutils/trace.h>
|
||||
#include <nuttx/sched_note.h>
|
||||
|
||||
int atrace_marker_fd = -1;
|
||||
uint64_t atrace_enabled_tags = ~0ull;
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
uint64_t atrace_get_enabled_tags(void)
|
||||
{
|
||||
return atrace_enabled_tags;
|
||||
}
|
||||
|
||||
void atrace_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
void atrace_setup(void)
|
||||
{
|
||||
}
|
||||
|
||||
void atrace_update_tags(void)
|
||||
{
|
||||
}
|
||||
|
||||
void atrace_set_debuggable(bool debuggable)
|
||||
{
|
||||
}
|
||||
|
||||
void atrace_set_tracing_enabled(bool enabled)
|
||||
{
|
||||
}
|
||||
|
||||
void atrace_begin_body(const char* name)
|
||||
{
|
||||
sched_note_printf(NOTE_TAG_ALWAYS, LOG_INFO, "B|%d|%s", gettid(), name);
|
||||
}
|
||||
|
||||
void atrace_end_body(void)
|
||||
{
|
||||
sched_note_printf(NOTE_TAG_ALWAYS, LOG_INFO, "E|%d", gettid());
|
||||
}
|
||||
|
||||
void atrace_async_begin_body(const char* name, int32_t cookie)
|
||||
{
|
||||
sched_note_printf(NOTE_TAG_ALWAYS, LOG_INFO, "S|%d|%s|%" PRId32, gettid(),
|
||||
name, cookie);
|
||||
}
|
||||
|
||||
void atrace_async_end_body(const char* name, int32_t cookie)
|
||||
{
|
||||
sched_note_printf(NOTE_TAG_ALWAYS, LOG_INFO, "F|%d|%s|%" PRId32, gettid(),
|
||||
name, cookie);
|
||||
}
|
||||
|
||||
void atrace_async_for_track_begin_body(const char* track_name,
|
||||
const char* name, int32_t cookie)
|
||||
{
|
||||
sched_note_printf(NOTE_TAG_ALWAYS, LOG_INFO, "G|%d|%s|%s|%" PRId32, gettid(),
|
||||
track_name, name, cookie);
|
||||
}
|
||||
|
||||
void atrace_async_for_track_end_body(const char* track_name, const char* name, int32_t cookie)
|
||||
{
|
||||
sched_note_printf(NOTE_TAG_ALWAYS, LOG_INFO, "H|%d|%s|%s|%" PRId32, gettid(),
|
||||
track_name, name, cookie);
|
||||
}
|
||||
|
||||
void atrace_instant_body(const char* name)
|
||||
{
|
||||
sched_note_printf(NOTE_TAG_ALWAYS, LOG_INFO, "I|%d|%s", gettid(), name);
|
||||
}
|
||||
|
||||
void atrace_instant_for_track_body(const char* track_name, const char* name)
|
||||
{
|
||||
sched_note_printf(NOTE_TAG_ALWAYS, LOG_INFO, "N|%d|%s|%s", gettid(),
|
||||
track_name, name);
|
||||
}
|
||||
|
||||
void atrace_int_body(const char* name, int32_t value)
|
||||
{
|
||||
sched_note_printf(NOTE_TAG_ALWAYS, LOG_INFO, "C|%d|%s|%" PRId32,
|
||||
gettid(), name, value);
|
||||
}
|
||||
|
||||
void atrace_int64_body(const char* name, int64_t value)
|
||||
{
|
||||
sched_note_printf(NOTE_TAG_ALWAYS, LOG_INFO, "C|%d|%s|%" PRId64, gettid(),
|
||||
name, value);
|
||||
}
|
||||
Loading…
Reference in New Issue