setregid系统调用及示例

setregid 函数详解

  1. 函数介绍

setregid系统调用详解及示例,掌握Linux进程权限管理,了解函数功能与使用方法,提升系统编程技能。setregid 是Linux系统调用,用于同时设置进程的真实组ID(real group ID)和有效组ID(effective group ID)。它是组ID管理的重要函数,允许进程在不同组权限之间切换,实现灵活的权限控制。

  1. 函数原型
1
2
3
#include <unistd.h>
int setregid(gid_t rgid, gid_t egid);

  1. 功能

setregid 同时设置进程的真实组ID和有效组ID。真实组ID标识进程的实际所有者,有效组ID决定进程当前的权限。这个函数主要用于权限切换和安全控制。

  1. 参数
  • gid_t rgid: 新的真实组ID(-1表示不改变)

  • gid_t egid: 新的有效组ID(-1表示不改变)

  1. 返回值
  • 成功: 返回0

  • 失败: 返回-1,并设置errno

  1. 相似函数,或关联函数
  • setgid: 仅设置有效组ID

  • setegid: 仅设置有效组ID

  • setrgid: 仅设置真实组ID

  • getgid: 获取真实组ID

  • getegid: 获取有效组ID

  1. 示例代码

示例1:基础setregid使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <grp.h>

/**
* 显示当前组ID信息
*/
void show_group_ids() {
gid_t real_gid = getgid();
gid_t effective_gid = getegid();

printf("=== 当前组ID信息 ===\n");
printf("真实组ID: %d", real_gid);
struct group *real_grp = getgrgid(real_gid);
if (real_grp) {
printf(" (%s)", real_grp->gr_name);
}
printf("\n");

printf("有效组ID: %d", effective_gid);
struct group *effective_grp = getgrgid(effective_gid);
if (effective_grp) {
printf(" (%s)", effective_grp->gr_name);
}
printf("\n\n");
}

/**
* 演示基础setregid使用方法
*/
int demo_setregid_basic() {
gid_t original_real_gid, original_effective_gid;
gid_t test_gid = 1000; // 测试组ID
int result;

printf("=== 基础setregid使用示例 ===\n");

// 显示原始组ID信息
printf("1. 原始组ID信息:\n");
show_group_ids();

original_real_gid = getgid();
original_effective_gid = getegid();

// 尝试设置组ID(需要适当权限)
printf("2. 尝试设置组ID:\n");
printf(" 尝试将真实组ID设置为: %d\n", test_gid);
printf(" 尝试将有效组ID设置为: %d\n", test_gid);

result = setregid(test_gid, test_gid);
if (result == 0) {
printf(" ✓ 成功设置组ID\n");
show_group_ids();

// 验证设置结果
if (getgid() == test_gid && getegid() == test_gid) {
printf(" ✓ 组ID设置正确\n");
} else {
printf(" ✗ 组ID设置可能有问题\n");
}
} else {
printf(" ✗ 设置组ID失败: %s\n", strerror(errno));
if (errno == EPERM) {
printf(" 原因:权限不足,需要root权限或适当的组权限\n");
} else if (errno == EINVAL) {
printf(" 原因:组ID无效\n");
}

printf(" 注意:普通用户通常无法随意更改组ID\n");
}

// 尝试部分设置(使用-1)
printf("\n3. 尝试部分设置组ID:\n");
printf(" 保持真实组ID不变,仅设置有效组ID\n");

result = setregid(-1, original_effective_gid);
if (result == 0) {
printf(" ✓ 部分设置成功\n");
show_group_ids();
} else {
printf(" ✗ 部分设置失败: %s\n", strerror(errno));
}

// 恢复原始组ID
printf("\n4. 恢复原始组ID:\n");
result = setregid(original_real_gid, original_effective_gid);
if (result == 0) {
printf(" ✓ 成功恢复原始组ID\n");
show_group_ids();
} else {
printf(" ✗ 恢复原始组ID失败: %s\n", strerror(errno));
}

return 0;
}

int main() {
return demo_setregid_basic();
}

示例2:权限检查和组管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>

/**
* 检查当前权限
*/
void check_permissions() {
uid_t uid = getuid();
uid_t euid = geteuid();
gid_t gid = getgid();
gid_t egid = getegid();

printf("=== 权限检查 ===\n");
printf("真实用户ID: %d", uid);
struct passwd *pwd = getpwuid(uid);
if (pwd) {
printf(" (%s)", pwd->pw_name);
}
printf("\n");

printf("有效用户ID: %d", euid);
pwd = getpwuid(euid);
if (pwd) {
printf(" (%s)", pwd->pw_name);
}
printf("\n");

printf("真实组ID: %d", gid);
struct group *grp = getgrgid(gid);
if (grp) {
printf(" (%s)", grp->gr_name);
}
printf("\n");

printf("有效组ID: %d", egid);
grp = getgrgid(egid);
if (grp) {
printf(" (%s)", grp->gr_name);
}
printf("\n");

if (uid == 0 || euid == 0) {
printf("✓ 当前具有root权限\n");
} else {
printf("✗ 当前没有root权限\n");
printf(" 提示:修改组ID通常需要root权限\n");
}
printf("\n");
}

/**
* 查找系统中的组
*/
void find_system_groups() {
struct group *grp;
int group_count = 0;

printf("=== 系统中的部分组信息 ===\n");

// 查找一些常见的组
const char *common_groups&#91;] = {"root", "daemon", "sys", "adm", "tty", "disk", NULL};

for (int i = 0; common_groups&#91;i]; i++) {
grp = getgrnam(common_groups&#91;i]);
if (grp) {
printf(" 组名: %-10s ID: %d\n", grp->gr_name, grp->gr_gid);
group_count++;
}
}

printf(" 找到 %d 个常用组\n\n", group_count);
}

/**
* 演示权限检查和组管理
*/
int demo_permission_check() {
gid_t test_groups&#91;3];
int num_groups = 0;

printf("=== 权限检查和组管理演示 ===\n");

// 检查当前权限
check_permissions();

// 查找系统组
find_system_groups();

// 准备测试组列表
struct group *grp;

// 尝试获取一些系统组ID
grp = getgrnam("daemon");
if (grp) {
test_groups&#91;num_groups++] = grp->gr_gid;
printf("添加组 daemon (ID: %d) 到测试列表\n", grp->gr_gid);
}

grp = getgrnam("sys");
if (grp) {
test_groups&#91;num_groups++] = grp->gr_gid;
printf("添加组 sys (ID: %d) 到测试列表\n", grp->gr_gid);
}

grp = getgrnam("adm");
if (grp) {
test_groups&#91;num_groups++] = grp->gr_gid;
printf("添加组 adm (ID: %d) 到测试列表\n", grp->gr_gid);
}

if (num_groups == 0) {
// 如果找不到系统组,使用示例ID
test_groups&#91;0] = 100;
test_groups&#91;1] = 200;
test_groups&#91;2] = 300;
num_groups = 3;
printf("使用示例组ID: 100, 200, 300\n");
}

// 显示当前组ID
printf("\n当前组ID信息:\n");
show_group_ids();

// 尝试设置组ID
printf("\n尝试设置组ID:\n");
printf(" 目标真实组ID: %d\n", test_groups&#91;0]);
printf(" 目标有效组ID: %d\n", test_groups&#91;1]);

int result = setregid(test_groups&#91;0], test_groups&#91;1]);
if (result == 0) {
printf("✓ 组ID设置成功\n");
show_group_ids();
} else {
printf("✗ 组ID设置失败: %s\n", strerror(errno));
if (errno == EPERM) {
printf(" 需要root权限\n");
}
}

// 演示部分设置
printf("\n演示部分设置 (仅改变有效组ID):\n");
printf(" 保持真实组ID: %d\n", getgid());
printf(" 新的有效组ID: %d\n", test_groups&#91;2]);

result = setregid(-1, test_groups&#91;2]);
if (result == 0) {
printf("✓ 部分设置成功\n");
show_group_ids();
} else {
printf("✗ 部分设置失败: %s\n", strerror(errno));
}

return 0;
}

// 辅助函数声明
void show_group_ids();

int main() {
return demo_permission_check();
}

示例3:组ID切换演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <sys/stat.h>
#include <fcntl.h>

/**
* 安全组切换演示
*/
int demo_secure_group_switching() {
gid_t original_real_gid, original_effective_gid;
gid_t test_gid = 1000; // 测试组ID

printf("=== 安全组切换演示 ===\n");

// 保存原始组ID
original_real_gid = getgid();
original_effective_gid = getegid();

printf("原始组ID信息:\n");
printf(" 真实组ID: %d\n", original_real_gid);
printf(" 有效组ID: %d\n", original_effective_gid);

// 演示组切换的安全性考虑
printf("\n1. 组切换安全考虑:\n");
printf(" ✓ 始终保存原始组ID状态\n");
printf(" ✓ 在使用特殊权限后及时恢复\n");
printf(" ✓ 检查组切换操作的返回值\n");
printf(" ✓ 记录组切换日志\n");
printf(" ✓ 最小权限原则:只请求必需的权限\n");

// 演示临时组权限使用
printf("\n2. 模拟使用临时组权限:\n");

// 尝试切换到测试组
int result = setregid(test_gid, test_gid);
if (result == 0) {
printf(" ✓ 成功切换到组 %d\n", test_gid);
show_group_ids();

// 模拟使用组权限的操作
printf(" 使用新组权限进行操作...\n");
// 这里可以进行需要特定组权限的操作

// 恢复原始组ID
result = setregid(original_real_gid, original_effective_gid);
if (result == 0) {
printf(" ✓ 成功恢复原始组ID\n");
} else {
printf(" ✗ 恢复原始组ID失败: %s\n", strerror(errno));
}
} else {
printf(" ✗ 切换到组 %d 失败: %s\n", test_gid, strerror(errno));
if (errno == EPERM) {
printf(" 需要root权限或适当的组权限\n");
}
}

show_group_ids();

// 演示权限提升检测
printf("\n3. 权限提升检测:\n");
gid_t current_real_gid = getgid();
gid_t current_effective_gid = getegid();

if (current_real_gid != original_real_gid ||
current_effective_gid != original_effective_gid) {
printf(" 警告:组ID状态未正确恢复\n");
} else {
printf(" ✓ 组ID状态已正确恢复\n");
}

// 显示组安全相关信息
printf("\n4. 组安全相关信息:\n");
printf(" 组ID切换需要CAP_SETGID能力\n");
printf(" 真实组ID只能向有效组ID降低\n");
printf(" 避免在生产环境中频繁切换组权限\n");

return 0;
}

// 辅助函数声明
void show_group_ids();

int main() {
return demo_secure_group_switching();
}

示例4:进程组权限管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <sys/wait.h>

/**
* 子进程函数
*/
void child_process(int child_id) {
printf("子进程 %d 启动 (PID: %d)\n", child_id, getpid());

// 显示子进程初始组ID
printf(" 子进程 %d 初始组ID:\n", child_id);
printf(" 真实组ID: %d\n", getgid());
printf(" 有效组ID: %d\n", getegid());

// 尝试修改组ID
printf(" 子进程 %d 尝试修改组ID:\n", child_id);

gid_t new_gid = 1000; // 测试组ID
int result = setregid(new_gid, new_gid);
if (result == 0) {
printf(" ✓ 子进程 %d 成功修改组ID\n", child_id);
printf(" 新真实组ID: %d\n", getgid());
printf(" 新有效组ID: %d\n", getegid());
} else {
printf(" ✗ 子进程 %d 修改组ID失败: %s\n", child_id, strerror(errno));
}

// 模拟一些工作
sleep(3);
printf("子进程 %d 结束\n", child_id);
exit(0);
}

/**
* 演示进程组权限管理
*/
int demo_process_group_permissions() {
pid_t child1_pid, child2_pid;
gid_t original_real_gid, original_effective_gid;

printf("=== 进程组权限管理演示 ===\n");

// 保存父进程原始组ID
original_real_gid = getgid();
original_effective_gid = getegid();

printf("父进程原始组ID信息:\n");
printf(" 真实组ID: %d\n", original_real_gid);
printf(" 有效组ID: %d\n", original_effective_gid);

// 创建第一个子进程
printf("\n1. 创建第一个子进程:\n");
child1_pid = fork();
if (child1_pid == 0) {
child_process(1);
} else if (child1_pid > 0) {
printf(" 父进程创建子进程1: PID=%d\n", child1_pid);
} else {
perror("创建子进程1失败");
return -1;
}

// 创建第二个子进程
printf("\n2. 创建第二个子进程:\n");
child2_pid = fork();
if (child2_pid == 0) {
child_process(2);
} else if (child2_pid > 0) {
printf(" 父进程创建子进程2: PID=%d\n", child2_pid);
} else {
perror("创建子进程2失败");
// 清理已创建的子进程
kill(child1_pid, SIGKILL);
return -1;
}

// 父进程也尝试修改组ID
printf("\n3. 父进程尝试修改组ID:\n");
gid_t parent_new_gid = 2000; // 测试组ID
int result = setregid(parent_new_gid, parent_new_gid);
if (result == 0) {
printf(" ✓ 父进程成功修改组ID\n");
printf(" 新真实组ID: %d\n", getgid());
printf(" 新有效组ID: %d\n", getegid());
} else {
printf(" ✗ 父进程修改组ID失败: %s\n", strerror(errno));
}

// 等待子进程结束
printf("\n4. 等待子进程结束:\n");
int status;
pid_t finished_pid;

while ((finished_pid = wait(&status)) > 0) {
printf(" 子进程 %d 已结束,退出状态: %d\n", finished_pid, WEXITSTATUS(status));
}

// 恢复父进程原始组ID
printf("\n5. 恢复父进程原始组ID:\n");
result = setregid(original_real_gid, original_effective_gid);
if (result == 0) {
printf(" ✓ 父进程成功恢复原始组ID\n");
printf(" 最终真实组ID: %d\n", getgid());
printf(" 最终有效组ID: %d\n", getegid());
} else {
printf(" ✗ 父进程恢复原始组ID失败: %s\n", strerror(errno));
}

return 0;
}

int main() {
return demo_process_group_permissions();
}

示例5:组权限安全测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>

/**
* 组权限安全测试
*/
int demo_group_security_testing() {
gid_t original_real_gid, original_effective_gid;
gid_t test_gid = 1000; // 测试组ID

printf("=== 组权限安全测试 ===\n");

// 保存原始状态
original_real_gid = getgid();
original_effective_gid = getegid();

printf("测试开始 - 原始组ID状态:\n");
printf(" 真实组ID: %d\n", original_real_gid);
printf(" 有效组ID: %d\n", original_effective_gid);

// 测试1: 正常组切换
printf("\n=== 测试1: 正常组切换 ===\n");
int result = setregid(test_gid, test_gid);
if (result == 0) {
printf("✓ 正常组切换成功\n");
printf(" 当前真实组ID: %d\n", getgid());
printf(" 当前有效组ID: %d\n", getegid());

// 恢复
result = setregid(original_real_gid, original_effective_gid);
if (result == 0) {
printf("✓ 成功恢复原始状态\n");
} else {
printf("✗ 恢复原始状态失败: %s\n", strerror(errno));
}
} else {
printf("✗ 正常组切换失败: %s\n", strerror(errno));
if (errno == EPERM) {
printf(" 预期结果:没有足够权限\n");
}
}

// 测试2: 部分组切换
printf("\n=== 测试2: 部分组切换 ===\n");
result = setregid(-1, test_gid);
if (result == 0) {
printf("✓ 部分组切换成功 (仅改变有效组ID)\n");
printf(" 当前真实组ID: %d\n", getgid());
printf(" 当前有效组ID: %d\n", getegid());

// 恢复有效组ID
result = setregid(-1, original_effective_gid);
if (result == 0) {
printf("✓ 成功恢复有效组ID\n");
} else {
printf("✗ 恢复有效组ID失败: %s\n", strerror(errno));
}
} else {
printf("✗ 部分组切换失败: %s\n", strerror(errno));
}

// 测试3: 无效组ID
printf("\n=== 测试3: 无效组ID ===\n");
gid_t invalid_gid = 999999; // 很可能不存在的组ID
result = setregid(invalid_gid, invalid_gid);
if (result == 0) {
printf("✗ 无效组ID设置成功 (这不应该发生)\n");
} else {
printf("✓ 无效组ID设置失败: %s\n", strerror(errno));
if (errno == EINVAL) {
printf(" 正确:组ID无效\n");
}
}

// 测试4: 权限验证
printf("\n=== 测试4: 权限验证 ===\n");
uid_t uid = getuid();
uid_t euid = geteuid();

printf("当前用户权限状态:\n");
printf(" 真实用户ID: %d", uid);
struct passwd *pwd = getpwuid(uid);
if (pwd) {
printf(" (%s)", pwd->pw_name);
}
printf("\n");

printf(" 有效用户ID: %d", euid);
pwd = getpwuid(euid);
if (pwd) {
printf(" (%s)", pwd->pw_name);
}
printf("\n");

if (uid == 0 || euid == 0) {
printf("✓ 具有root权限,可以进行组切换测试\n");
} else {
printf("✗ 没有root权限,组切换可能受限\n");
}

// 测试5: 状态一致性检查
printf("\n=== 测试5: 状态一致性检查 ===\n");
gid_t final_real_gid = getgid();
gid_t final_effective_gid = getegid();

printf("最终组ID状态:\n");
printf(" 真实组ID: %d\n", final_real_gid);
printf(" 有效组ID: %d\n", final_effective_gid);

if (final_real_gid == original_real_gid &&
final_effective_gid == original_effective_gid) {
printf("✓ 组ID状态一致性检查通过\n");
} else {
printf("✗ 组ID状态不一致,可能存在安全风险\n");
}

// 显示安全建议
printf("\n=== 安全建议 ===\n");
printf("1. 始终验证setregid的返回值\n");
printf("2. 保存原始组ID状态以便恢复\n");
printf("3. 使用最小权限原则\n");
printf("4. 记录组权限变更日志\n");
printf("5. 避免频繁的组权限切换\n");
printf("6. 在特权操作后及时降权\n");

return 0;
}

int main() {
return demo_group_security_testing();
}

setregid 使用注意事项

系统要求:

内核版本: 支持组ID管理的Linux内核

data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">

权限要求: 需要CAP_SETGID能力或root权限

架构支持: 支持所有主流架构

参数规则:

-1表示不改变: rgid或egid为-1时保持原值

权限限制: 普通用户只能在允许范围内切换

真实组ID限制: 真实组ID只能向有效组ID降低

错误处理:

EPERM: 权限不足(需要CAP_SETGID或root权限)

EINVAL: 组ID无效

EAGAIN: 资源暂时不可用

EFAULT: 指针参数无效

安全考虑:

权限提升: 可能导致权限提升风险

审计日志: 建议记录权限变更操作

最小权限: 遵循最小权限原则

状态恢复: 及时恢复原始权限状态

最佳实践:

权限检查: 执行前检查是否具有足够权限

参数验证: 验证参数的有效性和安全性

错误处理: 妥善处理各种错误情况

状态保存: 保存原始状态以便恢复

日志记录: 记录权限变更操作

及时恢复: 使用完特殊权限后及时恢复

组ID类型说明

真实组ID (Real Group ID):

  • 含义: 进程的实际所有者组ID

  • 用途: 标识进程的真正归属

  • 修改限制: 只能向有效组ID降低

有效组ID (Effective Group ID):

  • 含义: 当前用于权限检查的组ID

  • 用途: 决定进程当前的权限

  • 修改限制: 可以设置为真实组ID或保存的组ID

保存的组ID (Saved Group ID):

  • 含义: 进程启动时的有效组ID副本

  • 用途: 用于权限恢复

  • 修改时机: 通常在exec时设置

常见使用场景

1. 服务权限管理:

1
2
3
// 服务启动时降低权限
setregid(service_gid, service_gid);

2. 安全沙箱:

1
2
3
// 创建受限环境时设置适当的组权限
setregid(restricted_gid, restricted_gid);

3. 权限切换:

1
2
3
4
5
// 临时获取特定组权限
setregid(-1, target_gid); // 仅改变有效组ID
// 执行需要权限的操作
setregid(-1, original_gid); // 恢复有效组ID

权限检查规则

setregid权限要求:

超级用户: 可以设置任意组ID

普通用户: 只能设置为以下值之一:

  • 当前真实组ID

  • 当前有效组ID

  • 保存的组ID

安全限制:

真实组ID: 只能向有效组ID降低

有效组ID: 可以自由设置(在允许范围内)

审计: 系统通常会记录权限变更

总结

setregid 是Linux系统中重要的组权限管理函数,提供了:

组权限控制: 精确控制进程的组权限

灵活配置: 支持同时设置真实和有效组ID

安全机制: 通过权限检查保证系统安全

标准兼容: 符合POSIX标准

通过合理使用 setregid,可以实现细粒度的权限控制,构建更加安全可靠的系统应用。在实际应用中,需要注意权限要求、错误处理和安全最佳实践。

https://www.calcguide.tech/2025/08/11/setregid系统调用及示例/

https://www.calcguide.tech/2025/08/26/linux开源软件路线图/

data-ad-format="auto" data-full-width-responsive="true">